51单片机串口中断功能的设置

单片机 专栏收录该内容
4 篇文章 0 订阅

51单片机的串口功能就是和外界进行通讯,所谓的“外界”也就是与单片机进行交互的媒介,最常用的就是我们经常使用的计算机、平板或者其他设备(比如另一个单片机配合显示模块使用)。

既然和外界需要“交互”,就必须使用“中断”功能,所以一般串口和中断是配合使用的。

在总结串口使用方法之前需要对一些基本概念进行理解

第一. 关于波特率(baud rate)的解释,网上有很多文章,以我个人的肤浅理解觉得说得是这样一回事儿:

西方某大国正在经历总统换届选举,选来选去没有选出个像样的,公民们有意见,非要到国会进行“抗议(0元购)”,一下子来了7680口,有的人还拿着枪,有长的有短的,还有扛炮的!眼看着要乱,警察和抗议者达成协议,进来可以但是为了保证秩序,不能一下子都进来,需要分批进入,根据法律,拿枪的也不犯法,为了保证安全,需要对进场的人进行监视,尤其是带枪的(只限短款,扛炮的就先等等吧,几百年了这地方就被烧了一回,这次得谨慎点)。规定:每组只限2个人,每批包含4组,每组相距等同的距离,一次只能进入1批。根据带枪与否划分“风险级别”,两个人都带枪入场的为最高风险,两个人都不带枪的为最低风险,两个人其中之一带枪且带枪的人走在前面的为次级风险,两个人其中之一带枪且带枪的人走在后面的为三级风险。同时,不能光进不出,前门进后面出,进多少就得出多少,后门根据前门进入的人数统计需要出去的人数,每批之间的间隔时间并不固定,谁还没想在议长的椅子上坐会儿呢?

上面的例子中那2个人中的每个人就可以看成是计算机系统中的“位”,而2个人为一组就可以看成是通信领域中的“码元”,因为带枪与否是两种状态,所以共组成了四种状态,就是例子中的“风险级别”。

好了,现在有了这些概念就可以研究下面的概念了:

位:计算机系统中一个二进制数,0或者1,上面的例子中那2个人中的每个人就可以看成是计算机系统中的“位”

码元:一个二进制数是由若干个“位”构成的,而码元就是这个指定的二进制数,也就是说码元包含若干个二进制位,可以是1位,2位,或者3位,至于到底是几位是根据功能要求人为指定的,叫做“调制”,码元是传输信息的基本单位。上面的例子中每个小组就是一个“码元”

字节(字符):计算机系统存储单元的一个基本单位就是字节,一个字节包含8个位(bit),上面的例子中每1批就是一个字节,每批包含4组总共8个人

帧:通用异步接收发送方式(UART)中把特定的一组二进制数称为“帧”,这组特定的二进制数是由“开始位”,“数据位”,“奇偶校验位”,“停止位”依次连接而成的,等同于上面例子中的每批,在进出“国会”的时候,需要前后被两个警员夹在中间,前面一个警员就是“开始位”,末尾一个警员就是“停止位”,这里省去“奇偶校验位”,这“串”起来的2个警员再加上这1批中的8个人就是1“帧”。

比特率:单位时间内传输的二进制位的数量,单位 bit/s,等同于上面例子中统计每小时总共进去多少人

调制速率:单位时间传输码元的数量,等同于上面例子中统计每小时总共进去多少组

波特率:应该被称作调制速率,简称调制率,单位是波特(baud),可见,所谓波特率就是调制速率,对应的也是上面例子中的组数

公式:D=调制速率(波特率),R=比特率,L=每个码元中的比特(位)数,M=码元的状态数(上面的例子,带枪和不带枪2种情况(位)就构成4种风险级别(状态))

D=\frac{R}{L}=\frac{R}{\log_2M}

调制速率(波特率)与比特率的关系

比特率 = 调制速率(波特率)x 码元(单个调制状态)对应的二进制位数

不同的码元可以包含不同的位数,位数越多能分辨的不同状态的数量就越多,例如如果码元包含1个位,则只能分辨2种状态,如果码元包含2个位,则共能分辨4种状态,如果是包含3位呢,就共能分辨8种状态,可见,包含的位数越多,状态的分辨率越高,其实状态的总数就是2的N次方,N就是码元中包含的“位”数。至于一个码元要包含多少个二进制位,根据功能由设计者指定(你说了算!技术学到这儿是不是有点当家做主的感觉?)。

如果码元就包含1个二进制位,则比特率就等于调制速率(波特率),被称为“两相调制”;如果码元就包含2个二进制位,共四种状态,则比特率就等于调制速率(波特率)X2,被称为“四相调制”;如果码元就包含3个二进制位,共8种状态,则比特率就等于调制速率(波特率)X3,被称为“八相调制”。

