【51单片机】7. 串口通信、单片机向电脑发送数据电脑发送数据点亮LED灯Demo
1. 串口的定义
-
串口成本低、易使用、通信线路简单,可实现两个设备互相通信
-
单片机串口可以使单片机与各式各样的模块相互通信,极大扩展了单片机应用范围,增强单片机系统硬件实力
-
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机串口通信
2. 硬件电路
-
简单双向串口通信有两根通信线(发送端TXD和接收端RXD)
-
TXD与RXD交叉连接
-
仅需单项数据传输可以只接一根通信线
-
电平标准不一致时需要加电平转换芯片
3. 电平标准
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用电平标准包括:
-
TTL电平:+5V表示1,0V表示0(单片机使用)
-
RS232电平:-3-15V表示1,+3+15V表示0
-
RS485电平:两线压差+2+6V表示1,-2-6V表示0(差分信号)
TTL&RS232只能传10多米后错误率就很高了,RS485可以达到千米级
4. 常见通信接口
-
全双工:通信双方可以在同一时刻相互传输数据
-
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线(同一时刻只能一个人发)
-
单工:通信只能有一方发送到另一方,不能反向传输
-
异步:通信双方各自约定通信速率
-
同步:通信双方靠一根时钟线约定通信速率
如某一方1s传一个,但对方0.5s接收一个,就不能达成一致,同步就通过一根时钟线约定通信速率
异步就是双方各自计时,到时间了收/发
- 总线:链接各个设备的数据传输线路(类似一条马路将多个住户连接起来,使得住户可以相互交流)
5. 串口参数及时序图
-
波特率:串口通信速率(发送和接收各数据位间隔时间)
-
校验位:用与数据验证
-
停止位:用于数据帧间隔
9位相较而言,多出的那一位可以用于校验(奇偶校验)
6. 串口模式图
SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用的是相同的地址。写操作时写入发送寄存器,读操作时读出的是接收寄存器。
进入中断逻辑后是一个或门,只要有1个满足就会触发对应的中断
7. 串行口相关寄存器
-
SBUF是就是6中说的缓冲区
-
SCON是串行控制器(可位寻址),配置SM0和SM1可以控制不同工作方式:
-
方式0:SM0=0,SM1=0;同步移位串行方式:移位寄存器
-
方式1:SM0=0,SM1=1;8位UART,波特率可变
-
方式2:SM0=1,SM1=0;9位UART
-
方式3:SM0=1,SM1=1;9位UART,波特率可变
-
REN:允许/禁止串行接收控制位。REN=1允许串行接收,REN=0禁止接收
-
TI:发送中断请求标志位,在方式2中,停止位开始发送由内部硬件置位,必须使用软件复位
-
-
PCON是电源控制寄存器,里面的SMOD是波特率选择位
-
SMOD=1使通信方式1、2、3波特率加倍;SMOD=0各工作方式波特率加倍,复位时SMOD=0
-
SMOD0是帧错误检测有效控制位,SMOD0=1时,SCON中的SM0/FE用于帧错误检测;SMOD0=0时,SCON中的SM0/FE用于和SM1一起制定串口工作方式
-
8. 八位自动重载和十六位的区别
十六位记的数多,但每次都需要自己写的代码赋初值(参考定时器那一章的代码,每次溢出中断我们都需要重新给TH和TL赋初值),浪费时间。双八位就是将十六位分开,一个计数,另一个存放初值,每次计数完成后AR会自动将值赋给CNT,不用代码处理,比较快,但只有八位所以记的数少了。
串口通信设置八位自动重载,这样就不需要做手动设置了
9. 单片机通过串口向电脑发送数据
从波特率计算器中生成波特率设置相关的代码
接着写主代码:
#include <REGX52.H>
#include "Delay.h"unsigned char sec = 0;void UART_Init()
{SCON = 0x40; // 模式 -> 0 1,8位UART,波特率可变PCON = 0x80; // SMOD置1表示波特率加倍TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xF4; //设定定时初值TH1 = 0xF4; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}void UART_SendByte(unsigned char byteData)
{SBUF = byteData; // 填充缓冲区SBUFwhile (TI == 0) ; // 当没有到发送的时候就while等待TI = 0; // 根据规则,手动置0(手册P235)
}void main()
{UART_Init(); // 上电初始化while(1){UART_SendByte(sec); // 自动发送sec++; // sec++Delay(1000);}
}
Delay.c:
#include <INTRINS.H>void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}
Delay.h:
#ifndef __DELAY_H__
#define __DELAY_H__void Delay(unsigned int ms);#endif
效果如下(见接收缓冲区数字):
(使用串口助手时,注意要选对串口,设置相同的波特率,否则会出错)
10. 串口代码模块化
常用代码可以直接模块化
UART.c
#include <REGX52.H>/*** @brief 串口初始化* @param 无* @retval 调用后初始化串口*/
void UART_Init() // 4800bps@11.0592MHz
{SCON = 0x40; // 模式 -> 0 1,8位UART,波特率可变,注意,这里REN置0表示的是不接受数据,REN置1后是0x50PCON = 0x80; // SMOD置1表示波特率加倍TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xF4; //设定定时初值TH1 = 0xF4; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}/*** @brief 经串口发送数据* @param byteData表示发送的数据* @retval 发送数据范围0~255*/
void UART_SendByte(unsigned char byteData)
{SBUF = byteData; // 填充缓冲区SBUFwhile (TI == 0) ; // 当没有到发送的时候就while等待TI = 0; // 根据规则,手动置0(手册P235)
}
UART.h
#ifndef __UART_H__
#define __UART_H__void UART_Init();
void UART_SendByte(unsigned char byteData);#endif
11. 电脑向单片机发送数据控制灯亮
单片机接收数据需要中断,那么在初始化时需要将中断打开,同时REN位要置1才能接收数据,UART.c修改如下,主要是改了初始化的函数:
#include <REGX52.H>/*** @brief 串口初始化* @param 无* @retval 调用后初始化串口*/
void UART_Init() // 4800bps@11.0592MHz
{SCON = 0x50; // 模式 -> 0 1,8位UART,波特率可变,REN=1接收数据PCON = 0x80; // SMOD置1表示波特率加倍TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xF4; //设定定时初值TH1 = 0xF4; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1// 启用接收中断ES = 1;EA = 1;
}/*** @brief 经串口发送数据* @param byteData表示发送的数据* @retval 发送数据范围0~255*/
void UART_SendByte(unsigned char byteData)
{SBUF = byteData; // 填充缓冲区SBUFwhile (TI == 0) ; // 当没有到发送的时候就while等待TI = 0; // 根据规则,手动置0(手册P235)
}
main.c中写中断的处理
#include <REGX52.H>
#include "UART.h"
#include "Delay.h"void main()
{UART_Init();while(1){}
}void UART_Rountine(void) interrupt 4
{if (RI == 1) // 因为发送和接受触发的是同一个中断,需要把发送和接收区分开{// if 是接收中断的话P2 = ~SBUF; // 点灯UART_SendByte(SBUF); // 利用发做回复RI = 0; // 该标志位需要软件置1}
}
取反是因为低电平点灯。
效果如下:
12. 波特率计算
根据红框部分计算:
以江科大视频的为例,他是12MHz的晶振,选择SMOD=1(波特率倍速)。
根据软件给TH1和TL1设置的初值看:
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
F4转10进制是243,离溢出有13,即计13μs溢出一次
则溢出率是1/13μs=13MHz=0.07692MHz
又连接到÷16:0.07692/16=0.00480769MHz=4807.69Hz(12MHz的晶振有误差)
所以这个初值就是根据这个计算方式反向算出来的
13. HEX模式与文本模式
说白了文本模式就是根据你输入的16进制内容转成ASCII码
根据11的代码及ASCII码,我们将单片机回传的结果显示为文本模式,结果如下:
从而可以传输一些非数字的内容,同理,用电脑给单片机发字符“B”,单片机也会根据对应的16进制点灯,并回传对应的16进制内容: