mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-04 12:24:11 +01:00
09981b39c2
git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@647 ebee16cc-31ac-478f-84a7-5cbb03baadba
668 lines
18 KiB
C
668 lines
18 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* @file pios_i2c.c
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
|
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
|
|
* @brief I2C Enable/Disable routines
|
|
* @see The GNU Public License (GPL) Version 3
|
|
* @defgroup PIOS_I2C I2C Functions
|
|
* @{
|
|
*
|
|
*****************************************************************************/
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
/* Project Includes */
|
|
#include "pios.h"
|
|
|
|
|
|
|
|
#if defined(PIOS_INCLUDE_I2C)
|
|
|
|
|
|
|
|
/* Options */
|
|
//#define USE_DEBUG_PINS
|
|
|
|
#ifdef PIOS_INCLUDE_FREERTOS
|
|
#define USE_FREERTOS
|
|
#endif
|
|
|
|
|
|
/* Global Variables */
|
|
volatile uint32_t PIOS_I2C_UnexpectedEvent;
|
|
|
|
/* Local types */
|
|
typedef union {
|
|
struct {
|
|
unsigned ALL:8;
|
|
};
|
|
struct {
|
|
unsigned BUSY:1;
|
|
unsigned STOP_REQUESTED:1;
|
|
unsigned ABORT_IF_FIRST_BYTE_0:1;
|
|
unsigned WRITE_WITHOUT_STOP:1;
|
|
unsigned INIRQ:1;
|
|
};
|
|
} TransferStateTypeDef;
|
|
|
|
|
|
typedef struct {
|
|
I2C_TypeDef *base;
|
|
|
|
uint8_t i2c_address;
|
|
uint8_t *tx_buffer_ptr;
|
|
uint8_t *rx_buffer_ptr;
|
|
volatile uint16_t buffer_len;
|
|
volatile uint16_t buffer_ix;
|
|
|
|
volatile TransferStateTypeDef transfer_state;
|
|
volatile int32_t transfer_error;
|
|
|
|
#ifdef USE_FREERTOS
|
|
xSemaphoreHandle sem_readySignal;
|
|
portBASE_TYPE xHigherPriorityTaskWoken;
|
|
xSemaphoreHandle xBusyMutex;
|
|
#endif
|
|
|
|
} I2CRecTypeDef;
|
|
|
|
/* Local Prototypes */
|
|
static void PIOS_I2C_InitPeripheral(void);
|
|
static void EV_IRQHandler(I2CRecTypeDef *i2cx);
|
|
static void ER_IRQHandler(I2CRecTypeDef *i2cx);
|
|
|
|
/* Local Variables */
|
|
static I2CRecTypeDef I2CRec;
|
|
|
|
/* Local Functions */
|
|
static void TransferStart(I2CRecTypeDef *i2cx);
|
|
static void TransferEnd(I2CRecTypeDef *i2cx);
|
|
|
|
/* Macros */
|
|
#ifdef USE_DEBUG_PINS
|
|
#define DEBUG_PIN_ISR 0
|
|
#define DEBUG_PIN_BUSY 1
|
|
#define DEBUG_PIN_WAIT 2
|
|
#define DEBUG_PIN_ASSERT 7
|
|
#define DebugPinHigh(x) PIOS_DEBUG_PinHigh(x)
|
|
#define DebugPinLow(x) PIOS_DEBUG_PinLow(x)
|
|
#else
|
|
#define DebugPinHigh(x)
|
|
#define DebugPinLow(x)
|
|
#endif
|
|
|
|
#define Assert(exp) PIOS_DEBUG_Assert(exp)
|
|
|
|
|
|
/**
|
|
* Initializes IIC driver
|
|
* \param[in] mode currently only mode 0 supported
|
|
* \return < 0 if initialisation failed
|
|
*/
|
|
int32_t PIOS_I2C_Init(void)
|
|
{
|
|
/* Configure IIC pins in open drain mode */
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
GPIO_StructInit(&GPIO_InitStructure);
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
|
|
|
|
GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SCL_PIN;
|
|
GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure);
|
|
|
|
GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SDA_PIN;
|
|
GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure);
|
|
|
|
PIOS_I2C_InitPeripheral();
|
|
|
|
|
|
#ifdef USE_FREERTOS
|
|
vSemaphoreCreateBinary(I2CRec.sem_readySignal);
|
|
I2CRec.xBusyMutex = xSemaphoreCreateMutex();
|
|
#endif // USE_FREERTOS
|
|
|
|
TransferEnd(&I2CRec);
|
|
|
|
/* Configure and enable I2C interrupts */
|
|
NVIC_InitTypeDef NVIC_InitStructure;
|
|
NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_EV_CHANNEL;
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_EV_PRIORITY;
|
|
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
|
|
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_ER_CHANNEL;
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_ER_PRIORITY;
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
DebugPinLow(2);
|
|
|
|
/* No error */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Internal function to (re-)initialize the I2C peripheral
|
|
*/
|
|
static void PIOS_I2C_InitPeripheral(void)
|
|
{
|
|
I2C_InitTypeDef I2C_InitStructure;
|
|
I2CRecTypeDef *i2cx = &I2CRec;
|
|
|
|
/* Prepare I2C init-struct */
|
|
I2C_StructInit(&I2C_InitStructure);
|
|
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
|
|
I2C_InitStructure.I2C_OwnAddress1 = 0;
|
|
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
|
|
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
|
|
|
|
/* Define base address */
|
|
i2cx->base = PIOS_I2C_PORT;
|
|
|
|
/* enable peripheral clock of I2C */
|
|
RCC_APB1PeriphClockCmd(PIOS_I2C_CLK, ENABLE);
|
|
|
|
/* Set I2C clock bus clock params */
|
|
/* Note that the STM32 driver handles value <= 100kHz differently! (duty cycle always 1:1) */
|
|
/* Important: bus frequencies > 400kHz don't work stable */
|
|
I2C_InitStructure.I2C_DutyCycle = PIOS_I2C_DUTY_CYCLE;
|
|
I2C_InitStructure.I2C_ClockSpeed = PIOS_I2C_BUS_FREQ;
|
|
|
|
/* Trigger software reset via I2C_DeInit */
|
|
I2C_DeInit(i2cx->base);
|
|
|
|
/* Clear transfer state and error value */
|
|
i2cx->transfer_state.ALL = 0;
|
|
i2cx->transfer_error = 0;
|
|
|
|
/* Configure I2C peripheral */
|
|
I2C_Init(i2cx->base, &I2C_InitStructure);
|
|
}
|
|
|
|
|
|
#ifdef USE_FREERTOS
|
|
/**
|
|
* Semaphore handling: requests the IIC interface
|
|
* \param[in] timeout Timeout in ticks, 0 for no delay
|
|
* \return TRUE when the lock to the device was obtained
|
|
*/
|
|
bool PIOS_I2C_LockDevice(portTickType timeout)
|
|
{
|
|
if (xSemaphoreTake(I2CRec.xBusyMutex, timeout) == pdTRUE)
|
|
{
|
|
// Ok, got device
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Semaphore handling: releases the IIC interface for other tasks
|
|
* \return < 0 on errors
|
|
*/
|
|
void PIOS_I2C_UnlockDevice(void)
|
|
{
|
|
xSemaphoreGive(I2CRec.xBusyMutex);
|
|
}
|
|
|
|
#endif // USE_FREERTOS
|
|
|
|
|
|
/**
|
|
* Internal function called at the start of a transfer
|
|
*/
|
|
static void TransferStart(I2CRecTypeDef *i2cx)
|
|
{
|
|
Assert(i2cx->transfer_state.BUSY == 0);
|
|
|
|
DebugPinHigh(DEBUG_PIN_BUSY);
|
|
i2cx->transfer_state.BUSY = 1;
|
|
|
|
// Enable Interrupts: I2V2 event, buffer and error interrupt
|
|
I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
|
|
}
|
|
|
|
/**
|
|
* Internal function called at the end of a transfer
|
|
*/
|
|
static void TransferEnd(I2CRecTypeDef *i2cx)
|
|
{
|
|
// Disable all interrupts
|
|
I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
|
|
|
|
DebugPinLow(DEBUG_PIN_BUSY);
|
|
i2cx->transfer_state.BUSY = 0;
|
|
|
|
#ifdef USE_FREERTOS
|
|
if (i2cx->transfer_state.INIRQ)
|
|
{
|
|
xSemaphoreGiveFromISR(i2cx->sem_readySignal, &i2cx->xHigherPriorityTaskWoken);
|
|
}
|
|
else
|
|
{
|
|
xSemaphoreGive(i2cx->sem_readySignal);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Checks if transfer is finished
|
|
* \return 1 if ongoing transfer
|
|
* \return <=0 no transfer is busy; return value indicates error value of last transfer
|
|
* (PIOS_I2C_TransferBegin() has to be called again)
|
|
*/
|
|
int32_t PIOS_I2C_TransferCheck(void)
|
|
{
|
|
I2CRecTypeDef *i2cx = &I2CRec;
|
|
|
|
if(i2cx->transfer_state.BUSY)
|
|
return 1;
|
|
|
|
return i2cx->transfer_error;
|
|
}
|
|
|
|
/**
|
|
* Stop the current transfer
|
|
* \param error the error that must be reported
|
|
*/
|
|
void PIOS_I2C_TerminateTransfer(uint32_t error)
|
|
{
|
|
I2CRecTypeDef *i2cx = &I2CRec;
|
|
|
|
/* Send stop condition */
|
|
I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
|
|
/* Re-initialize peripheral */
|
|
PIOS_I2C_InitPeripheral();
|
|
|
|
i2cx->transfer_error = error;
|
|
}
|
|
|
|
/**
|
|
* Waits until transfer is finished.
|
|
* \return error value of the transfer
|
|
*/
|
|
int32_t PIOS_I2C_TransferWait(void)
|
|
{
|
|
I2CRecTypeDef *i2cx = &I2CRec;
|
|
|
|
DebugPinHigh(DEBUG_PIN_WAIT);
|
|
|
|
#ifdef USE_FREERTOS
|
|
|
|
if (i2cx->transfer_state.BUSY)
|
|
{
|
|
// Wait until we see the ready signal
|
|
if (xSemaphoreTake(i2cx->sem_readySignal, PIOS_I2C_TIMEOUT_VALUE/portTICK_RATE_MS) == pdTRUE)
|
|
{
|
|
// OK, got the semaphore, release it again
|
|
Assert(i2cx->transfer_state.BUSY == 0);
|
|
}
|
|
else
|
|
{
|
|
PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
#else
|
|
uint32_t repeat_ctr = PIOS_I2C_TIMEOUT_VALUE;
|
|
uint16_t last_buffer_ix = i2cx->buffer_ix;
|
|
|
|
if (i2cx->transfer_state.BUSY)
|
|
{
|
|
while(--repeat_ctr > 0)
|
|
{
|
|
/* Check if buffer index has changed - if so, reload repeat counter */
|
|
if(i2cx->buffer_ix != last_buffer_ix) {
|
|
repeat_ctr = PIOS_I2C_TIMEOUT_VALUE;
|
|
last_buffer_ix = i2cx->buffer_ix;
|
|
}
|
|
|
|
/* Get transfer state */
|
|
int32_t check_state = PIOS_I2C_TransferCheck();
|
|
|
|
/* Exit if transfer finished */
|
|
if(check_state <= 0)
|
|
{
|
|
DebugPinLow(DEBUG_PIN_WAIT);
|
|
return check_state;
|
|
}
|
|
}
|
|
|
|
/* Timeout error - something is stalling... */
|
|
PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT);
|
|
}
|
|
#endif
|
|
|
|
DebugPinLow(DEBUG_PIN_WAIT);
|
|
|
|
return i2cx->transfer_error;
|
|
}
|
|
|
|
/**
|
|
* Perform a transfer. No previous transfer should be ongoing when this function is called.
|
|
* When the function returns, the transfer is finished.
|
|
* See PIOS_I2C_StartTransfer() for details on the parameters.
|
|
* \return 0 no error
|
|
* \return < 0 on errors
|
|
*
|
|
*/
|
|
int32_t PIOS_I2C_Transfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len)
|
|
{
|
|
PIOS_I2C_StartTransfer(transfer, address, buffer, len);
|
|
return PIOS_I2C_TransferWait();
|
|
}
|
|
|
|
/**
|
|
* Starts a new transfer. No previous transfer should be ongoing when this function is called.
|
|
* When this function returns, the new transfer is ongoing. PIOS_I2C_TransferWait() should be called
|
|
* to wait for the transfer to finish and to retrieve the result code.
|
|
* \param[in] transfer type:<BR>
|
|
* <UL>
|
|
* <LI>I2C_Read: a common Read transfer
|
|
* <LI>I2C_Write: a common Write transfer
|
|
* <LI>I2C_Write_WithoutStop: don't send stop condition after transfer to allow
|
|
* a restart condition (e.g. used to access EEPROMs)
|
|
* \param[in] address of I2C device (bit 0 always cleared)
|
|
* \param[in] *buffer pointer to transmit/receive buffer
|
|
* \param[in] len number of bytes which should be transmitted/received
|
|
*/
|
|
void PIOS_I2C_StartTransfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len)
|
|
{
|
|
I2CRecTypeDef *i2cx = &I2CRec;
|
|
|
|
// Should not be busy
|
|
if (i2cx->transfer_state.BUSY)
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_FREERTOS
|
|
// Consume Ready semaphore in case it would be there for some reason
|
|
xSemaphoreTake(i2cx->sem_readySignal, 0);
|
|
Assert(xSemaphoreTake(i2cx->sem_readySignal, 0) == pdFALSE);
|
|
#endif
|
|
|
|
// Clear state
|
|
i2cx->transfer_state.ALL = 0;
|
|
i2cx->transfer_error = 0;
|
|
|
|
// Set buffer length and start index
|
|
i2cx->buffer_len = len;
|
|
i2cx->buffer_ix = 0;
|
|
|
|
if(transfer == I2C_Read)
|
|
{
|
|
/* Take new address/buffer/len */
|
|
/* Set bit 0 for read operation */
|
|
i2cx->i2c_address = address | 1;
|
|
/* Ensure that previous TX buffer won't be accessed */
|
|
i2cx->tx_buffer_ptr = NULL;
|
|
i2cx->rx_buffer_ptr = buffer;
|
|
// Ack the bytes we will be getting
|
|
I2C_AcknowledgeConfig(i2cx->base, ENABLE);
|
|
}
|
|
else if(transfer == I2C_Write || transfer == I2C_Write_WithoutStop)
|
|
{
|
|
/* Take new address/buffer/len */
|
|
/* Clear bit 0 for write operation */
|
|
i2cx->i2c_address = address & 0xfe;
|
|
i2cx->tx_buffer_ptr = buffer;
|
|
/* Ensure that nothing will be received */
|
|
i2cx->rx_buffer_ptr = NULL;
|
|
/* Option to skip stop-condition generation after successful write */
|
|
i2cx->transfer_state.WRITE_WITHOUT_STOP = transfer == I2C_Write_WithoutStop ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
i2cx->transfer_error = I2C_ERROR_UNSUPPORTED_TRANSFER_TYPE;
|
|
return;
|
|
}
|
|
|
|
// Start the transfer
|
|
I2C_GenerateSTART(i2cx->base, ENABLE);
|
|
TransferStart(i2cx);
|
|
}
|
|
|
|
/**
|
|
* Internal function for handling IIC event interrupts
|
|
*/
|
|
static void EV_IRQHandler(I2CRecTypeDef *i2cx)
|
|
{
|
|
uint8_t b;
|
|
uint32_t event;
|
|
|
|
DebugPinHigh(DEBUG_PIN_ISR);
|
|
|
|
// Update state
|
|
i2cx->transfer_state.INIRQ = 1;
|
|
#ifdef USE_FREERTOS
|
|
i2cx->xHigherPriorityTaskWoken = pdFALSE;
|
|
#endif
|
|
|
|
/* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */
|
|
event = I2C_GetLastEvent(i2cx->base);
|
|
|
|
/* The order of the handling blocks is chosen by test results @ 1MHZ */
|
|
/* Don't change this order */
|
|
|
|
/* RxNE set, will be cleared by reading/writing DR */
|
|
/* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */
|
|
/* Or a START or STOP condition generated */
|
|
/* Failsave: really requested a receive transfer? If not, continue to check TXE flag, if not set, */
|
|
/* We'll end up in the unexpected event handler. */
|
|
if(event & I2C_FLAG_RXNE && i2cx->rx_buffer_ptr != NULL) {
|
|
/* Get received data */
|
|
b = I2C_ReceiveData(i2cx->base);
|
|
|
|
/* Failsave: still place in buffer? */
|
|
if(i2cx->buffer_ix < i2cx->buffer_len) {
|
|
i2cx->rx_buffer_ptr[i2cx->buffer_ix++] = b;
|
|
}
|
|
|
|
|
|
/* Last byte received, disable interrupts and return. */
|
|
if(i2cx->transfer_state.STOP_REQUESTED) {
|
|
TransferEnd(i2cx);
|
|
goto isr_return;
|
|
}
|
|
|
|
/* Request NAK and stop condition before receiving last data */
|
|
if(i2cx->buffer_ix >= i2cx->buffer_len-1) {
|
|
/* Request NAK */
|
|
I2C_AcknowledgeConfig(i2cx->base, DISABLE);
|
|
/* Request stop condition */
|
|
I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
i2cx->transfer_state.STOP_REQUESTED = 1;
|
|
}
|
|
goto isr_return;
|
|
}
|
|
|
|
/* ADDR set, TRA flag not set (indicates transmitter/receiver mode). */
|
|
/* ADDR will be cleared by a read of SR1 followed by a read of SR2 (done by I2C_GetLastEvent) */
|
|
/* If transmitter mode is selected (TRA set), we go on, TXE will be catched to send the first byte */
|
|
if((event & I2C_FLAG_ADDR) && !(event & I2C_FLAG_TRA)) {
|
|
/* Address sent (receiver mode), receiving first byte - check if we already have to request NAK/Stop */
|
|
if(i2cx->buffer_len == 1) {
|
|
/* Request NAK */
|
|
I2C_AcknowledgeConfig(i2cx->base, DISABLE);
|
|
/* Request stop condition */
|
|
I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
i2cx->transfer_state.STOP_REQUESTED = 1;
|
|
}
|
|
goto isr_return;
|
|
}
|
|
|
|
/* TxE set, will be cleared by writing DR, or after START or STOP was generated */
|
|
/* This handling also applies for BTF, as TXE will alway be set if BTF is. */
|
|
/* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */
|
|
/* Or a START or STOP condition generated */
|
|
if(event & I2C_FLAG_TXE) {
|
|
/* Last byte already sent, disable interrupts and return. */
|
|
if(i2cx->transfer_state.STOP_REQUESTED) {
|
|
TransferEnd(i2cx);
|
|
goto isr_return;
|
|
}
|
|
|
|
if(i2cx->buffer_ix < i2cx->buffer_len) {
|
|
/* Checking tx_buffer_ptr for NULL is a failsafe measure. */
|
|
I2C_SendData(i2cx->base, (i2cx->tx_buffer_ptr == NULL) ? 0 : i2cx->tx_buffer_ptr[i2cx->buffer_ix++]);
|
|
goto isr_return;
|
|
}
|
|
|
|
/* Peripheral is transfering last byte, request stop condition / */
|
|
/* On write-without-stop transfer-type, request start condition instead */
|
|
i2cx->transfer_state.STOP_REQUESTED = 1;
|
|
if(!i2cx->transfer_state.WRITE_WITHOUT_STOP)
|
|
{
|
|
I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
}
|
|
else
|
|
{
|
|
DebugPinHigh(2);
|
|
}
|
|
|
|
if(i2cx->buffer_len == 0) {
|
|
TransferEnd(i2cx);
|
|
} else {
|
|
/* Disable the I2C_IT_BUF interrupt after sending the last buffer data */
|
|
/* (last EV8) to not allow a new interrupt just with TxE - only BTF will generate it */
|
|
/* If this is not done, BUSY will be cleared before the transfer is finished */
|
|
I2C_ITConfig(i2cx->base, I2C_IT_BUF, DISABLE);
|
|
}
|
|
goto isr_return;
|
|
}
|
|
|
|
// Send address
|
|
/* SB set, cleared by reading SR1 (done by I2C_GetLastEvent) followed by writing DR register */
|
|
if(event & I2C_FLAG_SB) {
|
|
/* Don't send address if stop was requested (WRITE_WITHOUT_STOP - mode, start condition was sent) */
|
|
/* We have to wait for the application to start the next transfer */
|
|
if(i2cx->transfer_state.STOP_REQUESTED) {
|
|
TransferEnd(i2cx);
|
|
DebugPinLow(DEBUG_PIN_ISR);
|
|
goto isr_return;
|
|
}
|
|
|
|
/* Send IIC address */
|
|
I2C_Send7bitAddress(i2cx->base, i2cx->i2c_address,
|
|
(i2cx->i2c_address & 1)
|
|
? I2C_Direction_Receiver
|
|
: I2C_Direction_Transmitter);
|
|
goto isr_return;
|
|
}
|
|
|
|
DebugPinHigh(DEBUG_PIN_ASSERT);DebugPinLow(DEBUG_PIN_ASSERT);
|
|
|
|
//
|
|
// FredericG: Despite the comments below, it seems to me that this situation can happen and can
|
|
// be ignored without ill effects...
|
|
// For now this condition does not stop the transfer, but further investigation in needed
|
|
//
|
|
|
|
// Assert(0);
|
|
//
|
|
// /* This code is only reached if something got wrong, e.g. interrupt handler is called too late, */
|
|
// /* The device reset itself (while testing, it was always event 0x00000000). we have to stop the transfer, */
|
|
// /* Else read/write of corrupt data may be the result. */
|
|
//
|
|
// /* Notify error */
|
|
// PIOS_I2C_UnexpectedEvent = event;
|
|
// i2cx->transfer_error = I2C_ERROR_UNEXPECTED_EVENT;
|
|
//
|
|
// TransferEnd(i2cx);
|
|
//
|
|
// /* Do dummy read to send NAK + STOP condition */
|
|
// I2C_AcknowledgeConfig(i2cx->base, DISABLE);
|
|
// b = I2C_ReceiveData(i2cx->base);
|
|
// I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
|
|
isr_return:
|
|
// Cause task-switch when needed
|
|
#ifdef USE_FREERTOS
|
|
portEND_SWITCHING_ISR(i2cx->xHigherPriorityTaskWoken);
|
|
#endif
|
|
|
|
// Update state
|
|
i2cx->transfer_state.INIRQ = 0;
|
|
DebugPinLow(DEBUG_PIN_ISR);
|
|
}
|
|
|
|
|
|
/**
|
|
* Internal function for handling IIC error interrupts
|
|
*/
|
|
static void ER_IRQHandler(I2CRecTypeDef *i2cx)
|
|
{
|
|
/* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */
|
|
uint32_t event = I2C_GetLastEvent(i2cx->base);
|
|
|
|
i2cx->transfer_state.INIRQ = 1;
|
|
|
|
/* Note that only one error number is available */
|
|
/* The order of these checks defines the priority */
|
|
|
|
/* Bus error (start/stop condition during read */
|
|
/* Unlikely, should only be relevant for slave mode?) */
|
|
if(event & I2C_FLAG_BERR) {
|
|
I2C_ClearITPendingBit(i2cx->base, I2C_IT_BERR);
|
|
i2cx->transfer_error = I2C_ERROR_BUS;
|
|
}
|
|
|
|
/* Arbitration lost */
|
|
if(event & I2C_FLAG_ARLO) {
|
|
I2C_ClearITPendingBit(i2cx->base, I2C_IT_ARLO);
|
|
i2cx->transfer_error = I2C_ERROR_ARBITRATION_LOST;
|
|
}
|
|
|
|
/* No acknowledge received from slave (e.g. slave not connected) */
|
|
if(event & I2C_FLAG_AF) {
|
|
I2C_ClearITPendingBit(i2cx->base, I2C_IT_AF);
|
|
i2cx->transfer_error = I2C_ERROR_SLAVE_NOT_CONNECTED;
|
|
/* Send stop condition to release bus */
|
|
I2C_GenerateSTOP(i2cx->base, ENABLE);
|
|
}
|
|
|
|
/* Notify that transfer has finished (due to the error) */
|
|
TransferEnd(i2cx);
|
|
|
|
i2cx->transfer_state.INIRQ = 0;
|
|
}
|
|
|
|
|
|
/* Interrupt vectors */
|
|
PIOS_I2C_IRQ_EV_HANDLER
|
|
{
|
|
EV_IRQHandler((I2CRecTypeDef *)&I2CRec);
|
|
}
|
|
|
|
PIOS_I2C_IRQ_ER_HANDLER
|
|
{
|
|
ER_IRQHandler((I2CRecTypeDef *)&I2CRec);
|
|
}
|
|
|
|
#endif
|