調試情況1
現象:主I2C發送數據而沒有收到應答,則下一次不能正常發送數據
背景:主I2C每次應該都能正常發送數據
硬件:野火STM32-MINI,1主0從,SCL和SDA直接上拉
軟件:按鍵觸發中斷,主I2C發送一次數據,中斷優先級,按鍵最低,I2C最高,且主I2C中有TIMEOUT計時
void PB_K2_IRQ_HANDLER(void) { if(EXTI_GetITStatus(PB_K2_EXTILINE) != RESET) { EXTI_ClearITPendingBit(PB_K2_EXTILINE); mini_i2c_write(0x22, 0x47); LED_D5_TOGGLE; // printf("Push button K2 clicked!\r\n"); } }
#define PB_PREPRIO 3 #define PB_SUBPRIO 3
#define SLAVER_I2C_PRIPRIO 0 #define SLAVER_I2C_SUBPRIO 0
優先級分組均為2
void mini_uart_config(void) { // structure define USART_InitTypeDef usart_struct; GPIO_InitTypeDef gpio_struct; NVIC_InitTypeDef nvic_struct; // clock enable DEBUG_UART_RCC_CMD(DEBUG_UART_RCC, ENABLE); GPIO_RCC_CMD(DEBUG_UART_GPIO_RCC, ENABLE); // GPIO initialization gpio_struct.GPIO_Mode = GPIO_Mode_AF_PP; gpio_struct.GPIO_Speed = GPIO_Speed_50MHz; gpio_struct.GPIO_Pin = DEBUG_UART_TX; GPIO_Init(DEBUG_UART_GPIO, &gpio_struct); gpio_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio_struct.GPIO_Pin = DEBUG_UART_RX; GPIO_Init(DEBUG_UART_GPIO, &gpio_struct); // NVIC initialization NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); nvic_struct.NVIC_IRQChannel = DEBUG_UART_IRQ; nvic_struct.NVIC_IRQChannelPreemptionPriority = DEBUG_UART_PRIPRIO; nvic_struct.NVIC_IRQChannelSubPriority = DEBUG_UART_SUBPRIO; nvic_struct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic_struct); // USART initialization usart_struct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; usart_struct.USART_Parity = USART_Parity_No; usart_struct.USART_StopBits = USART_StopBits_1; usart_struct.USART_WordLength = USART_WordLength_8b; usart_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; usart_struct.USART_BaudRate = DEBUG_UART_BAUDRATE; USART_Init(DEBUG_UART, &usart_struct); USART_Cmd(DEBUG_UART, ENABLE); USART_ITConfig(DEBUG_UART, USART_IT_RXNE, ENABLE); }
void mini_i2c_write(uint8_t reg_addr, uint8_t wdata) { uint32_t timeout; I2C_GenerateSTART(MASTER_I2C, ENABLE); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_MODE_SELECT) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } } // device address I2C_Send7bitAddress(MASTER_I2C, SLAVER_I2C_OWN_ADDR, I2C_Direction_Transmitter); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } } I2C_SendData(MASTER_I2C, reg_addr); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } } I2C_SendData(MASTER_I2C, wdata); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } } I2C_GenerateSTOP(MASTER_I2C, ENABLE); }
問題所在:主I2C在未收到ACK應答,會產生應答錯誤,SR1的AF位置1,在TIMEOUT時間后,上面的用戶代碼會調用I2C_GenerateSTOP(MASTER_I2C, ENABLE)函數,CR1的STOP為會被置位,導致出錯
調試代碼,用串口打印狀態寄存器及控制寄存器的值
void mini_i2c_write(uint8_t reg_addr, uint8_t wdata) { if((MASTER_I2C->SR1 != 0) || (MASTER_I2C->SR2 != 0)) { printf("Master I2C SR1 is %x and SR2 is %x\r\n", MASTER_I2C->SR1, MASTER_I2C->SR2); printf("Master I2C CR1 is %x and CR2 is %x\r\n", MASTER_I2C->CR1, MASTER_I2C->CR2); // mini_i2c_config(); MASTER_I2C->CR1 ^= 0x0200; printf("Master I2C SR1 is %x and SR2 is %x\r\n", MASTER_I2C->SR1, MASTER_I2C->SR2); printf("Master I2C CR1 is %x and CR2 is %x\r\n", MASTER_I2C->CR1, MASTER_I2C->CR2); } uint32_t timeout; I2C_GenerateSTART(MASTER_I2C, ENABLE); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_MODE_SELECT) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } } // device address I2C_Send7bitAddress(MASTER_I2C, SLAVER_I2C_OWN_ADDR, I2C_Direction_Transmitter); timeout = I2C_TIMEOUT; while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == RESET) { if(timeout-- == 0) { I2C_GenerateSTOP(MASTER_I2C, ENABLE); break; } }
經驗證,重新初始化I2C或復位CR1的STOP可以排除對下次發送數據的干擾,串口打印消息如下
// Using mini_i2c_config()
Program started! Master I2C SR1 is 400 and SR2 is 0 Master I2C CR1 is 601 and CR2 is 24 Master I2C SR1 is 0 and SR2 is 0 Master I2C CR1 is 401 and CR2 is 24
// Using MASTER_I2C->CR1 ^= 0x0200 Program started! Master I2C SR1 is 400 and SR2 is 0 Master I2C CR1 is 601 and CR2 is 24 Master I2C SR1 is 400 and SR2 is 0 Master I2C CR1 is 401 and CR2 is 24
解決方案:使能主I2C錯誤中斷,在中斷中重新初始化I2C,並設置一個變量使主I2C發送函數提前終止
nvic_struct.NVIC_IRQChannel = MASTER_I2C_ER_IRQ;
NVIC_Init(&nvic_struct);
I2C_ITConfig(MASTER_I2C, I2C_IT_ERR, ENABLE);
void MASTER_I2C_ER_IRQ_HANDLER(void) { if(I2C_GetITStatus(MASTER_I2C, I2C_IT_AF) != RESET) { I2C_ClearITPendingBit(MASTER_I2C, I2C_IT_AF); mini_i2c_config(); master_i2c_af = 1; } }
void mini_i2c_write(uint8_t reg_addr, uint8_t wdata) { master_i2c_af = 0; I2C_GenerateSTART(MASTER_I2C, ENABLE); while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_MODE_SELECT) == RESET); // device address I2C_Send7bitAddress(MASTER_I2C, SLAVER_I2C_OWN_ADDR, I2C_Direction_Transmitter); while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == RESET) { if(master_i2c_af) { master_i2c_af = 0; return; // maybe there is a solution here } } I2C_SendData(MASTER_I2C, reg_addr); while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == RESET); I2C_SendData(MASTER_I2C, wdata); while(I2C_CheckEvent(MASTER_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == RESET); I2C_GenerateSTOP(MASTER_I2C, ENABLE); }