文章目录[隐藏]
目录
一. IIC(I2C Inter-Intergrated Circuit 集成电路总线)
一. IIC(I2C Inter-Intergrated Circuit 集成电路总线)
由Pillphs公司发明的。
IIC也是属于通信的一种,并且也是串行通信(按bit收发数据)。
IIC属于串行总线通信:
只有一根数据线 SDA:Serial DAta 串行数据线
但是还有一根时钟线 SCL:Serial CLock串行时钟线
SDA:Serial DAta 串行数据线
数据传输按bit位,先传送最高bit(MSB)
SCL:Serial CLock串行时钟线
传递的是时钟信号,为什么需要时钟线?用来同步信号的。
同步:约定好发送数据只能在时钟的低跳变时,接收(采样)数据只能在时钟的高跳变时。
所以,IIC是半双工的通信,因为只有一根数据线,在发送数据的时候就不能接收,否则收的数据就是自己发送出去的。IIC通信设备都会挂载在SDA和SCL总线上,或者说在SDA和SCL总线上会挂载很多的设备。那么在任意时刻,只能有一个设备向总线上发送数据,但是接收没有限制,都可以接收。
为了让数据精准到达(而不是广播的形式发送),我们给IIC总线上的每一个设备都给一个唯一的地址,这个地址就是设备地址,用来区分不同的IIC设备的。
二. IIC时序图(IIC协议)
IIC数据通信的大概流程:
a. 总线空闲(空闲指没有数据通信时总线的状态)
我们约定:IIC总线在空闲的时候,SDA和SCL都处于高电平(通过在总线上上接一个上拉电阻即可)
接下来,如果有一个设备需要给另一个设备发送数据的话,就需要一个起始信号
b. 起始信号:用来表示我要往总线上发送数据啦。
SCL时钟线保持高电平
SDA数据线从高到低产生一个低跳变
模拟IIC的起始信号:
//空闲
SCL = 1;
SDA = 1;
delay();
//起始信号
SDA = 0;
delay();
有没有可能两个设备同时发送起始信号呢?
有可能,所以需要总线仲裁:决定谁的起始信号有效。
比如:在发送起始信号前,判断IIC总线是否空闲。
int time_out = SCL_T; //超时时间为一个SCL周期
while(SCL == 1 && SDA == 1 && time_out--);
如果一个SCL周期内,SCL和SDA都是高电平,那么说明就没有人往总线上发送数据。
c. 发送数据
数据包含用户真正要发送的数据,也包括设备地址(指定通信方)。
因为总线上有多个设备,其中一个发起一个起始信号,表示它要与总线上的某个设备或者多个设备进行通信,具体与谁进行通信?如果不指定,总线上所有的设备都可以收到数据。
所以IIC协议规定:每一个IIC总线上的设备都必须要有一个IIC设备地址(7bit/10bit)。
并且,同一个IIC总线上的设备,地址必须不一样。
IIC中数据(包括设备地址)的发送都是按8bit进行发送的。
一个完整的设备地址 = 7bit设备地址 + 1bit读写位(占最低位bit0)
bit0:0 W 表示我要从指定地址上的IIC设备上写入数据
bit0:1 R 表示我要从指定地址上的IIC设备上读取数据
例子:
设备B的地址是101 1010,CPU要发送数据0x55给设备A
CPU: START 10110100 01010101
A :
发送完一个字节的数据之后,对方必须要返回一个ACK(应答位)
CPU: START 10110100 01010101
A : ACK ACK
ACK:在SDA数据线上的第9个时钟周期,接收方给SDA一个低电平。问题:就是如果数据的最后一个bit本身就是一个低电平,那么SDA线此时的电平状态就是0,这个时候,不管接收方应答还是不应答,发送方可能会认为对方应答。
解决:发送方在发送完8bit的数据后,一般会释放SDA数据线(SDA = 1)。
在第9个时钟周期时,接收方就会给SDA一个低电平表示应答(表示我收到啦)。
数据发送规则:
数据发送起始就是根据要发送的数据的bit位的情况给SDA线低电平or高电平。
先发送MSB(最高位)。
发送数据时,更改数据线的要求如下:
IIC协议规定:
在SCL时钟线低跳变的时候,可以改变SDA数据线的电平
所以发送是下降沿触发,每一个下降沿可以发送一个bit的数据
在SCL时钟线高跳变的时候,SDA数据线保持稳定。
所以接收是上升沿触发,每一个上升沿到来,接收方就会去SDA上采集1bit的数据。
d. 停止信号
SCL保持高电平
SDA从低电平到高电平跳变
所以一帧数据:
发送:
START + data(7bit addr + 1bit0(W)) + data(8bit data) + .... + STOP
1bit ACK 1bit ACK接收:
START + data(7bit addr + 1bit1(R)) 1bit ACK 1bit NA + STOP
1bit ACK data(8bit data) + .... +
问题:
SDA线一般是由谁来发送数据就由谁来控制,那么SCL时钟线应该由谁来控制呢?
谁控制都可以,只要不同时控制,但是很多的设备,不具备输出时钟的功能。
因为它可能没有时钟单元,所以,像STM32中一般是由CPU作为时钟输出(控制)者。
所以我们通过谁控制SCL线将其区分成不同的角色:
IIC主设备:Master
产生IIC时钟输出的设备,它控制IIC总线的传输速率。
IIC从设备:Slave
被动接收IIC时钟的设备。
细分:
Master-Send 主发 Master-Receive 主收
Slave-Send 从发 Slave-Receive 从收
IIC总线上的时钟频率一般在几十K ~400Khz,频率越低通信速度就越慢,但是也越稳定
三. I2C模拟时序
/*
IIC_Send_Start:发送IIC起始信号
*/
void IIC_Send_Start(void)
{
//空闲
SCL = 1;
SDA = 1;
delay(IIC_T); //IIC_T:IIC时钟信号的周期
//起始信号
SDA = 0;
delay(IIC_T);
}/*
IIC_Send_Stop:发送IIC停止信号
*/
void IIC_Send_Stop(void)
{
SCL = 1;
SDA = 0;
delay(IIC_T);
SDA = 1;
delay(IIC_T);
}/*
IIC_Send_Byte:将一个字节的数据发送出去
@ch:要发送的数据,1个字节
@返回值:发送成功返回1(接收方回复了一个ACK表示发送成功)
失败返回0(接收方并没有回复ACK)
*/
int IIC_Send_Byte(uint8_t ch)
{
//MSB(最高位)先发,并且是在SCL下降沿的时候发送1bit
int i;
for(i = 7;i >= 0;i--) //8个SCL时钟周期发送8bit
{
SCL = 0; //周期开始
SDA = (ch >> i) & 0x01; //发送一个bit的数据
delay(IIC_T/2); //等待半个时钟周期
SCL = 1; //上升沿产生
delay(IIC_T/2); //延时等待一会让对方有时间接收
}
//发送发在发送完8bit数据之后,一般要先释放数据线
//同时在第9个时钟周期,等待接收方回应一个ACK低电平
SCL = 0; //第9个周期开始
SDA = 1; //发送方先释放数据线
delay(IIC_T/2); //等待接收方应答
SCL = 1;
if(SDA)
{
return 0; //代表无人应答,发送失败
}
else
{
return 1; //代表有人应答,发送成功
}
delay(IIC_T/2);
}/*
IIC_Recv_Byte:从IIC总线上接收一个字节
@返回值:将接收到的字节返回
*/
uint8_t IIC_Recv_Byte(void)
{
}
/*
IIC_Write_Datas:向指定的IIC设备写入数据
@addr:7bit的目标IIC设备地址
@str:要发送的数据字符串
@len:要发送的数据字符串的长度
@返回值:发送成功返回1,发送失败返回0
*/
uint8_t IIC_Write_Datas(uint8_t addr,uint8_t *str,uint32_t len)
{
}
四、STM32F4XX的IIC通信
五、重载printf()
为了更好的调试代码,重载ptintf。重载ptintf()实际是重载fputc()
int fputc(int c,FILE *stream)
{
USART_SendData(USART1,c & 0xFF);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return 0;
}
版权声明:本文为CSDN博主「歌咏^0^」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_58885663/article/details/121599535
目录
一. IIC(I2C Inter-Intergrated Circuit 集成电路总线)
一. IIC(I2C Inter-Intergrated Circuit 集成电路总线)
由Pillphs公司发明的。
IIC也是属于通信的一种,并且也是串行通信(按bit收发数据)。
IIC属于串行总线通信:
只有一根数据线 SDA:Serial DAta 串行数据线
但是还有一根时钟线 SCL:Serial CLock串行时钟线
SDA:Serial DAta 串行数据线
数据传输按bit位,先传送最高bit(MSB)
SCL:Serial CLock串行时钟线
传递的是时钟信号,为什么需要时钟线?用来同步信号的。
同步:约定好发送数据只能在时钟的低跳变时,接收(采样)数据只能在时钟的高跳变时。
所以,IIC是半双工的通信,因为只有一根数据线,在发送数据的时候就不能接收,否则收的数据就是自己发送出去的。IIC通信设备都会挂载在SDA和SCL总线上,或者说在SDA和SCL总线上会挂载很多的设备。那么在任意时刻,只能有一个设备向总线上发送数据,但是接收没有限制,都可以接收。
为了让数据精准到达(而不是广播的形式发送),我们给IIC总线上的每一个设备都给一个唯一的地址,这个地址就是设备地址,用来区分不同的IIC设备的。
二. IIC时序图(IIC协议)
IIC数据通信的大概流程:
a. 总线空闲(空闲指没有数据通信时总线的状态)
我们约定:IIC总线在空闲的时候,SDA和SCL都处于高电平(通过在总线上上接一个上拉电阻即可)
接下来,如果有一个设备需要给另一个设备发送数据的话,就需要一个起始信号
b. 起始信号:用来表示我要往总线上发送数据啦。
SCL时钟线保持高电平
SDA数据线从高到低产生一个低跳变
模拟IIC的起始信号:
//空闲
SCL = 1;
SDA = 1;
delay();
//起始信号
SDA = 0;
delay();
有没有可能两个设备同时发送起始信号呢?
有可能,所以需要总线仲裁:决定谁的起始信号有效。
比如:在发送起始信号前,判断IIC总线是否空闲。
int time_out = SCL_T; //超时时间为一个SCL周期
while(SCL == 1 && SDA == 1 && time_out--);
如果一个SCL周期内,SCL和SDA都是高电平,那么说明就没有人往总线上发送数据。
c. 发送数据
数据包含用户真正要发送的数据,也包括设备地址(指定通信方)。
因为总线上有多个设备,其中一个发起一个起始信号,表示它要与总线上的某个设备或者多个设备进行通信,具体与谁进行通信?如果不指定,总线上所有的设备都可以收到数据。
所以IIC协议规定:每一个IIC总线上的设备都必须要有一个IIC设备地址(7bit/10bit)。
并且,同一个IIC总线上的设备,地址必须不一样。
IIC中数据(包括设备地址)的发送都是按8bit进行发送的。
一个完整的设备地址 = 7bit设备地址 + 1bit读写位(占最低位bit0)
bit0:0 W 表示我要从指定地址上的IIC设备上写入数据
bit0:1 R 表示我要从指定地址上的IIC设备上读取数据
例子:
设备B的地址是101 1010,CPU要发送数据0x55给设备A
CPU: START 10110100 01010101
A :
发送完一个字节的数据之后,对方必须要返回一个ACK(应答位)
CPU: START 10110100 01010101
A : ACK ACK
ACK:在SDA数据线上的第9个时钟周期,接收方给SDA一个低电平。问题:就是如果数据的最后一个bit本身就是一个低电平,那么SDA线此时的电平状态就是0,这个时候,不管接收方应答还是不应答,发送方可能会认为对方应答。
解决:发送方在发送完8bit的数据后,一般会释放SDA数据线(SDA = 1)。
在第9个时钟周期时,接收方就会给SDA一个低电平表示应答(表示我收到啦)。
数据发送规则:
数据发送起始就是根据要发送的数据的bit位的情况给SDA线低电平or高电平。
先发送MSB(最高位)。
发送数据时,更改数据线的要求如下:
IIC协议规定:
在SCL时钟线低跳变的时候,可以改变SDA数据线的电平
所以发送是下降沿触发,每一个下降沿可以发送一个bit的数据
在SCL时钟线高跳变的时候,SDA数据线保持稳定。
所以接收是上升沿触发,每一个上升沿到来,接收方就会去SDA上采集1bit的数据。
d. 停止信号
SCL保持高电平
SDA从低电平到高电平跳变
所以一帧数据:
发送:
START + data(7bit addr + 1bit0(W)) + data(8bit data) + .... + STOP
1bit ACK 1bit ACK接收:
START + data(7bit addr + 1bit1(R)) 1bit ACK 1bit NA + STOP
1bit ACK data(8bit data) + .... +
问题:
SDA线一般是由谁来发送数据就由谁来控制,那么SCL时钟线应该由谁来控制呢?
谁控制都可以,只要不同时控制,但是很多的设备,不具备输出时钟的功能。
因为它可能没有时钟单元,所以,像STM32中一般是由CPU作为时钟输出(控制)者。
所以我们通过谁控制SCL线将其区分成不同的角色:
IIC主设备:Master
产生IIC时钟输出的设备,它控制IIC总线的传输速率。
IIC从设备:Slave
被动接收IIC时钟的设备。
细分:
Master-Send 主发 Master-Receive 主收
Slave-Send 从发 Slave-Receive 从收
IIC总线上的时钟频率一般在几十K ~400Khz,频率越低通信速度就越慢,但是也越稳定
三. I2C模拟时序
/*
IIC_Send_Start:发送IIC起始信号
*/
void IIC_Send_Start(void)
{
//空闲
SCL = 1;
SDA = 1;
delay(IIC_T); //IIC_T:IIC时钟信号的周期
//起始信号
SDA = 0;
delay(IIC_T);
}/*
IIC_Send_Stop:发送IIC停止信号
*/
void IIC_Send_Stop(void)
{
SCL = 1;
SDA = 0;
delay(IIC_T);
SDA = 1;
delay(IIC_T);
}/*
IIC_Send_Byte:将一个字节的数据发送出去
@ch:要发送的数据,1个字节
@返回值:发送成功返回1(接收方回复了一个ACK表示发送成功)
失败返回0(接收方并没有回复ACK)
*/
int IIC_Send_Byte(uint8_t ch)
{
//MSB(最高位)先发,并且是在SCL下降沿的时候发送1bit
int i;
for(i = 7;i >= 0;i--) //8个SCL时钟周期发送8bit
{
SCL = 0; //周期开始
SDA = (ch >> i) & 0x01; //发送一个bit的数据
delay(IIC_T/2); //等待半个时钟周期
SCL = 1; //上升沿产生
delay(IIC_T/2); //延时等待一会让对方有时间接收
}
//发送发在发送完8bit数据之后,一般要先释放数据线
//同时在第9个时钟周期,等待接收方回应一个ACK低电平
SCL = 0; //第9个周期开始
SDA = 1; //发送方先释放数据线
delay(IIC_T/2); //等待接收方应答
SCL = 1;
if(SDA)
{
return 0; //代表无人应答,发送失败
}
else
{
return 1; //代表有人应答,发送成功
}
delay(IIC_T/2);
}/*
IIC_Recv_Byte:从IIC总线上接收一个字节
@返回值:将接收到的字节返回
*/
uint8_t IIC_Recv_Byte(void)
{
}
/*
IIC_Write_Datas:向指定的IIC设备写入数据
@addr:7bit的目标IIC设备地址
@str:要发送的数据字符串
@len:要发送的数据字符串的长度
@返回值:发送成功返回1,发送失败返回0
*/
uint8_t IIC_Write_Datas(uint8_t addr,uint8_t *str,uint32_t len)
{
}
四、STM32F4XX的IIC通信
五、重载printf()
为了更好的调试代码,重载ptintf。重载ptintf()实际是重载fputc()
int fputc(int c,FILE *stream)
{
USART_SendData(USART1,c & 0xFF);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return 0;
}
版权声明:本文为CSDN博主「歌咏^0^」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_58885663/article/details/121599535
暂无评论