STM32硬件I2C调试
调试情况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); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
2016-11-10 STM32 HAL固件库编程的文件构架