最后问个问题,总共7680名抗议者要“有序抗议”国会,要求一天8小时“抗议完毕”,那么每小时要进出多少人?又是多少组呢?总共7680名抗议者,每组2人,每批4组,每批配备2名警员,每批一共10人,要让7680名抗议者“有序抗议”国会,配上警员的人数总共进出的人数达到9600(这个数字熟悉吗?),8小时抗议完毕,每小时要进出1200人,每小时600组,每小时60批(加上警员),结果中的1200人就等同于“位”的概念,600组就等同于“调制速率(波特率)”的概念,60批就等同与“字节(字符)”的概念

第二. 有了前面的“国会抗议”事件,相信大家对比特率,波特率,调制速率都有了个了解,接下来的问题是:在异步串口通讯中为什么要保证发送方与接收方的波特率相同,答案包括两个方面:

(1)单片机的“调制速率(波特率)”是如何确定的
         51 单片机中的“调制速率(波特率)”就是比特率,即每秒传送的位数,单位bit/s(位/秒)

(2)“调制速率(波特率)”设置的意义
         单片机工作时必须根据内部或者外部设置的时钟基准对多种任务进行协调,对于串口通讯的操作同样少不了使用时钟基准作为时间参照,这个问题涉及到“采样”的概念,请参考博文串口波特率问题的处理,里面讲得很清楚,其实就是为了同步采样频率。另外,从文中也可以推算出“波特率”与“采样频率”、“机器频率”、“振荡频率”的关系,首先,“采样频率”=16X“波特率”,“采样频率”=“机器频率”,如果以12分频(12T)为准,“振荡频率”=12X“机器频率”,所以“振荡频率”=12X16X“波特率”;从周期的角度来看,“波特周期”=16X“采样周期”,“采样周期”=“机器周期”,“机器周期”(12T分频)=12X“震荡周期”,所以“波特周期”=16X12X“振荡周期”,明白了各个频率之间的关系就自然能理解后面介绍的初值问题

第三. 51单片机异步串行通讯(UART)功能的使用

与51单片机其他功能的实现类似,要使用串口中断功能就必须包含三个步骤:1. 设置+赋值  2. 中断设计

设置和赋值操作因单片机型号的不同而不完全相同,但是原理是相似的,举一反三即可。这里用到的芯片是STC12C2052AD

(1)设置+赋值

这一步就是设置与串口和中断功能相关的寄存器以指定单片机的工作状态

设置“辅助寄存器(Auxiliary Register)” AUXR
AUXR可以设置定时器、串口传输的分频,这一设置决定了“机器频率”与“振动频率”的关系

其中通过设置“T0x12”和“T1x12”分别设置定时器T0和T1的分频,默认为0即12分频
其中通过设置“UART_M0x6”设置串口的分频,默认为0即12分频

 

设置“串口控制寄存器(Serial Control Register)”SCON
通过设置SCON可以指定波特率发生器的时钟基准


SM0/SM1/SM2三个位的设置可以指定波特率发生器的时钟基准

SM0SM1模式描述波特率时钟基准
000半双工状态,发送接收8位数据,使用移位寄存器f_osc/12
011全双工状态,发送10位数据定时器1的溢出率/PCON寄存器SMOD位
102全双工状态,发送11位数据f_osc/16或者f_osc/32
113全双工状态,发送11位数据定时器1的溢出率/PCON寄存器SMOD位








SM0(serial mode bit0):串口模式0移位寄存器
SM1(serial mode bit1):串口模式1,8位通用异步接收可变时钟基准寄存器
SM2(serial mode bit0):多处理器协同通讯设置位,
                                           在模式0下SM2无效,配置模式0时SM2应该设置为0
                                           在模式1下,SM2用来检测接收过程中的有效“结束位”,当SM2=1时,仅当检测到有效“结束位”后,RI才被激活
                                           在模式2和3下,SM2用来激活“多处理器协同通讯”
REN(reception enable):串口接收功能的使能位,REN=1:允许串口接收;REN=0:禁止串口接收
TB8:在模式2和3中,TB8用来存储发送数据的第9位
RB8:在模式2和3中,RB8用来存储接收数据的第9位
TI(transimission interrupt):发送中断标志位,在模式0中,当第8位发送后由硬件置1;在模式1~3中,当“结束位”发送后由硬件置1,TI必须由软件置0
RI(reception interrupt):接收中断标志位,在模式0中,当第8位接收后由硬件置1;在模式1~3中,当“结束位”接收后由硬件置1,RI必须由软件置0
模式0:以固定的波特率工作在半双工状态
             通过RxD管脚发送和接收8位数据,在数据发送与接收过程中通过TxD管脚输出移位时钟,波特率设置为12分之1的振荡频率
