i2c 传输,stm32f103 作为 Master,STOP 条件生成问题

Posted

技术标签:

【中文标题】i2c 传输,stm32f103 作为 Master,STOP 条件生成问题【英文标题】:i2c transmission, stm32f103 as a Master, problem with STOP condition generation 【发布时间】:2019-10-17 15:55:56 【问题描述】:

我正在使用 KEIL。这是我的 I2C 设置代码。我正在尝试使用中断进行设置。实际上,一切都像这样正常工作。但我唯一的问题是我做了一件奇怪的事情才能让它发挥作用。

// Variables passed to I2C1_EV_IRQHandler 
static volatile uint8_t counter=0;
static uint8_t slave_address=0, *data_buff,buff_num_bytes=0;

//I2c event and error interrupts setup
void I2C_NVIC_setup(void)
    NVIC->ISER[0] |=NVIC_ISER_SETENA_31; //I2C_IRQ_EV is at 31 position
    NVIC->ISER[1] |=NVIC_ISER_SETENA_0; //I2C_IRQ_ER is at 32 position
    NVIC->IP[31] &= ~0xF0;
    NVIC->IP[31]= (uint8_t)(0x5<<4);    //Event has lower priority
    NVIC->IP[32] &= ~0xF0;
    NVIC->IP[32]= (uint8_t)(0x4<<4);    //Error has higher priority
    __enable_irq();         //ENABLE interrupts


//GPIO settings for PB6 and PB7
void I2C_GPIO_init(void)
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //PORTB clock enable

    GPIOB->CRL |= GPIO_CRL_CNF6 |GPIO_CRL_CNF7;         //PB6 and PB7- open drain
    uint32_t buff=GPIOB->CRL;
    buff &= ~GPIO_CRL_MODE6 & ~GPIO_CRL_MODE7;  
    buff |= GPIO_CRL_MODE6_0 | GPIO_CRL_MODE7_0 ;   //2MHz maximum output speed
    GPIOB->CRL=buff; 
    //Dont care about ODR register


// Initialization of I2C
bool I2C_init(void)
I2C_GPIO_init();
    I2C_NVIC_setup();
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 0x14; //APB1 frequency is 20MHz

    I2C1->CCR |=  I2C_CCR_FS; //Fast mode
    I2C1->CCR |= I2C_CCR_DUTY;  //Duty cycle 16/9(for fast mode)

    I2C1->CCR &= ~I2C_CCR_CCR;
    I2C1->CCR |=    0x04;           //Thigh=1800ns Tlow=3200ns -200KHz frequency

    I2C1->TRISE &= ~I2C_TRISE_TRISE;
    I2C1->TRISE |= 0x07;    //300ns(max Trise)/50ns(Tclck) +1

    I2C1->CR1 |=    I2C_CR1_PE; //Enable peripheral

    I2C1->CR2 |= I2C_CR2_ITBUFEN; //Enable interupts of TxE and RxNE
    I2C1->CR2 |= I2C_CR2_ITEVTEN; //Enable event interupts
    I2C1->CR2 |= I2C_CR2_ITERREN; //Enable error interupts

//Master transmitter
bool I2C_transmit(uint8_t b7_address, uint8_t* data, uint8_t num_bytes)
    counter=num_bytes;
    buff_num_bytes=num_bytes;
    data_buff=data;
    slave_address=b7_address<<1 ;//To enter transmiter mode LSB=0

    I2C1->CR1 |= I2C_CR1_START; //Start condition
    while(counter || (I2C1->SR2 & I2C_SR2_MSL) || (I2C1->CR1 & I2C_CR1_STOP)) //Stop bit is checked because of the problem I have
    
   
