一、目标
- 单片机通过串口向电脑发送数据(数字递增)
- 电脑通过串口控制单片机上的LED
二、基本概念
1. 串口
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围,增强了单片机系统的硬件实力。
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
2. 硬件电路
简单双向串口通信有两根通信线:发送端(Transmit Exchange Data, TXD)和接收端(Revieve Exchange Data, 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(差分信号)
4. 引脚接口及定义(DB9)
现在笔记本电脑上都没有这个接口了,都用USB接口。
5. 常见通信接口比较
名称 | 引脚定义 | 通信方式 | 特点 |
---|---|---|---|
UART | TXD、RXD | 全双工、异步 | 点对点通信 |
I²C | SCL、SDA | 半双工、同步 | 可挂载多个设备 |
SPI | SCLK、MOSI、MISO、CS | 全双工、同步 | 可挂载多个设备 |
1-Wire | DQ | 半双工、异步 | 可挂载多个设备 |
此外还有:CAN、USB等
-
全双工:通信双方可以在同一时刻互相传输数据
-
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
-
单工:通信只能有一方发送到另一方,不能反向传输
-
异步:通信双方各自约定通信速率
-
同步:通信双方靠一根时钟线来约定通信速率
-
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
6. 51单片机的UART
STC89C52有1个UART
STC89C52的UART有四种工作模式:
模式0:同步移位寄存器
模式1:8位UART,波特率可变(常用)
模式2:9位UART,波特率固定
模式3:9位UART,波特率可变
TXD接RXD,RXD接TXD
7. 串口参数及时序图
- 波特率:串口通信的速率(发送和接收各数据位的间隔时间)
- 检验位:用于数据验证
- 停止位:用于数据帧间隔
8. 串口模式图
SBUF:串口数据缓存寄存器,物理上是两个独立的寄存器,但占用相同的地址。写操作时,写入的是发送寄存器,读操作时,读出的是接收寄存器
9. 串口和中断系统
在简单情况下,控制ES、EA即可控制中断。
10. 串口相关寄存器
三、单片机通过串口向电脑发送数据
新建项目“8-1 串口向电脑发送数据”,新建main.c。
把之前的Delay.c和Delay.h复制到本项目文件夹中。
SCON:串行控制寄存器(可位寻址)
其中SM0、SM1按下列组合确定串行口的工作方式:
REN:允许/禁止串行接收控制位。由软件置位REN,即REN=1为允许串行接收状态,可启动串行接收器RxD,开始接收信息。软件复位REN,即REN=0,则禁止接收。
TI:发送中断请求标志位。在方式0,当串行发送数据第8位结束时,由内部硬件自动置位,即TI=1,向主机请求中断,响应中断后必须用软件复位,即TI=0。在其他方式中,则在停止位开始发送时由内部硬件置位,必须用软件复位。初始化一般置0
RI:接收中断请求标志位。在方式0,当串行接收到第8位结束时由内部硬件自动置位RI1,向主机请求中断,响应中断后必须用软件复位,即RI0。在其他方式中,串行接收到停止位的中间时刻由内部硬件置位,即RI=1(例外情况见SM2说明),必须由软件复位,即RI=0。初始化一般置0
1. 串口初始化
我们选择工作方式1,所以SM0=0, SM1=1,其余的置零,0100 0000,即SCON=0x40
电源控制寄存器PCON(不可位寻址)
SMOD:波特率选择位。当用软件置位SMOD,即SMOD=1,则使串行通信方式1、3的波特率加倍;SMOD=0,则各工作方式的波特率加倍。复位时SMOD=0。
SMOD0:帧错误检测有效控制位。当SMOD0=1,SCON寄存器中的SM0/FE位用于FE(帧错误检测)功能;当SMOD0=0,SCON寄存器中的SM0/FE位用于SM0功能,和SM1一起指定串行口的工作方式。复位时SMOD0=0。
我们需要让波特率加倍,所以SMOD=1,寄存器赋值应该为1000 0000所以PCON=0x80
void Uart_Init(void) //4800bps@11.0592MHz
{PCON &= 0x80; //使能波特率倍速位SMODSCON = 0x50; //8位数据,可变波特率TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFA; //设定定时初值TH1 = 0xFA; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}
上述代码可以从STC-ISP中的波特率计算器获得
要注意误差为0%
2. 串口写入
void UART_SendByte(unsigned char Byte)
{SBUF=Byte; //写入while(TI==0);TI=0; //响应中断后,软件复位
}
主函数为
void main()
{Uart_Init();UART_SendByte(0x66);while(1){}
}
编译一下,发现按一下开发板的重置键,STC-ISP的串口助手就出现一次"66"
如果串口助手的波特率改为其他数字了,按下充值键出现的就不是66了,数据就出错了,因为采样的时间不一样。
3. 发送秒数
定义一个unsigned char Sec1;
用来记秒数,把UART_SendByte(Sec1);
放入while(1)
循环中。
发送完Sec1后,要自增Sec1++;
,然后延时1秒Delay(1000);
void main()
{Uart_Init();while(1){UART_SendByte(Sec1);Sec1++;Delay(1000);}
}
4. 模块化
新建UART.c和UART.h文件,把串口初始化程序和串口写入程序放到YART.c中,在UART.h中声明一下,在main.c中#include "UART.h"
。
UART.c:
#include <REGX52.H>/*** @brief 串口初始化,4800bps@11.0592MHz* @param 无* @retval 无*/
void Uart_Init(void) //4800bps@11.0592MHz
{PCON &= 0x80; //使能波特率倍速位SMODSCON = 0x50; //8位数据,可变波特率TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFA; //设定定时初值TH1 = 0xFA; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}/*** @brief 串口发送一个字节数据* @param Byte 要发送的一个字节数据* @retval 无*/
void UART_SendByte(unsigned char Byte)
{SBUF=Byte; //写入while(TI==0);TI=0; //响应中断后,软件复位
}
UART.h:
#ifndef __UART__H__
#define __UART__H__void Uart_Init(void);
void UART_SendByte(unsigned char Byte);#endif
main.c:
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"unsigned char Sec1;void main()
{Uart_Init();while(1){UART_SendByte(Sec1);Sec1++;Delay(1000);}
}
四、电脑通过串口控制LED
因为单片机不知道电脑什么时候发信息,所以需要一个中断程序,电脑发过来信息后,触发中断,在中断函数中处理数据。
所以在初始化的时候要把中断程序接上,即EA=1;ES=1;
SCON的REN之前置0,现在要置1,允许串行口接收信息。也就是说,SCON = 0x50;
void Uart_Init(void) //4800bps@11.0592MHz
{PCON &= 0x80; //使能波特率倍速位SMODSCON = 0x50; //8位数据,可变波特率TMOD &= 0x0F; //清除定时器1模式位TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFA; //设定定时初值TH1 = 0xFA; //设定定时器重装值ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1EA = 1;ES = 1;
}
在main.c中,定义一个中断服务子函数
void UART_Routine() interrupt 4
{P2=0x00;
}
当中断号 4 产生时,程序就会跳转到UART_Rountine(),点亮所有LED
编译一下,打开串口助手,随便发一个数据,可以看到,8个LED全亮了。
把程序升级一下,让LED根据发送的数据亮对应的灯。
修改一下中断子函数:
void UART_Routine() interrupt 4
{if(RI=1){P2=~SBUF;RI=0;}
}
编译一下,在串口助手中输入f0,点击发送数据
可以看到D5~D8亮了。
发送aa,D2 D4 D6 D8亮了。
下面再把接收到的数据发回给电脑
void UART_Routine() interrupt 4
{if(RI=1){P2=~SBUF;UART_SendByte(SBUF);RI=0;}
}
五、数据集显示模式
HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
文本模式/字符模式:以原始数据编码后的形式显示