模式1:以可变的波特率工作在全双工状态
             传输过程中数据为10位,1个起始位+8个数据位+1个结束位,在数据接收过程中,结束位被存储在RB8位中
             波特率的值取决于定时器1的溢出率和“电源控制寄存器”中SMOD位的设置值(这部分在后面介绍初值问题时有讲解)
模式2:以固定的波特率工作在全双工状态
             传输过程中数据为11位,1个起始位+8个数据位+1个可编程位+1个结束位,在数据接收过程中,可编程位被存储在RB8位中,
             在数据发送过程中,可编程位被存储在TB8位中
             波特率的值为32分之1的振荡频率或者16分之1的振荡频率,取决于“电源控制寄存器”中SMOD位的设置值
模式3    以可变的波特率工作在全双工状态(波特率)
             此模式与模式2相同,只是波特率是可变的,波特率的值取决于定时器1的溢出率和“电源控制寄存器”中SMOD位的设置值

 

设置“定时器模式控制寄存器(Time Mode Control Register)” TMOD
当在SCON寄存器设置了模式1或者3时则指定定时器1作为波特率发生器的时间基准,因此需要通过TMOD对定时器1进行设定

TMOD各个位的设置意义不在此处详谈,可以参考数据手册上对定时器设置的内容
一般使用定时器1工作在模式2作为波特率发生器(高四位的设置:GATE=0, C/T=0, M1=1, M0=0, 第四位设置为0)模式2为“自动填装定时器”,自动填装的意思就是用定时器的低8位TL1计时,当溢出时将高8位TH1的数值自动填装到低8位TL1
(TH1:定时器1的高8位;TL0:定时器1的低8位)

 

问题:“填装”的是什么值?
这就要引出定时器的初值设置问题,至于什么是初值和实现怎样的功能请参考之前我写的一篇文章,51单片机硬件定时器设定的初值问题,如果知道了初值的概念和怎样设置初值,就应该考虑定时器、初值和波特率的关系以便正确使用串口功能,之前讲过,51 单片机中的“调制速率(波特率)”就是比特率,即每秒传送的位数,单位bit/s(位/秒),为了保证传输的正确性,每个位又被采样分频设置成若干个检测点,也就是说1个位被检测若干次,取其中指定的几次进行比较以确定数据传输是正确的,这样的采样分频设置可以是16采样(每位检测16次)、32采样(每位检测32次)或者64采样(每位检测64次),而每一个检测点即采样点就意味着定时器需要计数溢出一次,这里注意每个检测点不是定时器计数一次,而是计数溢出一次,而定时器计多少数后溢出就需要我们为定时器指定“初值”,这就是指定“初值”的原因!如果采用32次采样设置,波特率和定时器计数溢出频率的关系就可以用如下公式表示

Baud=\frac{f_{overflow}}{32}

Baud:波特率,这个值是预先知道的,我们可以在各种波特率值中选择一个(问题又来了,为什么波特率是选择出来的,而不是自己指定的?大家可以自行寻找答案)

f_overflow:定时器的计数溢出频率

当设置相应寄存器用定时器1的模式2作为波特率发生器时,使用的是低8位作为定时器,高8位和低8位在最初都被设置为初值,但高8位不参与计时,只是存储初值(当低8位计时溢出时,高8位自动再将初值传送给低8位,这样每次就不用重复设置初值了)。低8位作为定时器从初值开始计数直到超过最大值255时就发生了1次溢出,溢出后,存储在高8位的初值自动加载到低8位中重新开始计数,这样就可以知道在单位时间内(1秒)定时器一共“溢出”多少次,就是“溢出率”即f_overflow。既然提到“率”就必须和时间联系在一起,既然溢出率就是溢出的次数,而溢出的次数又是定时器计数造成的,因此就必须知道定时器每1次计数用了多少时间,在51单片机中,定时器每计数一次所经过的时间就是一个机器周期(看到这儿也应该理解“机器周期”的概念了),所以从计时到溢出所用的时间就等于T=NxM,N=从计时开始到溢出总共计数的次数,就是255-初值,其中255是8位定时器最大计数值;M=每次计数所用的时间就是1个机器周期,所以单位时间的溢出次数即溢出率 f_overflow = 1/NxM,到这里就可以建立波特率和初值、机器周期的关系

Baud = \frac{1}{32\cdot N\cdot M}

