单片机串口总结修改版.docx
- 文档编号:23055498
- 上传时间:2023-04-30
- 格式:DOCX
- 页数:26
- 大小:32.13KB
单片机串口总结修改版.docx
《单片机串口总结修改版.docx》由会员分享,可在线阅读,更多相关《单片机串口总结修改版.docx(26页珍藏版)》请在冰豆网上搜索。
单片机串口总结修改版
第一篇:
单片机串口总结
51单片机串口总结
有句话说“尽信书不如无书”,要学好单片机就要不断的、大胆的实验,要多怀疑,即使我们的怀疑最终被证明是错误的那么这也是进步(人们认识事物很多情况下来源于怀疑),当怀疑出现时就要去实践。
有很多东西如果不通过实践是不可能掌握其中隐藏的奥秘,就拿51单片机串口通讯这一块,我认为掌握很好了,可以很轻松的实现数据的接收、发送,但这段时间当我重新学习串口时,我才发现里面还有很多小细节从没注意,更别说研究了。
对于接收发送程序永远是按照别人的模式来编写程序,并没有真真正正的挖掘深层次的内容。
我身边太多的人在临摹别人的程序,当然我不反对,但是希望自己多问几个问什么,单纯的会编程是学不好单片机的,毕竟单片机有自己独特的硬件结构。
开讲之前先简要说一下同步、异步通信:
同步通信:
发送方时钟对接收方时钟控制,使双方达到完全同步。
异步通信:
发送与接受设备使用各自的时钟控制数据的发送和接受过程(虽然时钟不同,但一般相差不大)。
51单片机串行口结构
从上图中我们看到,51单片机有两个物理上独立的接收、发送缓冲器SBUF,它们共用同一个地址99H,但是请注意:
接收缓冲器只能读而不能写,发送缓冲器只写不读。
单片机可以同时实现数据的发送与接收功能。
特别注意:
接收器是双缓冲结构:
当前一个字节从接收缓冲区取走之前,就已经开始接收第
二个字节(串行输入至移位寄存器),此时如果在第二个字节接收完毕而前一个字节还未被读走,那么就会丢失前一个字节。
51单片机串口控制寄存器
关于51单片机的控制寄存器各个位表示的含义在这里我只谈SM2。
SM2为多机控制位,主要用于工作方式2和3,当接收机的SM2=1时,可以利用接收到的RB8来控制是否激活RI(RB8=0不激活RI,收到的数据丢失;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务程序中将数据从SBUF中读走)。
当SM2=0时,不论收到的RB8为何值都将使接收到的数据进入SBUF,,并激活RI,通过控制SM2实现多机通信。
51单片机串口通讯方式
51串口通讯方式有3种,方式0、方式
1、方式2与方式3,他们的工作模式不尽相同。
首先他们的波特率很容易忽视。
方式0与方式2的波特率固定,而方式1和3的波特率由T1的溢出率决定。
方式0的波特率=f/12
系统晶振的12分频,换句话说12M晶振的情况下,其波特率可达1M,速度是很高的(当我们在选用串行器件并采用方式0时需要特别注意器件所能允许的最大时钟频率)。
方式2=f/64或f/32(当SMOD=1时为f/32,SMOD=0时为f/64)。
曾经我用方式2进行MODBUS通信时,总是通讯失败,我仔细检查程序,没有发现逻辑错误,特别是当我参考别人的程序时,发现很少有人用方式2进行MODBUS通讯,所以当时自己妄下结论51单片机的串行方式2不可用,直到有一天夜里我突然想起方式2的波特率是固定的,试想晶振11.0592M/32或11.0592M/64怎么也不可能是9600啊,怎么可能通信成功。
这才恍然大悟,看来还是自己太武断了,没有认真看书啊。
有时我们认为我们犯这样的错误很低级,其实我们很多次都是因为这样的小细节导致我们整个系统不正常,正所谓“千里之堤毁于蚁穴”,这些细节真的伤不起啊。
方式
1、3波特率=(2smod/32)*T1的溢出率,其中TI的溢出率=f/{12*[256-(TH1)]}.关于3种通讯方式其中有几点特别容易出错:
1、无论采用哪种通讯方式,数据发送和接受都是低位在先,高位在后。
2、3种方式作为输出,由于输出是CPU主动发送,不会产生重叠错误,当数据写入SBUF后,发送便启动(通过单片机内部逻辑控制,与程序无关),当该字节发送结束(SBUF空),
置TI。
不要理解为当数据一写入SBUF就置位TI,如果中断允许则在中断中发送数据,这就大错特错了。
3同样作为输入,可能会产生重叠错误(主要依赖于特定的环境),当一个字节的数据接收完毕(SBUF满)置位RI,表示缓冲区有数据提示CPU读取。
接下来通过一些实验具体说明串口通信中需要注意的地方
1方式0输出
方式0主要功能是作为移位寄存器,将数据从SBUF中逐位移出,最常见的用法就是外接串入并出的移位寄存器,如74LS164。
之前在做这一部分实验时总是利用单片机I/O端口模拟实现,现在想想在串口未被占用的情况下,方式0是最好的实现方式。
利用串口方式0,向74LS164输出字符“0”的编码,程序如下:
该程序采用了中断方式实现,结果是通过74LS164使数码管显示“0”。
实验结果如下:
这里我说明几点:
1如果采用查询方式,并且只发送一遍,那么程序最后的while
(1);不可以省略,否则会出现数码管闪烁的现象(在KEIL环境下,main()函数也是作为一个调用函数,最后也有返回RET,它不像C中的main()函数,当执行完毕后就停止,而是重新复位执行,如此反复,这一点要特别注意)
这是查询方式下不加while
(1);的现实效果
2如果采用中断方式发送,请记得中断中清除TI,仅仅是为了解除中断标志,而不是等待发送结束,因为此时数据早已离开了SBUF跑到外边去了。
374LS164最高25MHZ,采用方式0,没有问题。
方式0作为输入模式
以74ls165(最高时钟25MHZ)为例,可以满足要求。
对应结果如下:
(注意:
74ls165线传送高位,而串口通信低位在先,所以显示的数据和实际数据高低位正好相反
P1.7---P1.0对应D0---D7)。
本程序只接收一次,也许有人会问,中断程序中REN=0,表示什么意思?
可不可以改成ES=0?
这个问题很好,首先REN=0表示接收禁止,即不允许串口接收数据;ES=0是禁止中断和单片机是否接收数据没有关系,不接收数据自然中断允许也是徒劳,这两者有很大的区别。
我们在很多接收程序中经常可以看到在判断RI标志后紧跟着清除标志位,我想问一下,为什么?
)
如果我们也按照这种模式改写会怎样呢?
实验结果如下
两次结果差异怎么这么大?
为什么会这样子?
为了便于理解,也为了说明问题方便,对中断程序做了如下处理:
结果又变了
是不是感觉很奇怪,究竟咋回事呢?
首先中断程序中当判断RI置位标志后紧跟着清零是为了接收下一个字节的数据,也为了避免单片机重复中断。
当51单片机串口方式0作输入时,在REN=1且RI=0的条件下就启动了单片机串口接收过程。
如果有一个条件不满足就不能启动接收过程,以上出现的错误正式由于忽略了这个重要的因素造成的。
在RI清零后由于REN仍然为1,单片机已经开始接收第二字节的数据,由于串口速度很快,RI仍会置位,而紧接着将REN清零只能阻止单片机接收数据,但是却
不能阻挡第二次中断。
由于只接收了部分外部引脚数据(此时外部引脚为高电平,即逻辑1,其实单片机只接收了一位,对于12M晶振而言,方式0大约8us接收一个字节数据)。
相反在RI=0与REN=0之间加上适当的延迟,就可以保证一个字节的数据全部接收完毕,故此时我们读上来的一个字节为0xff。
我在中断程序中添加了一个中断计数器(不加延迟),发现中断服务程序的确执行了两次
结果如下
加上延迟结果
这就验证了刚才的结论。
至于说可不可以换做ES=0,回答是可以的,尽管同样可以实现数据的读取,但是实质不同,当禁止中断后,单片机仍在接收外部数据,只是不再请求中断,自然的不再读取第
2、3。
。
。
。
。
字节的数据,那么P1将保留第一次中断时从SBUF中读出的数据。
如果某一时刻打开中断发现结果不正常,如果理解了上面的机制就不会觉得惊讶了。
建议:
单次接收时,中断服务程序中REN清零放在RI之前。
还有一个问题非常重要:
如果我在中断服务程序中不清除RI,会怎样?
很少有人会这样用,但是经常有人忘记了(包括我)。
课本上写得很清楚,务必在中断中用软件清除RI,为什么要这样呢?
难道仅仅是为了接收下一次数据并且避免单片机不断的响应中断?
的确如此,如果对于一个小系统而言,不清除中断标志,那么单片机将不停的中断,影响接下来任务的执行,系统必然瘫痪,而且不能正常的接收数据。
总结:
方式0作为发送方,只要向SBUF中写入数据就启动了发送过程;
方式0在座位接收模式时,REN=
1、RI=0的情况下就已经启动了接收过程。
在中断程序中要注意两者清零的顺序。
还有一种情况要特别注意:
单片机复位时SCON自动清零,如果单片机不工作在方式0,那么如果采用位操作SCON时也要注意REN=1与SM0、SM1的书写顺序,总之切记方式0启动发送、接收数据的条件。
方式1方式1为10位异步通信模式。
作为输出和方式0没有本质的区别,不同的是数据帧的形式,但是对于接受模式则有点不同,当REN=1且RI=0时,单片机并不启动接收过程。
而是以已选择波特率的16倍速率采样RXD引脚的电平,当检测到输入引脚发生1---0负跳变时,则说明起始位有效,才开始接受本帧数据。
方式1模式下单片机可以工作在全双工以及半双工方式。
下面举两个例子
半双工
主机发送某一字符,从机接收到数据后返回数据加1的值比如主机发送“1“,从机收到后回复主机”2“。
实验结果如下:
方式1工作方式主要注意:
1波特率可变。
2数据接收以起始位为标志,停止位结束。
3当RI=0且SM2=0或接收到有效停止位时,单片机将接收到的数据移入SBUF中,两个条件缺一不可。
方式2和方式3方式2和3不同的只是波特率,这里以方式3为例
作为输出模式同方式1没有区别,只是增加了第八位数据位,第八位数据可以用作校验位或在多机通信中用作数据/地址帧的判别位。
首先我们来做模拟主从奇偶校验模式
主机发送一帧数据,并发送奇偶校验位,从机接收数据后,判断数据是否正确,如果正
确,接收指示灯亮,并且回送主机数据加1,反之回送0;主机接收从机信息,如果校验正确点亮LED指示灯.(从机、主机接收数据无论校验正确与否,均显示接收到的字节数据)。
奇校验模式演示结果如下:
(注:
从接接收不正确,返回0)
主从机接收正确效果
之前我们已经介绍了SM2的具体用法,主要用于多机通信,将SM2作为数据/地址帧
的判别位,在接收地址时令SM2=1,当接收到的第八位数据为1时激活RI产生中断,然后比较地址,如果地址符合则清除SM2准备接受数据信息,反之不理睬。
特别注意当RI=0且SM2=0(或SM2=1时接收到第9位数据为1)时,单片机将接收到的数据移入SBUF中,两个条件缺一不可。
在这里我只举一个简单的例子一个主机,两个从机
1起始时,主机从机的SM2均置位,所有的从机等待主机发送地址帧,主机令TB8=1,发送地址帧。
2所用的从机将接受到的地址和自己的地址比较,如果符合,点亮LED指示灯,清除SM2(准备接受主机发送的数据帧),并将自己的地址发送到主机。
3主机接收从机发送的地址信息,如果地址符合则数码管显示从机地址并开始准备发送数据,反之发复位信号,TB8=1。
4从机接收数据先判断RB8,如果RB8=1,则复位,重新开始接收主机发送的地址帧,反之通过P1口外接数码管显示接收到的数据。
实验结果如下:
注意:
如果主机没有得到正确的地址,则将按照一定的速率发送地址帧,直到接收正确的地址为止,该试验主机向从机2发送信息。
另外在这里我补充两点:
1我们可以很方便的利用串口通信的工作方式2或3实现奇偶校验,注意技巧,当为偶校验时TB8=P,奇校验时TB8=~P;
2当单片机利用中断发送大量数据时,尽量采用中断发送,因为单片机在写入SBUF数据后由硬件将数据发送完,在发送过程中,单片机还可以做很多事情,利用中断发送数据可以提高CPU利用率。
尤其在低波特率时效果更明显。
第二篇:
单片机串口通信方式总结
IIC总线通信协议————数据传输高位在前p2331,起始和停止条件
开始信号:
SCL为高电平,SDA由高电平向低电平跳变,开始传送数据。
voidstart()//开始位{SDA=1;
//SDA初始化为高电平“1”
SCL=1;
//开始数据传送时,要求SCL为高电平“1”
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SDA=0;
//SDA的下降沿被认为是开始信号
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SCL=0;
//SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)}结束信号:
SCL为高电平,SDA由低电平向高电平跳变,结束传送数据。
voidstop()//停止位{SDA=0;
//SDA初始化为低电平“0”
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SCL=1;
//结束数据传送时,要求SCL为高电平“1”
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SDA=1;
//SDA的上升沿被认为是结束信号}
2,数据格式(数据输入)
在IIC总线开始信号后,送出的第一个字节数据是用来选择器件地址和数据方向的,其格式为
从器件收到地址型号后与自己的地址比较,一致则此器件就是主器件要找的器件,并返回ACK(不管是写数据还是地址都会返回)。
IIC传送数据时SCL为低电平时SDA可改变高低电平,SCL转跳为高时数据输入(此时SDA不能跳变),
发送数据:
bitWriteCurrent(unsignedchary){unsignedchari;bitack_bit;
//储存应答位
for(i=0;i<8;i++)//循环移入8个位
{
SDA=(bit)(y&0x80);
//通过按位“与”运算将最高位数据送到S
//因为传送时高位在前,低位在后
_nop_();
//等待一个机器周期
SCL=1;
//在SCL的上升沿将数据写入AT24Cxx
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SCL=0;
//将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲
y
//将y中的各二进位向左移一位
}SDA=1;
//发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,
//以让SDA线转由接收设备(AT24Cxx)控制
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
SCL=1;
//根据上述规定,SCL应为高电平
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
_nop_();
//等待一个机器周期
ack_bit=SDA;//接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节
//若送高电平,表示没有接收到,传送异常
SCL=0;
//SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
returnack_bit;
//返回AT24Cxx应答位}读数据:
unsignedcharReadData()//从AT24Cxx移入数据到MCU{unsignedchari;unsignedcharx;
//储存从AT24Cxx中读出的数据
for(i=0;i<8;i++){
SCL=1;
//SCL置为高电平
x
//将x中的各二进位向左移一位
x|=(unsignedchar)SDA;//将SDA上的数据通过按位“或“运算存入x中
SCL=0;
//在SCL的下降沿读出数据
}return(x);
//将读取的数据返回}发送数据步骤:
oidWriteSet(unsignedcharadd,unsignedchardat)//在指定地址addr处写入数据WriteCurrent{start();
//开始数据传递
WriteCurrent(OP_WRITE);//选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(add);
//写入指定地址
WriteCurrent(dat);
//向当前地址(上面指定的地址)写入数据
stop();
//停止数据传递
delaynms(4);
//1个字节的写入周期为1ms,最好延时1ms以上}读数据步骤:
/***************************************************函数功能:
从AT24Cxx中的当前地址读取数据出口参数:
x(储存读出的数据)
***************************************************/unsignedcharReadCurrent(){unsignedcharx;start();
//开始数据传递
WriteCurrent(OP_READ);
//选择要操作的AT24Cxx芯片,并告知要读其数据
x=ReadData();
//将读取的数据存入xstop();
//停止数据传递
returnx;
//返回读取的数据}/***************************************************函数功能:
从AT24Cxx中的指定地址读取数据入口参数:
set_add出口参数:
x
***************************************************/unsignedcharReadSet(unsignedcharset_add)//在指定地址读取{start();
//开始数据传递
WriteCurrent(OP_WRITE);
//选择要操作的AT24Cxx芯片,并告知要对其写入数据
WriteCurrent(set_add);
//写入指定地址
return(ReadCurrent());
//从指定地址读出数据并返回}
单总线协议————数据传输低位在前——p2371,初始化单总线器件
初始化时序程序:
函数功能:
将DS18B20传感器初始化,读取应答信号出口参数:
flag
***************************************************/bitInit_DS18B20(void){bitflag;
//储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
DQ=1;
//先将数据线拉高
for(time=0;time
;DQ=0;
//再将数据线从高拉低,要求保持480~960usfor(time=0;time
;
//以向DS18B20发出一持续480~960us的低电平复位脉冲
DQ=1;
//释放数据线(将数据线拉高)
for(time=0;time
;//延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)
flag=DQ;
//让单片机检测是否输出了存在脉冲(DQ=0表示存在)
for(time=0;time
;return(flag);
//返回检测成功标志}
单总线通信协议中存在两种写时隙:
写0写1。
主机采用写1时隙向从机写入1,而写0时隙向从机写入0。
所有写时隙至少要60us,且在两次独立的写时隙之间至少要1us的恢复时间。
两种写时隙均起始于主机拉低数据总线。
产生1时隙的方式:
主机拉低总线后,接着必须在15us之内释放总线,由上拉电阻将总线拉至高电平;产生写0时隙的方式为在主机拉低后,只需要在整个时隙间保持低电平即可(至少60us)。
在写时隙开始后15~60us期间,单总线器件采样总电平状态。
如果在此期间采样值为高电平,则逻辑1被写入器件;如果为0,写入逻辑0。
下图为写时隙(包括1和0)时序
上图中黑色实线代表系统主机拉低总线,黑色虚线代表上拉电阻将总线拉高。
下面是代码:
WriteOneChar(unsignedchardat){unsignedchari=0;for(i=0;i
{
DQ=1;
//先将数据线拉高
_nop_();
//等待一个机器周期
DQ=0;
//将数据线从高拉低时即启动写时序
DQ=dat&0x01;
//利用与运算取出要写的某位二进制数据,
//并将其送到数据线上等待DS18B20采样
for(time=0;time
;//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样
DQ=1;
//释放数据线
for(time=0;time
;//延时3us,两个写时序间至少需要1us的恢复期
dat>>=1;
//将dat中的各二进制位数据右移1位
}
for(time=0;time
;//稍作延时,给硬件一点反应时间}
对于读时隙,单总线器件仅在主机发出读时隙时,才向主机传输数据。
所有主机发出读数据命令后,必须马上产生读时隙,以便从机能够传输数据。
所有读时隙至少需要60us,且在两次独立的读时隙之间至少需要1us恢复时间。
每个读时隙都由主机发起,至少拉低总线1us。
在主机发出读时隙后,单总线器件才开始在总线上发送1或0。
若从机发送1,则保持总线为高电平;若发出0,则拉低总线。
当发送0时,从机在读时隙结束后释放总线,由上拉电阻将总线拉回至空闲高电平状态。
从机发出的数据在起始时隙之后,保持有效时间15us,因此主机在读时隙期间必须释放总线,并且在时隙起始后的15us之内采样总线状态。
下图给出读时隙(包括0或1)时序
图中黑色实线代表系统主机拉低总线,灰色实线代表总局拉低总线,而黑色的虚线则代表上拉电阻总线拉高。
代码为:
unsignedcharReadOneChar(void){
unsignedchari=0;
unsignedchardat;//储存读出的一个字节数据
for(i=0;i
{
DQ=1;
//先将数据线拉高
_nop_();
//等待一个机器周期
DQ=0;
//单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
dat>>=1;
_nop_();
//等待一个机器周期
DQ=1;
//将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
for(time=0;time
;
//延时约6us,使主机在15us内采样
if(DQ==1)
dat|=0x80;//如果读到的数据是1,则将1存入dat
else
dat|=0x00;//如果读到的数据是0,则将0存入dat
//将单片机检测到
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单片机 串口 总结 修改