STM32 I2C 源代码,应用STM32的标准库,非中断方式,已测试,通信稳定,给初学STM32 I2C的朋友

2019-12-11 18:39发布

最近刚买了块金牛的开发板,用的STM32F107VC的MPU,在网上搜了半天找了些资料,但测试过程中发现通信不太稳定,在自己的板子上调试了几天,修改了几个地方,现在通过i2c读写,24CL02正常,且稳定。
下面整理些在开发中需要注意的问题,供初学者参考,并附上软件包。供大家研究。 代码是参照网上一位朋友的,在细节部分做了修改,在此表示感谢。

STM32 I2C开发注意问题,非中断方式
1.GPIO端口配置阶段,需要首先使能I2C时钟,然后再配置端口工作方式,否则无效,配置SDA、SCL端口为开漏输出模式,注意与ARM不同的是即使使能IO的特殊功能,仍然需要手动设置GPIO的工作方式;
2.对于I2C器件的读写,由于时序的原因,有的时候读写状态寄存器时会有问题,不像51那样可以自己控制时序与数据,操作起来比较方便,在查询和清除状态寄存器时同样要注意时序问题,否则可能不能正确捕捉到。比如在查询i2c总线是否被占用时,发送完地址数据需要延时一点时间,否则不能捕捉到ADDR置位信号。具体参看程序代码;
3.模拟I2C读写时,如果一次读取多个字节,总是传输不稳定,经过反复测试,将多字节操作全部由单字节操作实现,经实践发现通信比较稳定;
4.STM的数据手册写得确实不怎么样,对于在i2c通信过程中,产生的状态信息说明很不清楚,不要按照数据手册上的去研究通信过程中各个状态寄存器的单个或组合值,直接看它提供的库文件,它已经把各个状态时的状态寄存器组合值定义好了,拿过来直接用吧。
5.切记,在通信过程中,不是每一个状态仅置位一个状态位,而是多个状态位,否则你怎么搞都搞不通的。

以上是我在开发过程中的一点心得,希望对大家有帮助,也希望有心的人可以对驱动加以改进,大家共同研究。

另外说明:
代码中除了i2c,还有lcd的代码,lcd的代码是开发板带的,如果不用直接注释掉就可以了。
用stm32f107最好用mdk3.8以上的版本,另外如果要用到这个芯片的一些外围接口,最好安装RL-ARM库,因为现在对stm32的支持比较少,开发板上的例程很多都是用的stm的RL-ARM中的库文件。
STM32 的I2C应用-非中断方式ourdev_491300.rar(文件大小:398K) (原文件名:I2C.rar)
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
59条回答
wyz.0413
1楼-- · 2019-12-17 05:45
作为参考
nemo0507
2楼-- · 2019-12-17 05:47
你好,能否抽空帮我看下这个程序,我在两块STM32板子间做I2C通信,主发从收,因为没有两个主机两个仿真器 ,我就是先将主机的程序固化到主机中,然后在从机上调试运行,结果是跑了N多次,只有两次收到数据,都是正确的。其他时候就一直停在
1. while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));  //Bus busy flag 0x00020000 -- SR2 BUSY位
或者停在  /*接收地址*/
2. I2C_Send7bitAddress(I2C1,0x60,I2C_Direction_Receiver);  //从机地址 传输方向
     while(!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED));/*EV1 ADDR*/

你能否帮我看下,到底是哪里出错了。。。俺琢磨了好久,都是按照STM32手册写的,怎么就错了呢。。。。。。


////////////////////////////////////主STM32板  设置地址为0x90////////////////////////////////
#define N 8

int main(void)
{
  I2C_Configuration();
  I2C_Master_Send();
}

void I2C_Configuration(void)
{
        I2C_InitTypeDef  I2C_InitStructure;
        GPIO_InitTypeDef  GPIO_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);          //I2C时钟
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //I2C对应GPIO管脚时钟
   

        /* PB6,7 I2C1 PB10.11 I2C2  SCL and SDA */
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //设置管脚为复用功能开漏输出
        GPIO_Init(GPIOB, &GPIO_InitStructure);          //I2C接口使用的GPIO管脚初始化
       
            I2C_DeInit(I2C1); //0x40005400恢复到默认值
            I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;         //设置I2C接口模式
            I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //设置I2C接口的高低电平周期
            I2C_InitStructure.I2C_OwnAddress1 = 0x90;          //设置I2C接口的主机地址
            I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;        //设置是否开启ACK响应         
            I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
            I2C_InitStructure.I2C_ClockSpeed = 100000;        //100K速度
   
        I2C_Cmd(I2C1, ENABLE);                            //使能I2C接口
        I2C_Init(I2C1, &I2C_InitStructure);               //I2C接口初始化
        /*允许1字节1应答模式*/
        I2C_AcknowledgeConfig(I2C1, ENABLE);              //使能I2C接口响应--应答功能

}

