|
STM32F030 проблема с I2C, Непонятное поведение I2C периферии контроллера |
|
|
|
Nov 7 2015, 23:52
|
Местный
  
Группа: Участник
Сообщений: 294
Регистрация: 5-08-14
Из: Ярославль
Пользователь №: 82 466

|
Здравствуйте. Использую контроллер STM32F030F4P6. К нему по I2C подключен 1-Wire Master DS2482-100, а к нему, в свою очередь, по 1-Wire подключен термометр DS1820. В Keil 5 написан следующий код. Инициализация I2C: CODE void i2c1_init(void) { GPIO_InitTypeDef PORT; I2C_InitTypeDef I2C_INIT; I2C_StructInit(&I2C_INIT); I2C_INIT.I2C_Ack = I2C_Ack_Disable; I2C_INIT.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_INIT.I2C_Mode = I2C_Mode_I2C; I2C_INIT.I2C_OwnAddress1 = 0xAA; I2C_INIT.I2C_AnalogFilter = I2C_AnalogFilter_Disable; I2C_INIT.I2C_DigitalFilter = 0; I2C_INIT.I2C_Timing = 0x20000A0D; // Расчитано в Excel-таблице I2C_Timing_Configuration_V1.0.1.xls от ST I2C_DeInit(I2C1); PORT.GPIO_Mode = GPIO_Mode_AF; PORT.GPIO_OType = GPIO_OType_OD; PORT.GPIO_PuPd = GPIO_PuPd_NOPULL; PORT.GPIO_Speed = GPIO_Speed_2MHz; PORT.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_Init(GPIOA, &PORT); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_4); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4);
I2C_Init(I2C1, &I2C_INIT); I2C_Cmd(I2C1, ENABLE);
} Работа с DS2482-100: CODE t1 = 0; t2 = 0; while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) == SET); I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xF0); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xB4); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
do { I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); } while (I2C_ReceiveData(I2C1) & 0x01);
I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); // Зависает здесь I2C_SendData(I2C1, 0xA5); I2C_SendData(I2C1, 0xCC); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
do { I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); } while (I2C_ReceiveData(I2C1) & 0x01);
/* I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xA5); I2C_SendData(I2C1, 0x44); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); while(I2C_ReceiveData(I2C1) & 0x01) {} */
I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xA5); I2C_SendData(I2C1, 0xBE); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
do { I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); } while (I2C_ReceiveData(I2C1) & 0x01);
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0x96); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET); I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xE1); I2C_SendData(I2C1, 0xE1); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET); I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); t1 = I2C_ReceiveData(I2C1);
I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0x96); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET);
do { I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); } while (I2C_ReceiveData(I2C1) & 0x01);
I2C_TransferHandling(I2C1, DS2482A_Addr, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET); I2C_SendData(I2C1, 0xE1); I2C_SendData(I2C1, 0xE1); while(I2C_GetFlagStatus(I2C1, I2C_ISR_TC) == RESET); I2C_TransferHandling(I2C1, DS2482A_Addr, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read); t2 = I2C_ReceiveData(I2C1); while(I2C_GetFlagStatus(I2C1, I2C_ISR_STOPF) == RESET); I2C_ClearFlag(I2C1, I2C_ICR_STOPCF); Если данный код вручную прошагать в отладчике, то никаких проблем не проявляется и в переменных t1 и t2 будут 0x05 и 0x50, как и указано в даташите на DS18B20. Однако при обычном запуске программа зависает на четвертом блоке кода на проверке флага TXIS (отмечено комментарием). Флаг TXIS действительно не установлен, при этом флаг TXE установлен, хотя как я понял из даташита, в данном случае они должны подниматься вместе. Также установлен флаг TC, а NACKF, ARLO и другие сняты. Если установить Breakpoint на строчку выше (I2C_TransferHandling) и выполнить Step Over, то TXIS будет установлен и работа продолжится. Перепробовал множество вариантов, итог всегда один - пошагово работает, при обычном запуске виснет на проверке флага. У кого-нибудь было такое поведение I2C периферии STM32F0?
Сообщение отредактировал IgorKossak - Nov 8 2015, 17:52
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
 |