定时器的计数频率即机器频率又与时钟基准频率(振荡频率)有关,这样又把波特率和时钟基准频率建立了联系,公式如下

Baud=\frac{1}{32\cdot N }\cdot \frac{1}{M}=\frac{1}{32\cdot N }\cdot \frac{f_{osc}}{12}=\frac{f_{osc}}{32\cdot 12\cdot N }

Baud:波特率

f_osc:时钟基准频率,也是振荡频率,例如使用外部晶振12MHz,那么f_osc就是12MHz

M=每次计数所用的时间就是1个机器周期,倒数就是机器频率,而定时器机器频率(f_timer)=振荡频率(f_osc)/12,原因是系统采用了12分频,意思是外部晶振每振动12次,定时器计数1次,为什么采用12分频而不是其他值呢?其实这个分频数可以选择但不能随意指定,可以选择例如2分频,4分频,8分频,或者其他值,选择分频可以通过指定相应的寄存器值来实现,这里不展开,可以参考相应的技术手册。选择不同的分频意味着使用不同的速度,分频数越高,速度越慢,想要提高速度可以选择1分频,还记得之前介绍的那个AUXR寄存器吗?就是用来干这个事情的

在实际计算波特率的初值时,还需要考虑另外一个参数,就是SMOD,这个参数是一个叫“电源控制寄存器(PCON)”中的一个位,通过设置SMOD,可以实现不同波特率的取值

 

设置“电源控制寄存器(Power Control Register)” PCON

SMOD=0,波特率以32分频(即每一位采样32次)(以32分频为例,具体参考技术手册)
SMOD=1,波特率采样频率提高1倍

最终波特率公式如下

Baud=\frac{2^{smod}\cdot f_{osc}}{32\cdot 12\cdot N }=\frac{2^{smod}\cdot f_{osc}}{32\cdot 12\cdot \left (255-TH1 \right ) }

Baud:波特率

f_osc:时钟频率即振荡频率

TH1:存储在定时器1高8位的初值

SMOD:PCON寄存器的SMOD位(0:32分频;1:16分频)

在实际计算中波特率是事先选择的定值,例如4800,9600等,通过上面公式视为了计算初值,例如波特率选择为 4800 bit/s,使用12分频的外部12MHz晶振,SMOD=1,则初值 TH1约等于243

到这里就把选择指定波特率并且利用定时器1作为波特率发生器的设置和初值计算问题说完了

为了正确使用串口还不得不用到中断处理,当系统接收到或者发送出需要的数据时,通过中断处理就可以实现系统和外界的交互,因此还必须设置和中断相关的寄存器

 

设置“中断使能寄存器(Interrupt Enable Register)” IE

中断使能寄存器(IE)可以按位赋值,所谓“按位”的意思就是可以单独指定EA的值,比如形如 EA=xxx,所谓“使能”就是开关的意思
EA:总中断使能,想要使用中断功能,就必须将EA设置为1,否则任何中断都不能用
ES:串口中断使能,想要使用串口中断功能,就必须将ES设置为1
EPCA_LVD:PCA模块、低压检测中断
EADC_SPI:ADC、SPI中断
ET1:定时器1的中断使能
EX1:外部中断 INT1 使能
ET0:定时器0的中断使能
EX0:外部中断 INT0 使能

需要使用串口功能时,需要将 EA设置为1,将 ES设置为1

 

(2)中断

除了几个必须要进行设置的特殊功能寄存器外,还有一个寄存器也比较重要,就是SBUF,全程“串口数据缓冲器(Serial Data Buffer)”在通讯中无论是外界向单片机通过串口写入数据,还是外界通过串口读取单片机中的数据,都必须使用SBUF作为中转站,在使用时,把SBUF放置在中断处理函数中

另外,在中断处理函数中需要指定中断类型为“串口中断”,通过指定“中断代号码”来实现,因为系统中断可以不同的处理程序触发的,比如定时器,外部中断,所以需要指定是哪个处理程序触发的,采用中断号来区分,实现串口中断处理程序的中断号为 4。

串口中断处理函数的格式是  void 函数名() interrupt 4 {中断处理程序}

中断处理程序的编写包括
1. SBUF赋值
2. 发送标志TI、接收标志RI在程序中清0(单片机发送数据后TI自动置1,单片机接收数据后RI自动置1,在程序中清0后等待下一次发送、接收数据)

具体的程序示例我就不再写了,网上有很多,这里主要是把关于串口中断功能的相关设置总结一下,同时也就几个概念作出解释以便理解,本人也是初学者,如果有误还请见谅,指出,一同进步。

 

 

  • 7
    点赞
  • 2
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值