void I2C_Master_Send(void)
{
        u8 i,data_send[N],data_receive[N];
        for(i=0;i<N;i++)
        {
                data_send[i] = i+9;
                data_receive[i] = 0;       
        }

        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));         //Bus busy flag 0x00020000        -- SR2 BUSY位
                                                               
        /*起始位*/
        I2C_GenerateSTART(I2C1, ENABLE);                           //CR1_START_Set 0x0100 -- CR1 START
        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  /* EV5 0x00030001 BUSY, MSL and SB flag */

        /*写器件地址*/
        I2C_Send7bitAddress(I2C1,0x60,I2C_Direction_Transmitter);  //从机地址 传输方向
        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); /* EV6.8 BUSY, MSL, ADDR, TXE and TRA flags */

        /*写地址值*/
//        I2C_SendData(I2C1, 0);
//        while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); /* TRA, BUSY, MSL, TXE and BTF flags */

        for(i=0;i<N;i++)  
        {
          I2C_SendData(I2C1,data_send[i]);
          while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));        /* EV8 TRA, BUSY, MSL, TXE and BTF flags */
        }
       
        /*停止位*/
        I2C_GenerateSTOP(I2C1, ENABLE);
}

////////////////////////////////////从STM32板  设置地址为0x60////////////////////////////////
#define N 8

int main(void)
{
  I2C_Configuration();
  I2C_Slave_Receive();
}


void I2C_Configuration(void)
{
        I2C_InitTypeDef  I2C_InitStructure;
        GPIO_InitTypeDef  GPIO_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);          //I2C时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //I2C对应GPIO管脚时钟
   

        /* PB6,7 I2C1 PB10.11 I2C2  SCL and SDA */
        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //设置管脚为复用功能开漏输出
        GPIO_Init(GPIOB, &GPIO_InitStructure);          //I2C接口使用的GPIO管脚初始化
       
        I2C_DeInit(I2C1); //0x40005400恢复到默认值
        I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;         //设置I2C接口模式
            I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //设置I2C接口的高低电平周期
            I2C_InitStructure.I2C_OwnAddress1 = 0x60;          //设置I2C接口的主机地址
            I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;        //设置是否开启ACK响应         
            I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
            I2C_InitStructure.I2C_ClockSpeed = 100000;        //100K速度
   
        I2C_Cmd(I2C1, ENABLE);                            //使能I2C接口
        I2C_Init(I2C1, &I2C_InitStructure);               //I2C接口初始化
        /*允许1字节1应答模式*/
        I2C_AcknowledgeConfig(I2C1, ENABLE);              //使能I2C接口响应--应答功能

}

void I2C_Slave_Receive(void)
{
        u8 i,data_receive[N];
        for(i=0;i<N;i++)
        {
                data_receive[i] = 0;       
        }

        while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));         //Bus busy flag 0x00020000        -- SR2 BUSY位

        /*允许1字节1应答模式*/
        I2C_AcknowledgeConfig(I2C1, ENABLE);   //CR1_ACK_Set 0x0400 -- CR1 ACK位

        /* 发送起始位 */
            I2C_GenerateSTART(I2C1, ENABLE); //CR1_START_Set 0x0100 -- CR1 START

        /*接收地址*/
        I2C_Send7bitAddress(I2C1,0x60,I2C_Direction_Receiver);  //从机地址 传输方向
            while(!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED));/*EV1 ADDR*/

            for(i=0;i<N;i++)
            {
                if(7==i)
                {
                             I2C_AcknowledgeConfig(I2C1, DISABLE);        //最后一位后要关闭应答的
                            I2C_GenerateSTOP(I2C1, ENABLE);                        //发送停止位
                }
            
                while(!I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_BYTE_RECEIVED)); /* EV2 */
                    data_receive[i] = I2C_ReceiveData(I2C1);
            }
        //再次允许应答模式
        I2C_AcknowledgeConfig(I2C1, ENABLE);
}
nemo0507
3楼-- · 2019-12-17 07:43
咋木有人来看看涅…… 大侠……求解啊啊
nemo0507
4楼-- · 2019-12-17 09:45
 精彩回答 2  元偷偷看……
inspiriting
5楼-- · 2019-12-17 10:29
我的还没搞好
630511630511
6楼-- · 2019-12-17 16:10
学习  谢谢楼主

一周热门 更多>