×
单片机 > 单片机程序设计 > 详情

STM32F1xx的IIC通信

发布时间:2020-05-19 发布时间:
|

一、硬件配置相关


IIC的SCL和SDA都要求接上拉电阻,也即,只要主机和从机都还没开始干活,那么SCL和SDA就都是空闲的(默认高电平被称为“空闲”,也可以成为总线被“释放”),同理,SCL或SDA为low时,就称总线被占用或正忙。


IIC协议要求:SCL和SDA必须都是【开漏+上拉】!


简单些一下开漏和推挽的区别:参考《从硬件分析推挽输出和开漏输出详细区别》

(1)推挽:写1时,GPIO被导通到VCC,写0时GPIO被导通到GND

(2)开漏:写1时,GPIO被导通到悬空的漏极,写0时GPIO被导通到GND。

也即IO开漏时,要想写1驱动LED,必须得接上拉电阻,要不然这个IO上写1时,是高阻态,虽然电压表测出了电压,但仍然无法驱动LED,就好像这个IO是悬空似的。


下面讲讲为什么IIC必须要接上拉?


换句话说,IIC的从器件(如24C04等),不具备拉高总线的能力,从机控制SDA上的电平,只能通过令SDA接地或不接地来实现输出0或1,而无法通过接地或接VCC来实现输出0或1。这就要求IIC总线上必须是默认上拉的,否则从机无法令SDA为高。


从另一方面讲:只有开漏上拉,才能满足“线与”逻辑,也即SDA线上多个从机中的任何一个拉低了SDA,就会导致SDA为0。


主机为什么使用开漏?试想,

主机如果使用了推挽,当主机释放SDA时(令SDA=1时),相当于SDA被接通到VCC,这时从机无法控制SDA为0;

主机如果使用了开漏,当主机释放SDA时(令SDA=1时),相当于SDA被悬空上拉到VCC,这时从机可以拉低SDA,使SDA=0,也可以释放SDA使SDA=1,这样从机才能发数据。


void i2c_io_Init(void)

{      

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB1PeriphClockCmd(IIC_APB1Periph, ENABLE);

   

GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN | IIC_SDA_PIN;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //输出,读SDA时要改成输入

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//IIC要求必须上拉

GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//IIC要求必须开漏输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(IIC_GPIOx, &GPIO_InitStructure);

 

GPIO_SetBits(IIC_GPIOx, IIC_SCL_PIN | IIC_SDA_PIN);//开漏写1上拉,释放总线

}

为了能任意控制SCL的频率,建议用IO模拟时,把delay函数的实参定义为宏或变量,以便于修改。一般常用的SCL频率为100KHZ~400KHZ。


 


二、软件操作相关(硬件IIC)


用的是3.5版本的固件库


固件库中有些宏的名字起得并不好,这里主要做一些注释,方便以后查阅。而且还发现固件库做的各种检查实在是很繁琐,比如检查字节是否发送完毕,库函数不仅会检测字节是否发送完毕,还顺便同时检测5/6个比特位。如果对程序的速度需要优化的话,可以在库函数的基础上自己改改。


每次执行IIC操作后,都要检查操作的结果,检查所用的函数为I2C_CheckEvent(I2Cx, event),其中event为4B,高2B是I2C_SR2寄存器,低2B为I2C_SR1寄存器。


1、发送起始信号


I2C_GenerateSTART(I2C2,ENABLE);//起始信号

while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT));//等待起始信号完毕

其中I2C_EVENT_MASTER_MODE_SELECT宏代表SR2=b_0000_0011,SR1=b_0000_0001,也即SR共有3个bit置位,分别代表:IIC正在通信(忙标志)、当前为主机模式、起始信号已发送完毕。


PS:IIC的忙标志位SR2.1是由硬件自动置位和清除的,只要SCL或SDA中出现低电平,该bit就会被置1。


2、发送片选信号(写)


I2C_Send7bitAddress(I2C2, DB5883_CHIP_ADDR,I2C_Direction_Transmitter);//发送片选地址+写信号

while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//等待片选字节发送完毕,且等待从机的ack

其中I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED代表SR2=b_0000_0111,SR1=B_1000_0010,共5 bits置位,分别代表:数据已发送、正忙标志、是主机模式、数据寄存器已空、片选地址已发送(且已收到从机的ack)


3、发送片选信号(读)


I2C_Send7bitAddress(I2C2, DB5883_CHIP_ADDR,I2C_Direction_Receiver);//发送片选地址+读信号

while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//等待片选地址发送完成,且等待从机ack

其中I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED代表SR2=b_0000_0011,SR1=B_0000_0010,共3 bits置位,分别代表:正忙标志、是主机模式、片选地址已发送(且已收到从机的ack)


4、发送一个字节的数据


I2C_SendData(I2C2,REG_Address);

while(!I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//等待字节发送完毕,且等待从机ack

I2C_EVENT_MASTER_BYTE_TRANSMITTED代表SR2=b_0000_0111,SR1=B_1000_0100,共5 bits置位,分别代表:数据已发送、正忙标志、是主机模式、数据寄存器已空、收到了从机的ack。


5、设置主机接收到一个字节后要发送Ack还是NoAck


I2C_AcknowledgeConfig(I2C2, DISABLE);//不发送Ack(也即发送NoAck)

6、读取从机发来的字节


//等待读信号(选寄存器)信号发送完成

I2C_AcknowledgeConfig(I2C2, DISABLE);//设置为NoAck模式

while(!(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)));//直到接收完从机发来的字节

REG_data=I2C_ReceiveData(I2C2);//读出寄存器数据

I2C_GenerateSTOP(I2C2,ENABLE);//停止信号

有些iic从机支持被主机连续读多个字节,一般这种设备在被连续读时,主机每收到从机的一个字节,主机都要向从机发送ACK,直到读完最后一个字节,主机向从机发送NoAck,这时从机会停止发送,从机会等待主机发起stop信号或从机自动超时等待新的start


三、软件操作相关(软件模拟IIC)


关键字:STM32F1xx  IIC通信 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
AVR熔丝位操作时的要点和需要注意的相关事项