//IRQ I2C1 event handler
void I2C1_EV_IRQHandler(void) //Handle the interrupt of I2C1
    uint8_t buff_sr1=I2C1->SR1;
    if(buff_sr1 & I2C_SR1_SB) //SB bit is set(unsetting by read SR1 and writing adress to DR) 
        I2C1->DR=slave_address; 
    else if(buff_sr1 & I2C_SR1_ADDR) //ADDR bit is set(unsetting by read SR1 and read SR2)            
        (void)I2C1->SR2; 
    
    if ((buff_sr1 & I2C_SR1_TXE)&& !(counter==0))  //Checking TxE( clearing by writting to DR)
        I2C1->DR=data_buff[buff_num_bytes-counter];
        counter--;
     else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)) //Checking stop 
      //condition(TxE=1,BTF=1,counter=0)
        (void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
        I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition
    


如果你看看最后几行:

     else if((buff_sr1 & I2C_SR1_TXE)&&(buff_sr1 & I2C_SR1_BTF)) //Checking stop 
      //condition(TxE=1,BTF=1,counter=0)
        (void)I2C1->SR1; //Dont know why, but it works(just read some I2C1 register)
        I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition
    

这里当我调试时(这段代码,没有行(void)I2C1->SR1;),当我尝试发送数据时,第一个包将被发送而没有任何问题。最后它会产生停止条件(MSL 位将被清除,并且线路被释放),但 STOP 位将保持设置(这就是我将 STOP 位检查放入 I2C_transmit while 循环的原因)。如果我在调试窗口中手动清除 STOP 位,它会继续,但在下一个发送周期它不会产生正确的启动(SB 将被设置,但 MSL 位将保持在复位状态)。

在调试时,如果我在这一行之前放置断点:

(请记住,此时我还没有找到解决这个问题的方法,所以一切都一样,只是没有这条线-(void)I2C1->SR1;)

I2C1->CR1 |= I2C_CR1_STOP;  //Generate stop condition

停在那里,检查寄存器,然后继续,一切正常。我在stackexchage 上发现:

尤其是关于读取寄存器的部分可以给你一个 调试地狱,因为调试器的读取将触发状态 机器就像阅读你的代码一样,所以一旦你停下来 检查寄存器(有时调试器会做这一切 自动)读取发生,外围设备将执行某些操作 再次。

很明显,当我触发 STOP 条件时,某些要求不满足。所以我决定检查是否是这种情况,似乎这是一个解决方案。添加读取 I2C 的任何寄存器((void)I2C1->SR1;//或任何其他 I2C 寄存器),解决了问题。必须有合乎逻辑的解释,但我找不到。我非常仔细地研究了reference manual,结果是这段代码,但我没有找到任何可以解释我的东西。

这是Reference manual关于Master传输的表格,供您参考:

你能说出我没有满足什么条件,所以我的 STOP 没有正确处理。

【问题讨论】:

1 @P__J__ 你是对的。我全部更改为 CMSIS 定义 FWIW,我在 F1 系列勘误表中没有看到任何提及 st.com/resource/en/errata_sheet/cd00190234.pdf 提到了 BERR 和错位 STOP,但我认为它不适用于您的情况。 @RichardatImageCraft 谢谢你的回答。我还检查了勘误表,是的,那里没有提到这个问题。 我没有将代码编写为中断驱动的状态机,而是编写为直线轮询代码。在这种情况下,我知道我的代码在生成 STOP 条件之前无需读取 SR 即可工作,F1xx(您的情况)、L0xx、F2xx、F4xx 和 F7xx 就是这种情况。不知道它会如何产生影响。 【参考方案1】:

好的,我刚刚将我的测试程序转换为使用基于此代码的中断驱动传输。我的测试是在 STM32F411RE 上进行的,它具有与 F103 类似的 I2C 实现(F0xx、F3xx 和 F7xx 等其他系列使用另一种 I2C 实现)。无论如何,至少我可以确认没有“(void)I2C1->SR1;”在发送中断处理程序中,它不起作用。仅在添加该行时才有效。

测试使用 I2C 扩展芯片闪烁 8x8 LED 矩阵,因此有 64*2+ I2C 传输调用来遍历 LED 矩阵。

由于 F411RE 比 F103 更新,我猜这是一个未记录的要求!

现在至少在我的测试中,传输函数中不需要“(I2C1->CR1 & I2C_CR1_STOP)”。

【讨论】:

感谢您抽出时间查看 F4。问题是我认为,我们在这里遗漏了一些东西,我不确定是什么。它不仅仅是读取这个使一切正常的寄存器,它准备这个寄存器(或者,正如我在问题中所说,在切换停止位之前,I2C 中的一些其他寄存器),做“某事”,这使得正确的停止条件成为可能一代。那个“东西”就是我们所缺少的。附言(I2C1->CR1 & I2C_CR1_STOP) 添加只是为了方便调试,因为只是 STOP 位导致了问题。 @Vaso,是的,如果您找到根本原因,请更新此线程。参考手册或数据表中没有任何明显的内容可以说明这一点,但是您绝对正确地认为需要幻像读取。很奇怪。【参考方案2】:
I2C1->CR1|=0x0200;  // stop (bit 9 set for stop)
while(I2C1->SR2&0x0002);  // Bus busy

这两条指令用于设置停止位和清除总线忙标志。

【讨论】:

以上是关于i2c 传输,stm32f103 作为 Master,STOP 条件生成问题的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103VE基于STM32CubeMX 配置I2C驱动0.96/1.3 OLED显示

STM32F103VET6基于STM32CubeMX 配置硬件I2C驱动SH1106 OLED屏幕

Arduino STM32F103C8T6 驱动ssd1306 I2C oled

STM32F103VE基于标准库下DHT11数据I2C oled显示

STM32F103学习笔记(10)——I2C多路复用器TCA9548A使用

STM32F103学习笔记(10)——I2C多路复用器TCA9548A使用