单片机串口总结修改版.docx

上传人:b****2 文档编号:23055498 上传时间:2023-04-30 格式:DOCX 页数:26 大小:32.13KB
下载 相关 举报
单片机串口总结修改版.docx_第1页
第1页 / 共26页
单片机串口总结修改版.docx_第2页
第2页 / 共26页
单片机串口总结修改版.docx_第3页
第3页 / 共26页
单片机串口总结修改版.docx_第4页
第4页 / 共26页
单片机串口总结修改版.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

单片机串口总结修改版.docx

《单片机串口总结修改版.docx》由会员分享,可在线阅读,更多相关《单片机串口总结修改版.docx(26页珍藏版)》请在冰豆网上搜索。

单片机串口总结修改版.docx

单片机串口总结修改版

第一篇:

单片机串口总结

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

//将单片机检测到

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 行政公文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1