和STM32的I2C接口搏击道路1

  1. 一直使用STM32进行开发,对项目进行开发的过程中对I2C接口很常用到。平常在使用I2C过程中基本都是使用IO口直接模拟I2C。不过在使用软件I2C过程中可能会面临着达不到400k的速率。因此想趁着使用I2C器件的过程中使用硬件I2C。在调试的过程中,终于明白了大家为什么直接使用软件模拟I2C。因为硬件在使用过程中确实有点麻烦。
  2. 平常在对STM32的片内外设进行初始化的时候喜欢直接操作寄存器,主要原因是觉得对寄存器操作能够对该资源的运转有个质的把控。在使用库函数或者HAL库的时候总觉得隔着一层面纱不能真正对资源进行把控。因此,在对I2C进行调试的时候,依旧采用了寄存器操作来初始化硬件。
  3. 通过查看STM32F103RCT6的原理图,看到I2C的功能是复用在PB6,PB7上。因此在初始化的时候需要先对PB6,PB7进行复用初始化。这里要注意一点,I2C的管脚复用时为开漏推挽输出。因为I2C协议支持多个主设备与多个从设备在一条总线上,如果不用开漏输出,而采用推挽输出,会出现主设备之间短路的情况,因此要为开漏输出。而且I2C的片外器件要接上拉电阻。这里采用的I2C器件为MAX30101,如图1所示。该小开发板在板子上已经接了上拉电阻。
    图1 MAX30101开发板
  4. 硬件准备好后就需要进行软件的编写,这时候需要根据操作手册进行时序的编写。首先是关于I2C的初始化。在《STM32中文参考手册》中对I2C在主模式的操作顺序如图2所示。
    图2 对I2C主模式的操作时序说明
  5. 按照说明进行操作时,内想到初始化后一直不成功,进行断点调试发现在初始化后I2C的SR2寄存器的BUSY位一直是1,这说明总线一直被占用。但是这明显是不对的,没有数据“住在”总线的时候,总线竟然呈现着“被占用”的状态。刚开始逐行查找初始化的语句并没有发现错误。代码如下所示:
/*
*Description :I2C初始化,这里配置为100khz的普通模式
*Param       :None
*Return Code :None
*/
void I2C_Init(void)
{   
	RCC->APB2ENR|=1<<3;        //PORTB是专用使能
	RCC->APB1ENR|=1<<21;       //I2C1时钟使能
	GPIOB->CRL|=0X00FFFFFF;    //GPIOB6,7复用为开漏输出
	GPIOB->CRL|=0XFF000000;    //GPIOB6,7复用为开漏输出
//	GPIOB->CRL|=0XFF000000;    //GPIOB6,7复用为开漏输出
	GPIOB->ODR|=1<<6;
	GPIOB->ODR|=1<<7;
	I2C1->CR2|=0X24;           //0X02 2MHZ-0X24 36MHZ, 0X14 20MHZ,0X08
    I2C1->CCR|=0XB4;
    I2C1->TRISE&=0X0000;
	I2C1->TRISE|=0X25;
	I2C1->OAR1|=1<<14;         //在这里将该位用软件设置为1
	I2C1->OAR1|=0X0A;          //写入从机地址
	I2C1->CR1|=1<<10;          //使能I2C模式
	I2C1->CR1|=1<<0;           //使能I2C模式
}
  1. 单步调试如图3所示

图3 单步调试结果图

  1. 从代码分析刚开始确实没查找出错误,首先对PB6,PB7的操作来看,最终确实是配置成了开漏复用输出,对I2C的寄存器操作确实是都写进去了,但是SR2的BUSY位还是为1.表示总线处在繁忙的状态。查找无果。
  2. 既然寄存器一直走不通,何不采用库函数来配置一下。对I2C进行配置。然后查看SR2状态寄存器是否依旧是busy状态。采用库函数的配置程序如下
/*
*Description:MAX30101初始化
*Param      :None
*Return Code:None
*/
void max30102_init(void)
{  
	/*对端口进行初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	I2C_InitTypeDef I2C_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);           //打开PORTB口的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);            //打开I2C1的端口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD;                  //开漏复用输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;                        //SCL
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;                        //SDA
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	/*对I2C进行初始化*/
	I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;                       //设置为I2C模式
	I2C_InitStructure.I2C_OwnAddress1=0X0A;                        //自己的地址
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_ClockSpeed = 100000;
	I2C_Init(I2C1,&I2C_InitStructure);
	I2C_Cmd(I2C1,ENABLE);                                          //使能I2C1
}
  1. 使用库函数对I2C配置如图4所示。图4 使用库函数对I2C进行初始化
  2. 可以看出采用库函数初始化I2C的SR2状态位不是处于BUSY状态,这说明配置步骤没有问题。问题应该还是出在寄存器配置端口这里。重新返回事故现场进行调试。发现在执行第14行代码的时候。SR2寄存器的BUSY位就会进入置1的状态。结果如图5所示。因为学习的板子一直是参考正点原子的例程,因此习惯了先&后|对GPIO的寄存器进行操作。当然也不是说正点原子的例程有错误,只是在这时候执行第14行代码再执行第15行代码确实将PB6,PB7配置成了开漏输出,但是这个过程是先配置为模拟输入然后再配置成了开漏输出。这样实质是没有对PB6,PB7完成开漏配置。
    图5 单步调试现场图
  3. 跳过GPIO->CRL&=0X00FFFFFF;这个步骤,直接执行GPIO->CRL|=0XFF000000,在进行调试,发现事故不在发生。结果如图6所示。从结果中可以看出,I2C的SR2寄存器没有处在BUSY状态,对PB6,PB7的配置也是开漏输出。图6 更改后调试结果图
  4. 至此,刚走过初始化的部分。初始化中唯一一个坑就是对PB6,PB7的推挽输出复用功能时的寄存器配置。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dog345

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值