浅谈单片机的数据输入和接收
- 序
本文将以51单片机的I2C通信协议为例,将涉及到I2C的基本过程以及其中我认为比较重要的点。
- I2C的基本过程
首先:IIC作为一种串行通信协议,它需要两根线来完成数据传输。一根是时钟线SCL,另外一根是数据传输线SDA,其中这个数据传输线是双向的,既可以主机向从机也可以从机向主机。
发送过程:发送一个开始信号(SCL为高电平SDA由高电平变成低电平,持续时间大于4.7us。为了方便统一将延时设置为10us)。紧随着是7位从机地址位,而这7位地址位前四位为固定地址,后三位为可编程地址。一般情况下,固定地址为1010(十六进制的a)。从机地址位之后的一位是传输方向位,0表示主机向从机放松数据。然后,从机要向主机发送一个应答。表示被寻址的从机知道自己需要接收地址。发送应答后,开始发送数据,每发送一个字节的数据,都需要进行应答或着非应答来进行判断。(这是主机向从机发送数据)
代码部分:
unsigned char I2cSendByte(unsigned char dat)
{
unsigned char a=0,b=0;//最大255,一个机器周期为1us,最大延时255us。
for(a=0;a<8;a++)//要发送8位,从最高位开始
{
SDA=dat>>7; //起始信号之后SCL=0,所以可以直接改变SDA信号
dat=dat<<1;
Delay10us();
SCL=1;
Delay10us();//建立时间>4.7us
SCL=0; //低电平进行传送数据
Delay10us();//时间大于4us
}
SDA=1; //释放时钟线和数据线
Delay10us(); //也可以不写
SCL=1;
while(SDA)//等待应答,也就是等待从设备把SDA拉低
{
b++;
if(b>200) //如果超过2000us没有应答发送失败,或者为非应答,表示接收结束
{
SCL=0;
Delay10us();
return 0;
}
}
SCL=0;
Delay10us();
return 1;
}
这个部分用来将我们写入的命令转换成数据线相应的高低电平起伏。简单的说,就是发送数据。
需要注意的是,我们需要先将高位数据发送,这个过程我们可以通过右移7位来实现;(SDA=dat>>7
)是为了获取最高位的数据(0或1)。dat=dat<<1;
是为下一次循环获取第二高位数据做准备。至于后面的while循环部分,是为了判断是否产生应答,以决定是否继续发送数据。以下是时序图用来增加理解:
接下来是读取数据部分:
unsigned char I2cReadByte()
{
unsigned char a=0,dat=0;
SDA=1; //起始和发送一个字节之后SCL都是0
Delay10us();
for(a=0;a<8;a++)//接收8个字节
{
SCL=1;
Delay10us(); //使数据保持稳定
dat<<=1;
dat|=SDA;
Delay10us();
SCL=0;
Delay10us();
}
return dat;
}
这里有一个比较重要的点:dat=0
,当dat<<=1
之后,它依旧是0,但是当我们进行dat|=SDA
运算后,dat为0000 0001(在这里,我假设sda为高电平),如果我们需要纠结dat为0的时候是几位的,在这里,我理解为8位,毕竟我以前都误以为是一位…因为当它是一位,会导致整个循环过后,数据是9位…
除此之外,还需要注意一个顺序问题!先左移,在进行与运算!!!在这里,我简单写一个循环过程(假设sda一直为高电平):dat=0-----> dat<<1(dat=0000 0000)----->dat|=SDA(dat= 0000 0001)-----> dat<<1(dat=0000 0010)----->dat|=SDA(dat=0000 0011);到此,我想要强调的是,这个顺序可以保证移位8次,如果顺序相反,会导致移位9次,当然了,没有必要去记忆背诵这个顺序,理解才是最重要的。
至此,我想要记录的主要内容已经over了~
对了!如果从机在接收完一次数据后不希望继续接收,需要在以此刻为基准,接收的第一个数据后发送一个非应答信号!千万不要记错了
上一篇: 2440的UART初始化
下一篇: 原生JS封装变速移动函数