STM32F103使用硬件i2c作为从机模式

https://blog.csdn.net/c1033177205/article/details/79160803

一、简单说明

本例子参考了ST官方历程,官方历程的链接如下

http://www.st.com/content/st_com/zh/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32094.html

关于i2c的协议这里就不做描述了

关于STM32 i2c的模式可以在中文数据手册中查看

手册中已经描述,该模块默认工作在从模式,要想变为主模式,主要生产一个起始条件。(主模式的代码可以参考野火开发板的硬件i2c历程,本例子中也是使用野火开发板硬件i2c作为主机的)
二、i2c从机的配置

        I2C_DeInit(I2C1);
        /* I2C1 configuration ------------------------------------------------------*/
        I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//模式
        I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
        I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;//这个就是作为从机的地址,一定要配置正确
        I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
        I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位的地址
        I2C_InitStructure.I2C_ClockSpeed = ClockSpeed;
        I2C_Init(I2C1, &I2C_InitStructure);

上面配置注意的就是从机地址,这就是主机要查询的从机地址
三、i2c从机中断的配置

        /* Configure and enable I2Cx event interrupt -------------------------------*/
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
     
        /* Configure and enable I2C1 error interrupt -------------------------------*/
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
        NVIC_Init(&NVIC_InitStructure);

标准库中的i2c一共有两个中断
一个是事件中断(EV_IRQ)和一个错误中断(ER_IRQ)
EV_IRQ的中断只要响应EV1 EV2 EV4 之类的,后面会说明
ER_IRQ的中断只要响应没有应答和起始和停止条件出错等
四、使能中断

    /* Enable I2C1 event and buffer interrupts */
     
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
     
    /* Enable I2C1 Error interrupts */
     
    I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);

使能了I2C1的这三个中断,每个中断的作用下面说明
五、中断处理函数

    void I2C1_EV_IRQHandler(void)     
     
    //事件中断处理函数
     
    {
     
     switch (I2C_GetLastEvent(I2C1))
     
     //获取i2c1的中断事件
     
     {
     
      /* Slave Transmitter ---------------------------------------------------*/  
     
     case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
     
      /* 这个和下面那个都是从发送模式下发送数据的,具体两个的区别我也不是很明白,感觉就是移位寄存器空与非  空的区别,准备好数据发送吧 */
     
      I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);
     
       break;
     
     case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:             /* EV3 */    
     
      /* Transmit I2C1 data */
     
      I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);
     
       break;
     
     /* Slave Receiver ------------------------------------------------------*/
     
     case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:     /* EV1 */
     
      /* 地址匹配中断,不管从发送和接收都要匹配地址,如下图244、243发送地址之后都会响应EV1 */
     
      break;
     
     case I2C_EVENT_SLAVE_BYTE_RECEIVED:                /* EV2 */
     
      /* Store I2C1 received data */
     
      /* 这个中断就是响应EV2中断,如下图244,每次主机发送完一个数据就会产生一个EV2的中断 */
     
      I2C1_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1);
     
      /* 把接收到的中断填充到数组中 */
     
      /* 注意:地址不会填充进来的 */
     
       break;
     
     case I2C_EVENT_SLAVE_STOP_DETECTED:                /* EV4 */
     
       /* Clear I2C1 STOPF flag */
     
      /* 这个就是正常停止的时候产生的一个停止信号 */
     
      I2C_Cmd(I2C1, ENABLE);
     
      /* 我也不清楚这个为什么要这样,如果接收完一串数据之后,不响应主机的情况可以 关闭i2c,然后在处理完数据后再  从新配置i2c,记得是从新配置 */
     
      Rx_Idx=0;
     
      i2c_event = EVENT_OPCOD_NOTYET_READ;
     
      break;
     
     default:
     
      break;    
     
      }
     
    }
     
    void I2C1_ER_IRQHandler(void)
     
    {
     
     /* Check on I2C1 AF flag and clear it */
     
     if (I2C_GetITStatus(I2C1, I2C_IT_AF))
     
     {
     
      /* 这个就是图243中最后那个没有应答的中断,也就是发送了一串数据后的中断,可以做清零工作 */  
     
      I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
     
      Tx_Idx = 0;
     
      i2c_event = EVENT_OPCOD_NOTYET_READ;
     
     }
     
     /* Check on I2C1 AF flag and clear it */
     
     if (I2C_GetITStatus(I2C1, I2C_IT_BERR))   //这个就是起始和停止条件出错了
     
     {
     
      I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
     
      }
     
     }


注意:i2c中断中不要printf打印信息,

否则会出错


主要是参考了官方的历程,然后自己再调整一下逻辑即可使用。


最后的效果感觉还是挺稳定的,这篇文章主要是给大家抛砖引玉的作用,也记录一下自己调试几天的成果。
ST官网的历程还是挺多的,没遇到的知识可以去官网找找。

---------------------
作者:酱油师兄
来源:CSDN
原文:https://blog.csdn.net/c1033177205/article/details/79160803
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/sunjing_/article/details/84140585