51单片机串口发送“?”是怎么回事
11个回答
展开全部
一、51单片机串口概念
1、51单片机的串行口
51单片机的串行口是一个可编程全双工的通信接口,具有UART(通用异步收发器)的全部功能。
2、51单片机的硬件连接
简单双向串口通信有两根数据通信线:
发送端TXD(Transmit Data)
接收端RXD(Receive Data)
TXD和RXD要交叉连接
3、51单片机串口通信的基本结构
51单片机的串行口主要是由两个独立的串行数据缓存器SUBF(一个发送缓存寄存器,一个接收缓存寄存器)和发送控制器、接收控制器、输入移位寄存器及若干控制门电路组成。串行口的基本结构如图所示:
关于SUBF:串口数据缓存寄存器,物理上是两个独立的寄存器,但是占用相同的地址。写操作时,写入的是发送寄存器;读操作时,读出的是接收寄存器。
①:接收端:数据通过RXD接收引脚,再通过移位寄存器将数据转存到接收寄存器中
②:发送端:讲数据从发送寄存器中移出,通过TXD发送引脚将数据发送出去
③:波特率:通过设置定时器1的初值,获取T1溢出率,通过SMOD模式的设置求取波特率
④:中断:通过发送中断标志位或接收中断标志位是否被置位,判断是否进入串口中断程序,在接收数据完成后,会将RI置位,产生一个接收中断;在发送完成后,会将TI置位,产生发送中断
4、传播速率——比特率
比特率是指每秒传送的比特(bit)数。单位为bps(bit per second)也可表示为b/s,比特率越高,单位时间传送的数据量(位数)越大。
5、波特率
在串口通信中,收发双方对发送或接收数据的速率有约定,即双方要有相同的波特率,我们可以通过编程对单片机串行口设定4中工作方式:
其中,T1的溢出率 = 1/T1溢出的时间
①:关于定时器1方式的选择
在说选取定时器1方式之前插一句:这里的定时器1方式2不是串口那4中方式中的方式2;
在学习定时器的相关知识的时候,我们知道定时器有4种不同的工作方式,在串口通信的实验中,我们选择的是定时器1的工作方式2;
定时器T1工作于方式0:溢出所需周期数=8192-x
定时器T1工作于方式1:溢出所需周期数=65536-x
定时器T1工作于方式2:溢出所需周期数=256-x
为什么不选择定时器1的工作方式1:
如果我们使用定时器1的工作方式1在中断中装初值的方法来T1溢出率的话,在进入中断、重装值、出中断这个过程中很容易产生时间上的微小的误差,当多次操作时微小的误差不断累积,终会产生错误;
为什么选择定时器1的工作方式2:
因为方式2为自动重装初值的8位定时器/计数器模式(自动重装载就是在定时器溢出后自动装入设定的初值,这样的好处当然是显而易见的,不需要在中断服务器里手动赋值了,所以可以精确的定时)所以用它来做波特率发生器最恰当。
②:波特率的计算
在上面介绍串口四种方式的时候提到了波特率的计算公式,由公式可以看出,串口方式0和方式2的波特率是固定的;方式1和方式3的波特率是可变的(根据定时器T1的溢出率来控制)
话不多说,根据题来理解:
根据已知波特率,如何计算定时器1方式2下计数寄存器中的初值:
已经波特率 = 9600,系统的晶振频率 = 12Mhz,求给TH1和TL1的初值:
由此可见,当系统的晶振频率为12Mhz时,定时器的初值不是整数;经过计算可得,当晶振频率为11.0592Mhz时,(256-x) = 3;
当时钟频率选用11.0592MHZ时,取易获得标准的波特率,所以很多单片机系统选用这个看起来“怪”的晶振就是这个道理。
6、波特率与比特率关系与区别
码元:在数字通信中常常用时间间隔相同的符号来表示一个 二进制数字 ,这样的时间间隔内的信号称为 (二进制)码元。
在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。即波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示。每秒钟通过信道传输的信息量称为位传输速率,简称比特率。比特率表示有效数据的传输速率。波特率与比特率的关系是比特率=波特率X单个调制状态对应的二进制位数。波特率是传输通道频宽的指标。
二、串口通信有关寄存器
1、数据缓存寄存器
SBUF是可以直接寻址的专用寄存器。物理上,它对应着两个寄存器,即一个发送寄存器一个接收寄存器,CPU写SBUF就是修改发送寄存器;读SBUF就是读接收寄存器。接收器是双缓冲的,以避免在接收下一帧数据之前,CPU未能及时的响应接收器的中断,没有把上一帧的数据读走而产生两帧数据重叠的问题。对于发送器,为了保持最大的传输速率,一般不需要双缓冲,因为发送时CPU是主动的,不会产生重叠问题。
2、电源寄存器PCON
该寄存器是用来管理单片机的电源部分,包括上电复位检测,掉电模式,空闲模式等
在串口通信中,我们仅仅需要关注SMOD这一位
SMOD = 0且串口方式为1、2、3时,波特率正常
SMOD = 1且串口方式为1、2、3时,波特率加倍
3、状态寄存器SCON(98H)
SM0 SM1:工作方式选择位,串行口有四种工作方式,他们由SM0和SM1设定,其对应关系表如下:
SM2:多机通信时的接收允许标志位
REN:允许串行接收位
TB8,RB8:发送接收数据的第9位,当串口工作于方式2或3 时使用到,指向的是串行传输的第9位数据;
1)SM2=0,在方式2或3下,TB8、RB8 发送与接收第9位奇偶校验位;
2)SM2=1,多机通信时的接收允许位,并且在方式2或3下工作;
TI:发送中断标志位,在方式0时,当串行发送第8位数据结束时,或在其他方式,串行发送停止位的开始时,由其内部硬件使TI置1,向CPU发出中断申请。在中断服务程序中,必须用软件将其清 0,取消此中断申请。
RI:接收中断标志位,在方式0时,当串行接收第8位数据结束时,或在其他方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发出中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。
三、串口通信代码
串行口在工作之前,应对其进行初始化,主要是设置产生波特率的定时器1,串行口控制和中断控制。
void usart_init()
{
TMOD = 0x20; //选择定时器1的工作方式2
TH1 = 0xF3; //通过设置定时器1的初值来选择波特率
TL1 = 0xf3;
TR1 = 1; //打开计数器
SCON = 0x50; //0101 0000
PCON = 0x80;
ES = 1; //打开通信中断 ①
EA = 1; //打开总中断 ②
}
或者
void usart_init()
{
TMOD = 0x20;
TH1 = 253;
TL1 = 253;
TR1 = 1;
SM0 = 0;
SM1 = 1;
// REN = 1
// EA = 1;
// ES = 1;
}
在编写串口发送端程序时,无需用到接收数据和中断服务函数,所以REN、EA、ES不需要对他们进行操作
1、51单片机的串行口
51单片机的串行口是一个可编程全双工的通信接口,具有UART(通用异步收发器)的全部功能。
2、51单片机的硬件连接
简单双向串口通信有两根数据通信线:
发送端TXD(Transmit Data)
接收端RXD(Receive Data)
TXD和RXD要交叉连接
3、51单片机串口通信的基本结构
51单片机的串行口主要是由两个独立的串行数据缓存器SUBF(一个发送缓存寄存器,一个接收缓存寄存器)和发送控制器、接收控制器、输入移位寄存器及若干控制门电路组成。串行口的基本结构如图所示:
关于SUBF:串口数据缓存寄存器,物理上是两个独立的寄存器,但是占用相同的地址。写操作时,写入的是发送寄存器;读操作时,读出的是接收寄存器。
①:接收端:数据通过RXD接收引脚,再通过移位寄存器将数据转存到接收寄存器中
②:发送端:讲数据从发送寄存器中移出,通过TXD发送引脚将数据发送出去
③:波特率:通过设置定时器1的初值,获取T1溢出率,通过SMOD模式的设置求取波特率
④:中断:通过发送中断标志位或接收中断标志位是否被置位,判断是否进入串口中断程序,在接收数据完成后,会将RI置位,产生一个接收中断;在发送完成后,会将TI置位,产生发送中断
4、传播速率——比特率
比特率是指每秒传送的比特(bit)数。单位为bps(bit per second)也可表示为b/s,比特率越高,单位时间传送的数据量(位数)越大。
5、波特率
在串口通信中,收发双方对发送或接收数据的速率有约定,即双方要有相同的波特率,我们可以通过编程对单片机串行口设定4中工作方式:
其中,T1的溢出率 = 1/T1溢出的时间
①:关于定时器1方式的选择
在说选取定时器1方式之前插一句:这里的定时器1方式2不是串口那4中方式中的方式2;
在学习定时器的相关知识的时候,我们知道定时器有4种不同的工作方式,在串口通信的实验中,我们选择的是定时器1的工作方式2;
定时器T1工作于方式0:溢出所需周期数=8192-x
定时器T1工作于方式1:溢出所需周期数=65536-x
定时器T1工作于方式2:溢出所需周期数=256-x
为什么不选择定时器1的工作方式1:
如果我们使用定时器1的工作方式1在中断中装初值的方法来T1溢出率的话,在进入中断、重装值、出中断这个过程中很容易产生时间上的微小的误差,当多次操作时微小的误差不断累积,终会产生错误;
为什么选择定时器1的工作方式2:
因为方式2为自动重装初值的8位定时器/计数器模式(自动重装载就是在定时器溢出后自动装入设定的初值,这样的好处当然是显而易见的,不需要在中断服务器里手动赋值了,所以可以精确的定时)所以用它来做波特率发生器最恰当。
②:波特率的计算
在上面介绍串口四种方式的时候提到了波特率的计算公式,由公式可以看出,串口方式0和方式2的波特率是固定的;方式1和方式3的波特率是可变的(根据定时器T1的溢出率来控制)
话不多说,根据题来理解:
根据已知波特率,如何计算定时器1方式2下计数寄存器中的初值:
已经波特率 = 9600,系统的晶振频率 = 12Mhz,求给TH1和TL1的初值:
由此可见,当系统的晶振频率为12Mhz时,定时器的初值不是整数;经过计算可得,当晶振频率为11.0592Mhz时,(256-x) = 3;
当时钟频率选用11.0592MHZ时,取易获得标准的波特率,所以很多单片机系统选用这个看起来“怪”的晶振就是这个道理。
6、波特率与比特率关系与区别
码元:在数字通信中常常用时间间隔相同的符号来表示一个 二进制数字 ,这样的时间间隔内的信号称为 (二进制)码元。
在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。即波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示。每秒钟通过信道传输的信息量称为位传输速率,简称比特率。比特率表示有效数据的传输速率。波特率与比特率的关系是比特率=波特率X单个调制状态对应的二进制位数。波特率是传输通道频宽的指标。
二、串口通信有关寄存器
1、数据缓存寄存器
SBUF是可以直接寻址的专用寄存器。物理上,它对应着两个寄存器,即一个发送寄存器一个接收寄存器,CPU写SBUF就是修改发送寄存器;读SBUF就是读接收寄存器。接收器是双缓冲的,以避免在接收下一帧数据之前,CPU未能及时的响应接收器的中断,没有把上一帧的数据读走而产生两帧数据重叠的问题。对于发送器,为了保持最大的传输速率,一般不需要双缓冲,因为发送时CPU是主动的,不会产生重叠问题。
2、电源寄存器PCON
该寄存器是用来管理单片机的电源部分,包括上电复位检测,掉电模式,空闲模式等
在串口通信中,我们仅仅需要关注SMOD这一位
SMOD = 0且串口方式为1、2、3时,波特率正常
SMOD = 1且串口方式为1、2、3时,波特率加倍
3、状态寄存器SCON(98H)
SM0 SM1:工作方式选择位,串行口有四种工作方式,他们由SM0和SM1设定,其对应关系表如下:
SM2:多机通信时的接收允许标志位
REN:允许串行接收位
TB8,RB8:发送接收数据的第9位,当串口工作于方式2或3 时使用到,指向的是串行传输的第9位数据;
1)SM2=0,在方式2或3下,TB8、RB8 发送与接收第9位奇偶校验位;
2)SM2=1,多机通信时的接收允许位,并且在方式2或3下工作;
TI:发送中断标志位,在方式0时,当串行发送第8位数据结束时,或在其他方式,串行发送停止位的开始时,由其内部硬件使TI置1,向CPU发出中断申请。在中断服务程序中,必须用软件将其清 0,取消此中断申请。
RI:接收中断标志位,在方式0时,当串行接收第8位数据结束时,或在其他方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发出中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。
三、串口通信代码
串行口在工作之前,应对其进行初始化,主要是设置产生波特率的定时器1,串行口控制和中断控制。
void usart_init()
{
TMOD = 0x20; //选择定时器1的工作方式2
TH1 = 0xF3; //通过设置定时器1的初值来选择波特率
TL1 = 0xf3;
TR1 = 1; //打开计数器
SCON = 0x50; //0101 0000
PCON = 0x80;
ES = 1; //打开通信中断 ①
EA = 1; //打开总中断 ②
}
或者
void usart_init()
{
TMOD = 0x20;
TH1 = 253;
TL1 = 253;
TR1 = 1;
SM0 = 0;
SM1 = 1;
// REN = 1
// EA = 1;
// ES = 1;
}
在编写串口发送端程序时,无需用到接收数据和中断服务函数,所以REN、EA、ES不需要对他们进行操作
展开全部
51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机都会进入串口中断处理程序。在中断程序中,要区分出来究竟是发送引起的中断,还是接收引起的中断,然后分别进行处理。看到过一些书籍和文章,在串口收、发数据的处理方法上,很多人都有不妥之处。接收数据时,基本上都是使用“中断方式”,这是正确合理的。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
看了一些网友编写的程序,发现有如下几条容易出错:
1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。这种处理方法,就会遗漏收到的数据。
2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。但是,却在中断函数中,将 TI 清零! 这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
看了一些网友编写的程序,发现有如下几条容易出错:
1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。这种处理方法,就会遗漏收到的数据。
2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。但是,却在中断函数中,将 TI 清零! 这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机都会进入串口中断处理程序。在中断程序中,要区分出来究竟是发送引起的中断,还是接收引起的中断,然后分别进行处理。看到过一些书籍和文章,在串口收、发数据的处理方法上,很多人都有不妥之处。接收数据时,基本上都是使用“中断方式”,这是正确合理的。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
看了一些网友编写的程序,发现有如下几条容易出错:
1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。这种处理方法,就会遗漏收到的数据。
2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。但是,却在中断函数中,将 TI 清零! 这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
看了一些网友编写的程序,发现有如下几条容易出错:
1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。这种处理方法,就会遗漏收到的数据。
2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。但是,却在中断函数中,将 TI 清零! 这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
51单片机内部串行口的结构以及它是如何发送数据和接收数据的实际上并不是特别难,如果还是没有特别理解的话可以去找一本单片机的教材,一般在教材当中也会对电路做一个具体详细的描述,先理解串行口的工作机制,然后才能够去做典型的应用,否则的话一旦在做串行通信的时候出现问题的时候就不容易解决,而且很多朋友在网上找的程序,有的是使用的查询方式,有的是使用的中断方式,有的是查询和中断混合再用,就解决不了,因为有的地方,有的程序写的不好的时候,它既有查询也有中断,有朋友就很困惑,特别是发送和接收,如果是同时使用中断和查询,这个时候数据一定会出错,有的时候可能会发送两次,有的时候可能会接收出错就解决不了,所以一定要先理解他内部的结构。
单片机
要想发送数据,就是把数据写到SBUF当中,靠发送器首先就把这个数据转成串行的,当然也是靠移位寄存器,转成串行的数据,一位一位的转成这个串行,然后会给它自动的加一个起始位,在末尾会再加一个停止位,然后出现在这根线上变成高低电平,当这一帧数据一旦发送到停止位的时候,也都发送完的时候,会使这个T硬件自动置一,就可以查询这个标志位代表发送完了,如果这个没有变成一,就代表还没有发送完,一定要等他发送完才能够再发送下一帧数据,所以大家写程序的时候也会这样子来查询标志位,来等待发送完再去发送下一数据,在接收的时候也是一样的,一位一位地过来它会进入这个移位寄存器,靠接收控制器会把这个数据一位一位地给他移到SBUF当中,他会把这个起始位和停止位都会去掉,只保留数据位放到这个里面供用户去读取
单片机
要想发送数据,就是把数据写到SBUF当中,靠发送器首先就把这个数据转成串行的,当然也是靠移位寄存器,转成串行的数据,一位一位的转成这个串行,然后会给它自动的加一个起始位,在末尾会再加一个停止位,然后出现在这根线上变成高低电平,当这一帧数据一旦发送到停止位的时候,也都发送完的时候,会使这个T硬件自动置一,就可以查询这个标志位代表发送完了,如果这个没有变成一,就代表还没有发送完,一定要等他发送完才能够再发送下一帧数据,所以大家写程序的时候也会这样子来查询标志位,来等待发送完再去发送下一数据,在接收的时候也是一样的,一位一位地过来它会进入这个移位寄存器,靠接收控制器会把这个数据一位一位地给他移到SBUF当中,他会把这个起始位和停止位都会去掉,只保留数据位放到这个里面供用户去读取
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机都会进入串口中断处理程序。在中断程序中,要区分出来究竟是发送引起的中断,还是接收引起的中断,然后分别进行处理。看到过一些书籍和文章,在串口收、发数据的处理方法上,很多人都有不妥之处。接收数据时,基本上都是使用“中断方式”,这是正确合理的。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。这时,处理不好的话,就可能带来问题。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询