Ответов
|
Nov 9 2015, 11:29
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(p_kav @ Nov 9 2015, 11:51)  А посмотреть можно? Или это коммерческая тайна? Можно. Однако я не пишу, как в примерах, с огромным, каждый раз другим, main(). Общение с DS2484 не написано как эксклюзив пользования I2C, а есть у меня I2C драйвер (или скорее, две функции), через который происходит доступ к шине, а доступ к DS2484 ими пользуется. Многое завязано на мои наработанные библиотеки. Сложно будет вычленить код, чтобы он странслировался вне моей, как сейчас модно говорить, "экосистемы". Например, код работы с DS2484: CODE /*---------------------------------------------------------------------------- * Name: DS2484.c * Purpose: I2C to 1-Wire IC driver * * Version: V1.00 *---------------------------------------------------------------------------- * History: * V1.00 Initial Version *----------------------------------------------------------------------------*/
#include "DS2484.h" // // General HAL: // #include <HAL_I2C.h> #include <HAL_CMx.h>
/* --------------------------------------------------------------------- * * What I2C peripheral to use. */ const uint8_t DS_I2C_Peripheral __attribute__((weak, noinline)) = 0;
/* --------------------------------------------------------------------- * * Tries to recover from the stall state if any. */ __attribute__((weak)) void DS_Recover(void) { I2C_Recover(DS_I2C_Peripheral); } /* --------------------------------------------------------------------- * * Reads a currently pointed register from the device. * If 'source' != 0, the source pointer code will be set before. * * Returns: -1 on error or [0..255] (read byte) on success. */ int DS_ReadData(uint8_t source) { int res = -1, wcnt = source ? 2 : 0; unsigned char I2CBuf[] = {DS_DEVICE_ID, DS_SET_READPOINTER_COMMAND, source, 0}; if (ReadW_I2C(DS_I2C_Peripheral, I2CBuf, wcnt, 1) > 0) res = I2CBuf[1 + wcnt];
return res; } /* --------------------------------------------------------------------- * * Sends a command eventually followed by a data byte to * the device. * * Returns true on success. */ bool DS_SendCommand(uint8_t cmd, uint8_t dat) { int wcnt = 2; unsigned char I2CBuf[] = {DS_DEVICE_ID, cmd, dat};
switch (cmd) { case DS_DEVICE_RESET_COMMAND: case DS_1WIRE_RESET_COMMAND: case DS_1WIRE_READBYTE_COMMAND: wcnt--; // no parameter break; } return WriteW_I2C(DS_I2C_Peripheral, I2CBuf, wcnt) > 0; } /* --------------------------------------------------------------------- * * Weak callback invoked before and while DS_WaitFor1Wire() * function waits for the 1-Wire bus got free. * * 'tmo' in ms when the function called before the loop (init) * 'tmo' is 0 while calling in the loop (poll). * * The function to return false as soon as the timeout has expired. * The weak implementation always returns true. */ __attribute__((weak)) bool DS_WaitingLoop(int tmo) {return true;}
/* --------------------------------------------------------------------- * * Waits as long as 1WB (bus busy) but not longer than * given 'tmo' in ms. * * ATTENTION! Assumes, the internal register pointer in the device * is set to the status register by the preceeding * operation. * * Returns status bits ( >= 0) on success (1WB is 0 anyway) * or < 0 if expired/any errors. Actually, the negative value * is still keeping the last status register content in the lowest * byte. */ int DS_WaitFor1Wire(int tmo) { int res; bool done = true; DS_WaitingLoop(tmo); // start timeout while((res = DS_ReadData(0)) >= 0 // status got && (res & DS_STATUS_1WB) // still busy && (done = DS_WaitingLoop(0))); // not expired yet // tricky make negative while // keeping the status bits on return done ? res : res + INT32_MIN; } /* --------------------------------------------------------------------- * * Starts 1-Wire communication issueing 1-Wire RESET and waiting for * the operation completed (1WS is no more busy and PPD bit is set due * to some device on the bus). * * Returns the value of the status register in the lowest byte * or -1 if an error occured. * * NOTE! - since PPD must be set for success, the result is always > 0 * in this case. */ int DS_Start1Wire(void) { int res = -1, val; if (DS_SendCommand(DS_1WIRE_RESET_COMMAND, 0) && (val = DS_WaitFor1Wire(DS_TIMEOUT_PER_BYTE/2)) > 0 && (val & DS_STATUS_PPD)) // a 1-Wire device is present { res = val; } return res; } /* --------------------------------------------------------------------- * */ bool Init_DS2484(void) { return DS_SendCommand(DS_DEVICE_RESET_COMMAND, 0); } //------------------ EOF -------------------------------------------------------
В коде жирным выделены функции транзакций по I2C: ReadW_I2C() и WriteW_I2C(). В свою очередь, они реализованы в " драйвере": CODE /*---------------------------------------------------------------------------- * Name: stm32f0xx_iic.c * Purpose: STM32F0xx I2C support * * Version: V1.00, tested on STM32F051 *---------------------------------------------------------------------------- * History: * V1.00 Supports multibus access (now over two I2C busses). * The specification in HAL_I2C.h has been also changed to * add the 'port' parameter to the I/O functions. *----------------------------------------------------------------------------*/ // #include "stm32f0xx_proc.h" #include "stm32f0xx_iic.h" #include <stm32f0xx_dma.h> #include <stm32f0xx_i2c.h> #include <HAL_DWT.h>
// -----------------------------------------------------------------------------
#define GPIO_SCL1 {GPIOB, {GPIO_Pin_6, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1} #define GPIO_SDA1 {GPIOB, {GPIO_Pin_7, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1} #define GPIO_SCL1_GPIO GPIOB
#define GPIO_SCL2 {GPIOB, {GPIO_Pin_10, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1} #define GPIO_SDA2 {GPIOB, {GPIO_Pin_11, GPIO_Mode_AF, GPIO_Speed_10MHz, GPIO_OType_OD, GPIO_PuPd_UP}, Bit_SET, GPIO_AF_1} #define GPIO_SCL2_GPIO GPIOB
// ----------------------------------------------------------------------------- // // I2C access control block type // typedef struct i2c_control {
I2C_TypeDef *i2c; // pointer to the peripheral RCC_PCLK_TypeDef rcc[2]; // clock init structures for I2C and AGFIO GPIO_PIN_TypeDef lines[2], // SCL/SDA lines init structures gpio; // SCL as GPIO init structure DMA_Channel_TypeDef *rx_DMAChannel, // pointer the DMA channel servicing RX *tx_DMAChannel; // pointer the DMA channel servicing TX int rate, // I2C rate wcnt, // counter of bytes to write rcnt, // counter of bytes to read icnt, // counter of all bytes indx, // current index in the exchange buffer code; // return code char complete, // complete flag errorflag, // error flag guard; // I2C stall guard flag unsigned char *ptr; // exchange buffer pointer I2C_CallBackType cb; // call back pointer
} I2C_Control_Type;
// ----------------------------------------------------------------------------- // // Constant structures // #if (USE_I2C_TX_DMA1 || USE_I2C_RX_DMA1) static const RCC_PCLK_TypeDef DMA1Clocks[] = {RCC_DMA1}; #endif
static const NVIC_InitTypeDef NVIC_I2CInitStructure[] = { { I2C1_IRQn, I2C_IT_Priority, ENABLE }, { I2C2_IRQn, I2C_IT_Priority, ENABLE } }; // ----------------------------------------------------------------------------- // // I2C access control blocks for I2C1 and I2C2 // static I2C_Control_Type control[] = {{I2C1, RCC_I2C1, {GPIO_SCL1, GPIO_SDA1}, GPIO_SCL1, DMA1_Channel3, DMA1_Channel2, }, {I2C2, RCC_I2C2, {GPIO_SCL2, GPIO_SDA2}, GPIO_SCL2, DMA1_Channel5, DMA1_Channel4, }, };
// -- USEFULL LOCAL MACROS -----------------------------------------------------
#define BITS_ONE_BYTE 9
/* INIT_I2C_FLAGS: 1). Reset flags and pointers 2). Assume Buffer Index = 1 3). Clear possible RxNE by reading RXDR out */ #define INIT_I2C_FLAGS© { c->code = \ c->complete = \ c->errorflag = 0; \ c->indx = 1; \ I2C_GET_DR(c->i2c);}
#define I2C_FINISH_CALLBACK(n, c, e) { if (c->cb) \ c->cb(n, e);} #define I2C_IS_BUSY(n) I2C_Is_Busy(n) #define I2C_ISSUE_START(d) I2C_START(d); #define I2C_ISSUE_STOP(d) I2C_STOP(d);
// -- LOCAL PROCEDURES ---------------------------------------------------------
// ----------------------------------------------------------------------------- // // Implements the initialization of the expire counter for SB bit timeout // and returns its value. // static int32_t I2C_SBTimeoutInit(uint8_t port, uint16_t bits) { int32_t us = bits*(1000000/control[port].rate + 1); return DWT_NextTP(us); // time point ahead } // ----------------------------------------------------------------------------- // // Implements the modification and comparision of the expire counter for // SB bit timeout. This function is called out of the loop. It should // return any value >0 if the timeout has expired. // static uint8_t I2C_SBTimeoutExpired(int32_t *tp) { return !DWT_Compare(*tp); } // ----------------------------------------------------------------------------- // // Makes the main initilization of the I2C peripheral // (and its assigned DMA channels if any in use). // If the input parameter >0 (TRUE), forces the // peripheral software reset before. // static void ReInit_I2C(uint8_t port, uint8_t full) { I2C_TypeDef *i2c = control[port].i2c; I2C_InitTypeDef I2C_InitStruct = { 0, I2C_AnalogFilter_Enable, 0, I2C_Mode_I2C, 0, I2C_Ack_Enable, I2C_AcknowledgedAddress_7bit }; RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); switch (control[port].rate/100000) { case 0: case 1: // around 100kHz I2C_InitStruct.I2C_Timing = (RCC_Clocks.I2C1CLK_Frequency/4000000 - 1) << 28 | 0x0042 << 16 | // the constants: see doc 0x0F13; break; case 3: case 4: // around 400kHz I2C_InitStruct.I2C_Timing = (RCC_Clocks.I2C1CLK_Frequency/8000000 - 1) << 28 | 0x0033 << 16 | // the constants: see doc 0x0309; break; } RCC_Configuration (control[port].rcc, sizeof(control[port].rcc[0])); if (full) { CLEAR_CR1(i2c, ~(0)); } I2C_INIT(i2c, I2C_InitStruct); // (sets PE, too) IRQ_INIT(NVIC_I2CInitStructure[port]); GPIO_Configuration(control[port].lines, sizeof(control[port].lines));
#if (USE_I2C_RX_DMA1) { DMA_InitTypeDef DMA_InitRStruct = { (uint32_t)control[port].i2c + offsetof(I2C_TypeDef, RXDR), NULL, // DMA_MemoryBaseAddr, TBD DMA_DIR_PeripheralSRC, // DMA_DIR 0, // DMA_BufferSize, TBD DMA_PeripheralInc_Disable, // DMA_PeripheralInc DMA_MemoryInc_Enable, // DMA_MemoryInc DMA_PeripheralDataSize_Byte,// DMA_PeripheralDataSize DMA_MemoryDataSize_Byte, // DMA_MemoryDataSize DMA_Mode_Normal, // DMA_Mode DMA_Priority_Medium, // DMA_Priority DMA_M2M_Disable // DMA_M2M }; DMA_INIT(control[port].rx_DMAChannel, DMA_InitRStruct); } I2C_DMACmd(i2c, I2C_DMAReq_Rx, ENABLE); #endif
#if (USE_I2C_TX_DMA1) { DMA_InitTypeDef DMA_InitTStruct = { (uint32_t)control[port].i2c + offsetof(I2C_TypeDef, TXDR), NULL, // DMA_MemoryBaseAddr, TBD DMA_DIR_PeripheralDST, // DMA_DIR 0, // DMA_BufferSize, TBD DMA_PeripheralInc_Disable, // DMA_PeripheralInc DMA_MemoryInc_Enable, // DMA_MemoryInc DMA_PeripheralDataSize_Byte,// DMA_PeripheralDataSize DMA_MemoryDataSize_Byte, // DMA_MemoryDataSize DMA_Mode_Normal, // DMA_Mode DMA_Priority_Medium, // DMA_Priority DMA_M2M_Disable // DMA_M2M }; DMA_INIT(control[port].tx_DMAChannel, DMA_InitTStruct); } I2C_DMACmd(i2c, I2C_DMAReq_Tx, ENABLE); #endif } // ----------------------------------------------------------------------------- // // Function to recover from fatal bus/slave/I2C peripheral states mentioned in // Errata. // // 1). Tries to recover from slave stall by issue of 11 clocks. // For this purpose the SCL line is temporary reconfigured and // driven as a GPIO. // 2). Makes reset and full reinit of the I2C peripheral. // void I2C_Recover(uint8_t port) { uint8_t pulses = 2*11; I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c;
#if (USE_I2C_RX_DMA1)
DMA_STOP(ctrl->rx_DMAChannel); // DMA_Cmd(ctrl->rx_DMAChannel, DISABLE); #endif #if (USE_I2C_TX_DMA1)
DMA_STOP(ctrl->tx_DMAChannel); // DMA_Cmd(ctrl->tx_DMAChannel, DISABLE); #endif I2C_PE_CLEAR(i2c); // software reset of I2C
GPIO_Configuration(&ctrl->gpio, sizeof(ctrl->gpio)); while (pulses--) { TogglePin(&ctrl->gpio); DWT_Delay(5); // 5us pulse for 100kHz period } ReInit_I2C(port, 1); ctrl->guard = 0; } // ----------------------------------------------------------------------------- // static uint8_t I2C_Is_Busy(uint8_t port) { int bsy, tp = I2C_SBTimeoutInit(port, 2*BITS_ONE_BYTE); // wait as for two bytes do bsy = I2C_BUSY(control[port].i2c); while ((bsy != 0) && !I2C_SBTimeoutExpired(&tp)); return (bsy != 0); } // ----------------------------------------------------------------------------- // // Transaction tail handlers: // static void I2C_Success_Handler(uint8_t port) { I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c; I2C_DISABLE_IE(i2c); #if (USE_I2C_RX_DMA1)
DMA_STOP(ctrl->rx_DMAChannel); #endif #if (USE_I2C_TX_DMA1)
DMA_STOP(ctrl->tx_DMAChannel); #endif I2C_ISSUE_STOP(i2c); ctrl->complete = 1; I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_OK); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // // Issues STOP after Error only if the MCU has been a master on the bus. // static void I2C_May_STOP(uint8_t port, uint32_t e) { I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c; if (e & I2C_FLAG_BUSY) { I2C_ISSUE_STOP(i2c); if ((I2C_GET_CR2(i2c) & I2C_START_BIT) || !ReadPin(&ctrl->lines[1])) // SDA still LOW? { I2C_Recover(port); } } else I2C_GET_DR(i2c); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void I2C_Cancel_Handler(uint8_t port) { I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c;
I2C_DISABLE_IE(i2c); #if (USE_I2C_RX_DMA1)
DMA_STOP(ctrl->rx_DMAChannel); #endif #if (USE_I2C_TX_DMA1)
DMA_STOP(ctrl->tx_DMAChannel); #endif I2C_May_STOP(port, ctrl->code = I2C_GET_SR1(i2c));
ctrl->complete = ctrl->errorflag = 0; I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_CANCELLED); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void I2C_Error_Handler(uint8_t port, uint32_t e) { I2C_Control_Type *ctrl = &control[port];
I2C_DISABLE_IE(ctrl->i2c); #if (USE_I2C_RX_DMA1)
DMA_STOP(ctrl->rx_DMAChannel); #endif #if (USE_I2C_TX_DMA1)
DMA_STOP(ctrl->tx_DMAChannel); #endif I2C_May_STOP(port, e);
ctrl->errorflag = 1; I2C_FINISH_CALLBACK(port, ctrl, I2C_RETURN_ERROR); } #define I2C_SUCCESS_HANDLER(n) I2C_Success_Handler(n) #define I2C_CANCEL_HANDLER(n) I2C_Cancel_Handler(n) #define I2C_ERROR_HANDLER(n, e) I2C_Error_Handler(n, e) // ----------------------------------------------------------------------------- // EVENT INTERRUPT HANDLER // -----------------------------------------------------------------------------
#if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) static void DMA_Reload(DMA_Channel_TypeDef* dmac, void *ptr, uint8_t cnt, uint8_t enable) { DMA_STOP(dmac); // neccessary before reload if (cnt) DMA_RELOAD(dmac, cnt); if (ptr) DMA_MEMPTR(dmac, (uint32_t)ptr); if (enable) DMA_START(dmac); } #endif static void i2c_ReloadTransfer(I2C_Control_Type *ctrl, uint8_t writing) { I2C_TypeDef *i2c = ctrl->i2c; int *pcnt; #if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) DMA_Channel_TypeDef *dmac; #endif if (writing) { #if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) dmac = ctrl->tx_DMAChannel; #endif pcnt = &ctrl->wcnt; } else { #if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) dmac = ctrl->rx_DMAChannel; #endif pcnt = &ctrl->rcnt; } if (*pcnt > UINT8_MAX) { #if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) DMA_Reload(dmac, &ctrl->ptr[ctrl->indx], UINT8_MAX, ENABLE); #endif I2C_NumberOfBytesConfig(i2c, UINT8_MAX); // continue } else { I2C_ReloadCmd (i2c, DISABLE); #if (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) DMA_Reload(dmac, &ctrl->ptr[ctrl->indx], *pcnt, ENABLE); #endif I2C_NumberOfBytesConfig(i2c, *pcnt); // continue } } #define READING 0 #define WRITING !READING
#if !(USE_I2C_TX_DMA1) static void i2c_SendData(I2C_Control_Type *ctrl) { I2C_TypeDef *i2c = ctrl->i2c; I2C_SendData(i2c, ctrl->ptr[ctrl->indx++]); ctrl->icnt--; if (!--ctrl->wcnt) // the last byte is on the way, make ready for TC I2C_ITConfig(i2c, I2C_IT_TCI, ENABLE); } #endif #if !(USE_I2C_RX_DMA1) static void i2c_ReceiveData(I2C_Control_Type *ctrl) { I2C_TypeDef *i2c = ctrl->i2c; ctrl->ptr[ctrl->indx++] = I2C_ReceiveData(i2c); ctrl->icnt--; if (!--ctrl->rcnt) // the last byte got, make ready for TC coming soon I2C_ITConfig(i2c, I2C_IT_TCI, ENABLE); } #endif static void I2Cx_IRQHandler(uint8_t port) { I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c;
// NOTE: flags I2C_IT_BERR, I2C_IT_ARLO, I2C_IT_OVR, I2C_IT_ALERT and I2C_IT_PECERR // belong to the group of error flags triggering the interrupt ERRIE if (I2C_GetITStatus(i2c, I2C_IT_NACKF | I2C_IT_BERR | I2C_IT_ARLO | I2C_IT_OVR | I2C_IT_ALERT | I2C_IT_PECERR )) { // STOP must have been issued by HW after an error on bus. I2C_ERROR_HANDLER(port, I2C_ReadRegister(i2c, I2C_Register_ISR)); } else if (!ctrl->rcnt) // writing only { #if !(USE_I2C_TX_DMA1) if (I2C_GetITStatus(i2c, I2C_IT_TXI)) // disabled if TX by DMA { i2c_SendData(ctrl); // decrements ctrl->wcnt } else // TC: can be request to stop (SoftEnd_Mode) or to reload (Reload_Mode) #endif if (I2C_GetITStatus(i2c, I2C_IT_TCI)) { if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // Request to reload { #if (USE_I2C_TX_DMA1) ctrl->indx += UINT8_MAX; ctrl->wcnt -= UINT8_MAX; ctrl->icnt -= UINT8_MAX; #endif i2c_ReloadTransfer(ctrl, WRITING); } else { // to STOP as complete #if (USE_I2C_TX_DMA1) ctrl->indx += ctrl->wcnt; ctrl->icnt -= ctrl->wcnt; ctrl->wcnt = 0; #endif I2C_SUCCESS_HANDLER(port); } } } else { // read only or write-restart-read #if (USE_I2C_RX_DMA1 && USE_I2C_TX_DMA1) if (I2C_GetITStatus(i2c, I2C_IT_TCI)) { if (ctrl->wcnt) // writing preamble took place, make reading { ctrl->indx += ctrl->wcnt; ctrl->icnt -= ctrl->wcnt; ctrl->wcnt = 0; I2C_MasterRequestConfig(i2c, I2C_Direction_Receiver); i2c_ReloadTransfer(ctrl, READING); I2C_ITConfig(i2c, I2C_IT_NACKI, DISABLE); I2C_GenerateSTART (i2c, ENABLE); } else { // reading if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // to reload as ctrl->rcnt has been >UINT8_MAX { // Assumes, the write preamble cannot be longer than 255 bytes ctrl->rcnt -= UINT8_MAX; ctrl->icnt -= UINT8_MAX; ctrl->indx += UINT8_MAX; i2c_ReloadTransfer(ctrl, READING); } else { ctrl->indx += ctrl->rcnt; ctrl->icnt -= ctrl->rcnt; ctrl->rcnt = 0; I2C_SUCCESS_HANDLER(port); } } } #elif (USE_I2C_RX_DMA1 || USE_I2C_TX_DMA1) // no DMA at all #error "Either both TX and RX DMA or none (interrupt mode) supported so long" #else if (I2C_GetITStatus(i2c, I2C_IT_TXI)) // writing preamble { i2c_SendData(ctrl); // decrements ctrl->wcnt } else if (I2C_GetITStatus(i2c, I2C_IT_RXI)) // reading bytes { i2c_ReceiveData(ctrl); // decrements ctrl->rcnt } else // TC can be request to stop (SoftEnd_Mode), // to reload (Reload_Mode) or // to restart for reading after write preamble if (I2C_GetITStatus(i2c, I2C_IT_TCI)) { if (I2C_GetFlagStatus(i2c, I2C_FLAG_TCR)) // to reload as ctrl->rcnt has been >UINT8_MAX { // Assumes the write preamble cannot be longer than 255 bytes i2c_ReloadTransfer(ctrl, READING); } else if (ctrl->rcnt) // to restart for reading after writing preamble { I2C_MasterRequestConfig(i2c, I2C_Direction_Receiver); i2c_ReloadTransfer(ctrl, READING); I2C_ITConfig(i2c, I2C_IT_TXI // no more TX | I2C_IT_NACKI // not needed while reading | I2C_IT_TCI, DISABLE); I2C_ITConfig(i2c, I2C_IT_RXI, ENABLE); I2C_GenerateSTART(i2c, ENABLE); // restart } else // to stop (reading complete) I2C_SUCCESS_HANDLER(port); } #endif } ctrl->guard = 1; } void I2C1_IRQHandler(void) {I2Cx_IRQHandler(0);} void I2C2_IRQHandler(void) {I2Cx_IRQHandler(1);}
// ----------------------------------------------------------------------------- // // Informs about the current state of the I2C transaction after call to // "Read_I2C": // // 1: I2C transfer completed successfully. // 0: Transfer in progress... // -1: an Error encountered, call I2C_Result to know more. // int32_t State_I2C(uint8_t port) { return (control[port].errorflag ? I2C_RETURN_ERROR : control[port].complete); } // ----------------------------------------------------------------------------- // // Returns the result of the last I2C transaction. // In fact this is last stored (I2Cx->SR1 + I2Cx->SR2 << 16) // The call to this function "destroys" the last result. // int32_t I2C_Result(uint8_t port) { int32_t r = control[port].code; control[port].code = I2C_RETURN_NOTHING; return r; } // ----------------------------------------------------------------------------- // // "The name is the program..." // static int32_t I2C_Start_It(uint8_t port) { I2C_Control_Type *ctrl = &control[port]; I2C_TypeDef *i2c = ctrl->i2c; int32_t cnt = ctrl->icnt, it_Bits = I2C_IT_NACKI | I2C_IT_ERRI; uint16_t slave = ctrl->ptr[0]; // assume 7-bit mode only I2C_DISABLE_IE(i2c); I2C_ClearFlag(i2c, ~(0)); if (!ctrl->rcnt) // write only operation { #if (USE_I2C_TX_DMA1) DMA_Reload(ctrl->tx_DMAChannel, &ctrl->ptr[1], 0, 0); #endif if (ctrl->wcnt > UINT8_MAX) { I2C_TransferHandling(i2c, slave, UINT8_MAX, I2C_Reload_Mode, I2C_Generate_Start_Write); #if (USE_I2C_TX_DMA1) DMA_Reload(ctrl->tx_DMAChannel, NULL, UINT8_MAX, ENABLE); #else it_Bits |= I2C_IT_TCI; #endif } else { I2C_TransferHandling(i2c, slave, ctrl->wcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); #if (USE_I2C_TX_DMA1) DMA_Reload(ctrl->tx_DMAChannel, NULL, ctrl->wcnt, ENABLE); #endif } #if (USE_I2C_TX_DMA1) it_Bits |= I2C_IT_TCI; // always for DMA transfers #else it_Bits |= I2C_IT_TXI; #endif } else { // read or write-restart-read
if (!ctrl->wcnt) // read only { if (ctrl->rcnt > UINT8_MAX) { I2C_TransferHandling(i2c, slave, UINT8_MAX, I2C_Reload_Mode, I2C_Generate_Start_Read); #if (USE_I2C_RX_DMA1) DMA_Reload(ctrl->rx_DMAChannel, &ctrl->ptr[1], UINT8_MAX, ENABLE); #else it_Bits |= I2C_IT_TCI; // for TCR flag #endif } else { I2C_TransferHandling(i2c, slave, ctrl->rcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Read); #if (USE_I2C_RX_DMA1) DMA_Reload(ctrl->rx_DMAChannel, &ctrl->ptr[1], ctrl->rcnt, ENABLE); #endif } #if (USE_I2C_RX_DMA1) it_Bits |= I2C_IT_TCI; // always for DMA transfers #else it_Bits |= I2C_IT_RXI; #endif } else { // write-restart-read I2C_TransferHandling(i2c, slave, ctrl->wcnt, I2C_SoftEnd_Mode, I2C_Generate_Start_Write); #if (USE_I2C_TX_DMA1) DMA_Reload(ctrl->tx_DMAChannel, &ctrl->ptr[1], ctrl->wcnt, ENABLE); it_Bits |= I2C_IT_TCI; #else it_Bits |= I2C_IT_TXI; #endif } } I2C_ITConfig(i2c, it_Bits, ENABLE); ctrl->guard = 1; return cnt; } // ----------------------------------------------------------------------------- // // Help function to init the 'tp' for I2C_Loop() // static int32_t i2c_init_tp(uint8_t port, int32_t cnt) { cnt = 2 * (cnt > 0 ? cnt : 2) * BITS_ONE_BYTE; return I2C_SBTimeoutInit(port, cnt); // 2 * cnt bytes to wait } // ----------------------------------------------------------------------------- // // Internal loop function common for blocking R/W operations // // Returns: // >0: number of bytes transferred // -1: an error occurred // -3: if cancelled by a call back // else passes the result from the caller function through // static int32_t I2C_Loop(uint8_t port, int32_t cnt, I2C_CallBackType proc) { if (cnt > I2C_RETURN_NOTHING) { int32_t cont, tp = i2c_init_tp(port, cnt); I2C_Control_Type *ctrl = &control[port];
do { if (ctrl->guard) // initially set to 1 by I2C_Start_It(), thus { // the first assignment to tp always takes place. /* The .guard flag set by any interrupt occurred signals some progress in the transaction. If no flag has been detected after a certain period of time, an error is assumed. */ ctrl->guard = 0;
// Restart the timeout tp = i2c_init_tp(port, ctrl->icnt); } cont = !I2C_SBTimeoutExpired(&tp) || ctrl->errorflag;
} while (!ctrl->complete && !ctrl->errorflag && cont && (proc == NULL || (cont = proc(port, ctrl->icnt)))); if (!cont) // cancelled by the callback/timeout { I2C_CANCEL_HANDLER(port); cnt = I2C_RETURN_CANCELLED; } else { cnt = (ctrl->errorflag ? I2C_RETURN_ERROR : (cnt - ctrl->icnt)); if (I2C_IS_BUSY(port)) // still? I2C_Recover(port); } } return cnt; } // ----------------------------------------------------------------------------- // // Starts the writing transaction on the I2C bus. For more, see comments to // the function "WriteC_I2C" below but the callback here has another effect: // if provided, it will be called upon the transaction has finished. // The parameter of the callback will contain the result code I2C_RETURN_*. // // NOTE: the callback is invoked out of the interrupt handler! Don't do // any actions in the callback that could make the system stall. // // Returns: // >0: number of bytes to be transferred (wcnt) // 0: no transfer will be executed at all // -2: I2C bus busy, try later. // int32_t Write_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, I2C_CallBackType proc) { if (!I2C_IS_BUSY(port)) { if (!wcnt) return I2C_RETURN_NOTHING; else { I2C_Control_Type *ctrl = &control[port]; INIT_I2C_FLAGS(ctrl); ctrl->ptr = buf; // REMEMBER: buf[0] contains the slave address! ctrl->rcnt = 0; // no reading ctrl->icnt = // total bytes counter ctrl->wcnt = wcnt; // data bytes to write (excluding the slave address!) ctrl->cb = proc; return I2C_Start_It(port); } } // else I2C Bus is BUSY return I2C_RETURN_BUSY; } // ----------------------------------------------------------------------------- // // Writes "wcnt" bytes to an I2C device WAITING until the transaction is // complete (blocking write) calling an user provided callback "proc" out // of the waiting loop. This callback defined as // // typedef uint8_t (*I2C_CallBackType)(uin8_t port, int16_t left), // // provides in "left" the number of bytes yet to process and should return // >0 (TRUE) to continue the transaction or 0 (FALSE) to cancel it. // // A 7-bit Device ID (I2C address of the device) to provide in the first // byte of the I/O buffer (buf[0]), the Data start at buf[1]. // // Returns: // >0: number of bytes really transferred // 0: no transfer executed at all // -1: an error occurred, call I2C_Result to know more. // -2: I2C bus busy, try later. // -3: cancelled by a callback // int32_t WriteC_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, I2C_CallBackType proc) { return I2C_Loop(port, Write_I2C(port, buf, wcnt, NULL), proc); } // ----------------------------------------------------------------------------- // // As "WriteC_I2C" above but w/o callback // int32_t WriteW_I2C(uint8_t port, uint8_t *buf, int32_t wcnt) { return I2C_Loop(port, Write_I2C(port, buf, wcnt, NULL), NULL); } // ----------------------------------------------------------------------------- // // Starts the reading transaction on the I2C bus. For more, see comments to // the function "ReadC_I2C" below but the callback here has another effect: // if provided, it will be called upon the transaction has finished. // The parameter of the callback will contain the result code I2C_RETURN_*. // // NOTE: the callback is invoked out of the interrupt handler! Don't do // any actions in the callback that could make the system stall. // // Returns: // >0: number of bytes to be transferred (wcnt + rcnt) // 0: no transfer will be executed at all // -2: I2C bus busy, try later. // int32_t Read_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt, I2C_CallBackType proc) { if (!I2C_IS_BUSY(port)) { if (!rcnt) return I2C_RETURN_NOTHING; else { I2C_Control_Type *ctrl = &control[port]; INIT_I2C_FLAGS(ctrl); ctrl->ptr = buf; ctrl->rcnt = rcnt; ctrl->wcnt = wcnt; ctrl->cb = proc; ctrl->icnt = ctrl->wcnt + ctrl->rcnt; // to include the WRITE operations return I2C_Start_It(port); } } // else I2C Bus is BUSY return I2C_RETURN_BUSY; } // ----------------------------------------------------------------------------- // // Reads "rcnt" bytes from an I2C device out WAITING until the transaction is // complete (blocking read) calling an user provided callback "proc" out of // the WAITING loop. This callback defined as // // typedef uint8_t (*I2C_CallBackType)(uin8_t port, int16_t left), // // provides in "left" the number of bytes yet to process and should return // >0 to continue the transaction or 0 to cancel it. // // A 7-bit Device ID (I2C address of the device) to provide in the first // byte of the I/O buffer (buf[0]). // // The function implements a START-ADDR-[WRITE-RESTART-ADDR]-READ-STOP // sequence. The [WRITE-RESTART-ADDR] is inserted only if (wcnt > 0): a // WRITE preamble of "wcnt" bytes with RESTART issued afterwards precedes // the READ transaction. This feature is usefull for random read access // to EEPROMs or other devices with the similar access protocol. // // The bytes read are stored back in the buffer starting at buf[wcnt+1], // while the bytes to write, if any, are provided in buf[1] to buf[wcnt]. // // FYI: For EEPROMs with >256 bytes capacity the first address byte // carries MSB and the second - LSB! // // Returns: // >0: number of bytes have been really transferred (wcnt + rcnt mostly) // 0: no transfer executed at all // -1: an error occurred, call I2C_Result to know more. // -2: I2C bus busy, try later. // -3: cancelled by callback // int32_t ReadC_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt, I2C_CallBackType proc) { return I2C_Loop(port, Read_I2C(port, buf, wcnt, rcnt, NULL), proc); } // ----------------------------------------------------------------------------- // // As "ReadC_I2C" above but w/o call back. // int32_t ReadW_I2C(uint8_t port, uint8_t *buf, int32_t wcnt, int32_t rcnt) { return I2C_Loop(port, Read_I2C(port, buf, wcnt, rcnt, NULL), NULL); } // ----------------------------------------------------------------------------- // // Returns TRUE if I2C port 'port' is busy // uint8_t Busy_I2C(uint8_t port) { return I2C_BUSY(control[port].i2c); } // ----------------------------------------------------------------------------- // // Changes the speed on I2C bus. // // Input: a value in Hz // 100000 would set 100kHz. // // ATTENTION: no range check! // // Returns: // >0: OK // -2: I2C Bus is busy, no changes on-the-fly allowed! // int32_t Speed_I2C(uint8_t port, uint32_t rate) { if (!I2C_IS_BUSY(port)) { if (!rate) rate = I2C_CLOCKSPEED; control[port].rate = rate; control[port].icnt = 0; ReInit_I2C(port, 1); return control[port].rate/1000;
} else return I2C_RETURN_BUSY; } // ----------------------------------------------------------------------------- // // Provide: // the desired I2C bus rate in Hz (below/equal 400000). // If rate == 0, the value predefined in stm32f0xx_iic.h is used. // void Init_I2C(uint8_t port, uint32_t rate) { uint8_t cnt = 3; DWT_Init(); #if (USE_I2C_TX_DMA1 || USE_I2C_RX_DMA1) RCC_Configuration (DMA1Clocks, sizeof(DMA1Clocks)); #endif while (Speed_I2C(port, rate) == I2C_RETURN_BUSY && cnt--) { I2C_Recover(port); } } //------------------ EOF -------------------------------------------------------
Здесь куча условных трансляций в зависимости от того, хочу я DMA, или только по прерываниям. Используется симуляция DWT счетчика для подсчета коротких задержек на случай stall состояний слейвов, и прочая кухня, которая выросла за многие годы. В общем, без бутылки и меня рядом не разберешься. Поэтому код как таковой вряд ли будет в помощь. Я утверждаю, что задача решаема, т.к. у меня все работает, но поможет ли Вам приведенный код, и не забанит ли меня модерация за "многа буков", я не знаю.
Сообщение отредактировал KnightIgor - Nov 9 2015, 11:31
|
|
|
|
Сообщений в этой теме
p_kav STM32F030 проблема с I2C Nov 7 2015, 23:52 etoja Цитата(p_kav @ Nov 8 2015, 03:52) Использ... Nov 8 2015, 17:16 p_kav Цитата(etoja @ Nov 8 2015, 22:16) I2C и 1... Nov 8 2015, 17:36 Alechek Цитата(p_kav @ Nov 8 2015, 22:36) Всё вер... Nov 9 2015, 06:45  scifi Цитата(Alechek @ Nov 9 2015, 09:45) Так, ... Nov 9 2015, 07:08   adnega Цитата(scifi @ Nov 9 2015, 10:08) +1. Кст... Nov 9 2015, 08:04 p_kav Alechek
Что-то не могу найти ни одной статьи о так... Nov 9 2015, 07:04 Alechek Цитата(p_kav @ Nov 9 2015, 12:04) Что-то ... Nov 9 2015, 08:05 adnega По теме: флаги под отладчиком могут меняться, если... Nov 9 2015, 08:07 scifi Цитата(adnega @ Nov 9 2015, 11:07) По тем... Nov 9 2015, 08:15 p_kav scifi
Я пробовал вставлять задержки, не помогает.
... Nov 9 2015, 08:28 scifi Цитата(p_kav @ Nov 9 2015, 11:28) мне каж... Nov 9 2015, 08:41 AVI-crak Цитата(p_kav @ Nov 9 2015, 15:28) К тому ... Nov 9 2015, 09:03 p_kav Цитата(scifi @ Nov 9 2015, 13:41) А ещё п... Nov 9 2015, 08:47 KnightIgor Цитата(p_kav @ Nov 9 2015, 09:47) Вот, на... Nov 9 2015, 10:21 vet Пытался работать с I2C-периферией и на STM32F, и н... Nov 9 2015, 09:10 p_kav Цитата(AVI-crak @ Nov 9 2015, 14:03)... Nov 9 2015, 09:13 adnega Цитата(p_kav @ Nov 9 2015, 12:13) остаетс... Nov 9 2015, 09:22 p_kav Цитата(KnightIgor @ Nov 9 2015, 15:21) ТС... Nov 9 2015, 10:33 KnightIgor Цитата(p_kav @ Nov 9 2015, 11:33) А код о... Nov 9 2015, 10:50
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|