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);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章