NOTE
I2C外设库函数介绍
//生成起始条件
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
//生成终止条件
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
//配置CR1的ACK应答使能
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//发送数据到DR数据寄存器
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
//接收数据--读取DR的数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//发送7位地址的专用函数
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
/*I2C State Monitoring Functions 状态监控函数
1) Basic state monitoring:基本状态监控
Using I2C_CheckEvent() function
这种方式就是同时判断一个或多个标志位来确定EVx状态是否发生 */
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
/*2) Advanced state monitoring:高级状态监控
Using the function I2C_GetLastEvent()---一般不用,了解即可 */
uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx);
/*3) Flag-based state monitoring:基于标志位的状态监控
Using the function I2C_GetFlagStatus() */
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
//清除标志位
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
//读取中断标志位
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);
//清除中断标志位
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);
1.由USART引入I2C
USART串口通信就是通过TX引脚向RX引脚发送数据流,数据流以字节为单位也可以组合多个字节组成数据包传输,另外串口通信的设计是一条发送线TX,一条接收线RX,没有时钟线的异步全双工协议
假设有一个公司需要设计一个通信协议,在单片机与外部模块连接少量的几根线来实现单片机读写外部模块寄存器的功能,单单这个要求可以用串口的HEX数据包来实现
首先定义一个三个字节的数据包,从单片机向外部模块发送,第一个字节表示读写,发送00表示写数据包,01表示读数据包;第二个字节表示读写的地址;第三个字节表示写入的数据。eg:发送一个数据包为0x00,0x66,0xAA,外部模块收到后就要在0x66这个地址写入0xAA数据;发送一个数据包为0x01,0x66,0x00,外部模块收到后就要读取0x66地址的数据,然后再发送一个字节来返回0x66地址下的数据
当然公司还提了另外的4个要求,如下:
要求1:不要两条通信线,只能在同一根通信线上发送和接收数据,全双工->半双工
要求2:增加应答机制,要求单片机每发送一个字节对方都要有一个应答,每接收一个字节单片机也要给对方一个应答
要求3:在这一条通信线上单片机可以同时跟多个模块进行通信,在与其中一个模块通信时其他模块不能对其正常的通信产生干扰
要求4:将异步时序改成同步时序的协议,另外加一条时钟线来指导对方读写,单片机可以随时停止传输
- 补充同步时序与异步时序的区别:异步时序的优点是少一条时钟线节省资源,缺点是对时间要求非常严格,非常依赖硬件电路;同步时序的优点是对时间要求不严格,不依赖硬件电路,在一些低端的单片机缺少硬件资源的情况下也很容易使用软件来模拟时序,缺点是多一条时钟线
2.I2C介绍
1)简介
-
I2C总线 (Inter IC BUS) 是由Philips公司开发的一种通用数据总线
-
两根通信线:SCL(Serial Clock)、SDA(Serial Data)(满足了上述的要求4和1)
-
同步,半双工
-
带数据应答(满足了上述的要求2)
-
支持总线挂载多设备(一主多从、多主多从)(满足了上述的要求3)
-
一主多从:单片机作为主机,主导I2C总线的运行,所有挂载在I2C线上的外部模块都作为从机,从机只有被主机点名后才能控制I2C总线
-
多主多从目前不需要掌握
-
2)硬件电路
-
所有I2C设备的SCL连在一起,SDA连在一起
-
I2C总线是"线与"的 特征,只要有一个设备输出低电平,总线就处于低电平
-
设备的SCL和SDA均要配置成开漏输出模式
-
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
3)I2C时序基本单元
-
起始条件:SCL高电平期间,SDA从高电平切换到低电平
-
终止条件:SCL高电平期间,SDA从低电平切换到高电平
-
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
最开始,SCL低电平,主机如果想发送0,就拉低SDA到低电平;如果想发送1,就让SDA回到高电平,然后主机拉低SCL,把数据放在SDA上,主机松开SCL回到高电平,从机读取SDA数据
-
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)---在下图中最开始主机在接收之前就要释放SDA,由低电平回到高电平,也就是最开始的实线部分,从而主机进入接收数据状态
发送和接收字节的区别:发送一个数据是低电平主机放数据,高电平从机读数据;接收一个数据是低电平从机放数据,高电平主机读数据
-
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
-
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
-
意思就是主机在发送完一个字节之后就要紧跟着调用接收应答的时序,用来判断主机是否接收到了数据,如果从机接收到了数据,在应答位上,主机在释放SDA的同时,从机就需要立刻将SDA拉低,在SCL高电平期间主机就会读取应答位,要是读取到应答位为0说明从机接收到了数据,反之同理
-
4)I2C完整时序数据帧
从机设备地址采用7位地址
A. 指定地址写的时序
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
-
指定设备(Slave Address)---由7位从机地址+读写位组成8位,最低位读写位为0表示后面的时序进行写入操作,为1表示后面的时序进行读出操作
-
要是想一个数据帧写入多个数据,就重复写入指定数据(Data)和接收应答,在写入一个数据后地址指针就会自增一次,下一次写入数据就会自动写入下一个地址,以此类推就可以在指定地址按顺序连续写入多个字节
上面的时序图表示对于指定的从机地址为1101000的设备,在其内部0x19地址寄存器中,写入0xAA这个数据
B. 当前地址读的时序
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
-
如果主机想要读取从机的数据就可以执行这个时序
-
当前地址指针指示的地址:在从机中,所有寄存器都被分配到了一个线性区域中,此时会有单独的指针变量来指示其中的某个寄存器,每次写入一个字节后指针就会自增一次,移动到下一个地址。
-
eg:比如调用了上面指定地址写的时