大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全家桶1年46,售后保障稳定
单片机 串口编程之串口通信仿真实验
一、简述
记–简单的使能串口,串口收发数据的例子。(使用Proteus仿真+虚拟串口调试)
代码,仿真文件打包:链接: https://pan.baidu.com/s/1nyb46fTJrYcAy_VarFdO3A 提取码: j44s
蓝奏:https://www.lanzous.com/i2fx3oh
虚拟串口调试软件打包:链接: https://pan.baidu.com/s/1qaOgM8P7ZRmXb903NkiMOQ 提取码: r18u
蓝奏:外链:https://wwm.lanzouv.com/b0catp8te 密码:6yug
串口助手源码:链接: https://pan.baidu.com/s/1T9ZA8jnsjXDhNLuL1ezGdg 提取码: 6usr
二、效果
PC机通过串口调试助手发送数据给单片机,单片机收到之后回复:I received.。如果单片机收到的是’1’则点亮LED灯,否则熄灭LED灯。
使用c#编写的串口助手
(真实硬件实验:使用Keil C51将代码编译为HEX文件,用串口线/USB转串口线连接PC机,可以使用STC-ISP软件烧写到51单片机,在PC机可以使用串口调试助手(STC-ISP也有这个功能,也可以是其他软件,当然也可以自己编写串口程序)与单片机进行串口通信。实际中,电脑串口采用232电平,而单片机串口则采用TTL电平,如果不进行电平转换,单片机与电脑就无法正常通信,甚至单片机芯片可能会被烧坏。MAX232 芯片可以进行电平转换,是将单片机输出的TTL电平转换成PC机能接收的232电平或将PC机输出的232电平转换成单片机能接收的TTL电平。电平是个电压范围,如可能规定输出电压>2.4V则为高电平,输出电压低于<0.4V则为低电平,不同的电子器件、不同的标准有着不同的电压范围)
注:一个COM一般不能同时被两个程序占用。
三、工程结构及各属性设置
keil工程
proteus仿真
添加虚拟串口
四、源文件
main.c文件
#include <reg51.h>
sbit p1_0 = P1^0;
//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
SCON = 0x50; // SCON: 方式 1, 8-bit, 允许接收数据
TMOD |= 0x20; // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
TH1 = 0xFD; // TH1: 初始值为0xFD 波特率:9600 晶振频率:11.0592MHz
TL1 = 0x0;
TR1 = 1; // TR1: 开启定时器1
EA = 1; //打开总中断
ES = 1; //打开串口中断
}
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
TI = 0;// 将TI清零,表示可以发送下一字节数据。
}
// 发送字符串
void uart_send_str(unsigned char *s)
{
while(*s != '#include <reg51.h>
sbit p1_0 = P1^0;
//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
SCON = 0x50; // SCON: 方式 1, 8-bit, 允许接收数据
TMOD |= 0x20; // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
TH1 = 0xFD; // TH1: 初始值为0xFD 波特率:9600 晶振频率:11.0592MHz
TL1 = 0x0;
TR1 = 1; // TR1: 开启定时器1
EA = 1; //打开总中断
ES = 1; //打开串口中断
}
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
TI = 0;// 将TI清零,表示可以发送下一字节数据。
}
// 发送字符串
void uart_send_str(unsigned char *s)
{
while(*s != '\0')// '\0':字符串结束标志
{
uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
s++;// 指向下一个字符
}
}
// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 //也叫串行中断服务程序
{
unsigned char recv_data;// 用来存放接收到的数据
unsigned char send_data[] = "I received.\n";// 要发送的信息
if(RI) //接收数据(1字节)完毕,RI会被硬件置1
{
RI = 0; // 将 接收中断标志位 清零(让串口可以继续接收数据)
recv_data = SBUF; //读取接收到的数据,并存放到data
if(recv_data == '1')//如果收到的字符是'0',就让灯灭
{
p1_0 = 0;// p1.0引脚低电平,LED灯亮
}
else
{
p1_0 = 1;// p1.0引脚高电平,LED灯灭
}
uart_send_str(send_data); //收到数据之后,发送字符串"I received."给对方
}
if(TI)// 发送数据(1字节)完毕
{
TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
}
}
void main(void)
{
init_uart();// 初始化串口
while(1)// 主循环不做任何动作。(死循环)
{}
}
')// '#include <reg51.h>
sbit p1_0 = P1^0;
//初始化串口 (设置串口,开启串口中断)
void init_uart(void)
{
SCON = 0x50; // SCON: 方式 1, 8-bit, 允许接收数据
TMOD |= 0x20; // TMOD: 设置定时器1工作在方式2, 8-bit 自动重装
TH1 = 0xFD; // TH1: 初始值为0xFD 波特率:9600 晶振频率:11.0592MHz
TL1 = 0x0;
TR1 = 1; // TR1: 开启定时器1
EA = 1; //打开总中断
ES = 1; //打开串口中断
}
// 发送一个字节数据
void uart_send_byte(unsigned char dat)
{
SBUF = dat; // 将数据送到发送缓冲寄存器SBUF,一位一位的发送
while(!TI); // 等待发送完毕 (发送完毕TI硬件置1)
TI = 0;// 将TI清零,表示可以发送下一字节数据。
}
// 发送字符串
void uart_send_str(unsigned char *s)
{
while(*s != '\0')// '\0':字符串结束标志
{
uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
s++;// 指向下一个字符
}
}
// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 //也叫串行中断服务程序
{
unsigned char recv_data;// 用来存放接收到的数据
unsigned char send_data[] = "I received.\n";// 要发送的信息
if(RI) //接收数据(1字节)完毕,RI会被硬件置1
{
RI = 0; // 将 接收中断标志位 清零(让串口可以继续接收数据)
recv_data = SBUF; //读取接收到的数据,并存放到data
if(recv_data == '1')//如果收到的字符是'0',就让灯灭
{
p1_0 = 0;// p1.0引脚低电平,LED灯亮
}
else
{
p1_0 = 1;// p1.0引脚高电平,LED灯灭
}
uart_send_str(send_data); //收到数据之后,发送字符串"I received."给对方
}
if(TI)// 发送数据(1字节)完毕
{
TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
}
}
void main(void)
{
init_uart();// 初始化串口
while(1)// 主循环不做任何动作。(死循环)
{}
}
':字符串结束标志
{
uart_send_byte(*s);// 发送1个字节数据,1个字符占8位,1字节
s++;// 指向下一个字符
}
}
// 串口中断处理函数 (串口接收到数据,发送数据完毕都可以引起串口中断)
void uart_interrupt(void) interrupt 4 //也叫串行中断服务程序
{
unsigned char recv_data;// 用来存放接收到的数据
unsigned char send_data[] = "I received.\n";// 要发送的信息
if(RI) //接收数据(1字节)完毕,RI会被硬件置1
{
RI = 0; // 将 接收中断标志位 清零(让串口可以继续接收数据)
recv_data = SBUF; //读取接收到的数据,并存放到data
if(recv_data == '1')//如果收到的字符是'0',就让灯灭
{
p1_0 = 0;// p1.0引脚低电平,LED灯亮
}
else
{
p1_0 = 1;// p1.0引脚高电平,LED灯灭
}
uart_send_str(send_data); //收到数据之后,发送字符串"I received."给对方
}
if(TI)// 发送数据(1字节)完毕
{
TI = 0;// 将 发送中断标志位 清零(让串口可以继续发送数据)
}
}
void main(void)
{
init_uart();// 初始化串口
while(1)// 主循环不做任何动作。(死循环)
{}
}
五、总结
51单片机系列
1、单片机的串口相关的寄存器
1) 串口控制寄存器SCON:(Serial CONtrol)
2) 串行数据寄存器SBUF:(Serial data BUFfer)
3)电源管理寄存器PCON:(Power CONtrol)
2、串口控制寄存器SCON
用来对串口进行控制,其寄存器地址为0x98,支持位寻址。
一般来说单片机的寄存器的个数在制作好就已经确定了,MCS-51单片机有一些寄存器是8位的(可以存放8位数据),有一些是16位的,这些寄存器有点像全局变量(寄存器名-变量名),它们分配了固定的地址空间,我们可以对这个寄存器变量进行赋值或读取值。比如我们禁止串口中断,那么我们可以设置相应的值,当系统处理串口相关的任务时,读取到这个我们设置的值时,直到我们要禁止串口中断,那么系统就不会相应串口中断。串口控制寄存器SCON的地址为0x98,我们可以直接对SCON这个”变量”进行赋值,支持位寻址意思是:SCON一共有8位,每1位代表的意思都有不同,可以直接单独对某一位进行设置,有一些寄存器是不可以位寻址的。
SCON控制寄存器 | ||||||||
位编号 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
位名称 |
SM0 |
SM1 |
SM2 |
REN |
TB8 |
RB8 |
TI |
RI |
位地址 |
9FH |
9EH |
9DH |
9CH |
9BH |
9AH |
99H |
98H |
SM0,SM1串行口工作方式控制位 | ||||
SM0 |
SM1 |
工作方式 |
功能 |
波特率 |
0 |
0 |
方式0 |
8位同步移位寄存器 |
晶振频率/12 |
0 |
1 |
方式1 |
10位UART |
可变 |
1 |
0 |
方式2 |
11位UART |
晶振频率/64或晶振频率/32 |
1 |
1 |
方式3 |
11位UART |
可变 |
SM2 | 多机通信控制位 | 当该位置1为多机通信,多机通信仅在方式2、方式3有效。 |
REN | 接收允许位 | 为1允许接收,为0禁止接收 |
TB8 | 待发送的第9位数据 | 同来存放工作方式2,工作方式3模式下等待发送的第9位数据 |
RB8 | 收到的第9位数据 | 同来存放工作方式2,工作方式3模式下接收到的第9位数据,在方式1下为接收到的停止位,工作当时0不使用该位。 |
TI | 发送中断标志位 | 发送完成标志位,当SBUF中的数据发送完成后由硬件置1,并且当单片机硬件中断被使能之后触发中断事件,该位必须由软件清零,并且该位被清零之后才能进行下一个字节数据的发送。 |
RI | 接收中断标志位 | 接受完成标志位,当SBUF收到一个字节的数据后由硬件系统置1,并且当单片机硬件中断被使能之后触发串行中断事件,该位必须由软件清零,并且只有该位被清零之后才能够进行下一字节数据的接收。 |
3、串行数据寄存器SBUF
用于存放串行通信中发送和接收的相关数据。其寄存器地址是0x99。
SBUF由发送缓冲寄存器和接收缓冲寄存器组成,这两个寄存器占用同一个寄存器地址。其中发送缓冲寄存器只写,接收寄存器只读。
发送数据:当将一个数据写入SBUF后,单片机立即根据选择的工作方式和波特率将写入的字节数据进行相应的处理然后从TXD(P3.1)引脚串行发送出去,发送完成之后置位相应寄存器的标志位,只有相应的标志位被清除之后才能进行下一次的数据发送。
接收数据:当RXD(P3.0)引脚根据工作方式和波特率接收到一个完整的数据字节后单片机将该数据字节放入接收缓冲寄存器中,并置位相应的标志位。接收缓冲寄存器是双字节的,这样就可以在单片机读取接收缓冲寄存器的数据时同时进行下一个字节的数据接收。不会发生前后两个字节的数据冲突(数据覆盖)。
4. 电源管理寄存器PCON
可以控制串口波特率加倍。
PCON电源管理寄存器 | ||||||||
位编号 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
位符号 |
SMOD |
SMOD0 |
LVDF |
POF |
GF1 |
GF0 |
PD |
IDL |
SMOD:该位与串口通信有关。
SMOD=0; 串口方式1,2,3时,波特率正常。
SMOD=1; 串口方式1,2,3时,波特率加倍。
5、串口的工作方式
MCS-51单片机的串口一共4种工作方式。其中工作方式0为同步通信方式,其余3种为异步通信。
工作方式0:外部引脚TXD(P3.0)为数据的输出输入端,外部引脚TXD(P3.1)提供数据的同步脉冲。
因此工作在方式0进行串口通信不需要考虑波特率。
工作方式1:使用定时器作为波特率发生器。
波特率的计算
常见的波特率对应的初始值 | ||||||
波特率/工作频率 | 11.0592MHz | 12MHz |
14.74456MHz |
16MHz | 20MHz | SMOD值 |
150(b/s) | 0x40 | 0x30 | 0x00 | 0 | ||
300(b/s) | 0xA0 | 0x98 | 0x80 | 0x75 | 0x52 | 0 |
600(b/s) | 0xD0 | 0xCC | 0xC0 | 0xBB | 0xA9 | 0 |
1200(b/s) | 0xF8 | 0xE6 | 0xE0 | 0xDE | 0xD5 | 0 |
2400(b/s) | 0xF4 | 0xF3 | 0xF0 | 0xEF | 0xEA | 0 |
4800(b/s) | 0xF3 | 0xEF | 0xEF | 1 | ||
4800(b/s) | 0xFA | 0xF8 | 0xF5 | 0 | ||
9600(b/s) | 0xFD | 0xFC | 0 | |||
9600(b/s) | 0xF5 | 1 | ||||
19200(b/s) | 0xFD | 0xFC | 1 |
工作方式2:
工作方式3:波特率计算方式与工作方式1相同。
6.波特率
波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示。
在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。
通信双方约定一致的波特率以便正确的接收数据。(数据发送前可能进行了调制,波特率不同,调制参数不同)
如果收发双方的波特率不一样,那么调制与解调的参数不一样,就很有可能读取不到正确的数据,从而解读为乱码。
通常串口工作方式1使用定时器作为波特率发生器。
7、串口中断
使能串口中断:打开总中断-中断控制寄存器IE的EA位置1,打开串口中断中断控制寄存器IE的ES位置1
中断控制寄存器IE(Interrupt Enable) | ||||||||||||||||||||||||||
位序号 |
D0 |
D1 |
D2 |
D3 |
D4 |
D5 |
D6 |
D7 |
||||||||||||||||||
说明 |
外部中断0 |
定时/计数0 |
外部中断1 |
定时/计数1 |
串行口中断 |
保留位 |
保留位 |
全局中断位 |
||||||||||||||||||
位符号 |
EX0 |
ET0 |
EX1 |
ET1 |
ES |
— |
— |
EA |
||||||||||||||||||
位地址 |
A8H |
A9H |
AAH |
ABH |
ACH |
— |
— |
AFH |
||||||||||||||||||
|
使能串口中断之后,接收到数据(RI置1)/完成发送(TI置1),RI/TI被置1就会触发串口中断事件,然后执行中断处理函数。
中断处理函数编写格式:
void 函数名(void) interrupt 4 using 工作寄存器组编号
{
//要执行的动作
}
4的含义: 一共5个中断编号为0~4,串口中断的编号为4
using 寄存器组编号:一共4组工作寄存器组,编号为0~3,如果不写默认为工作寄存器组0。
8、初始化串口为模式1过程。
1) SCON设置
位名称 |
SM0 |
SM1 |
SM2 |
REN |
TB8 |
RB8 |
TI |
RI |
设置值 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
含义 | 工作在方式1 | 波特率不加倍 | 允许接收数据 | 工作方式1不设置该位 | 初始为未发送完数据 |
初始为为接收到数据 |
SCON = 0x01010000 = 0x50
2) PCON设置
位编号 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
位符号 |
SMOD |
SMOD0 |
LVDF |
POF |
GF1 |
GF0 |
PD |
IDL |
默认波特率不加倍 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
波特率加倍 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
PCON = 0x00 (SMOD=0,波特率不加倍)
PCON = 0x80 (SMOD=1,波特率加倍)
3) 定时器设置
工作方式设置:
MI,M0定时器模式选择 | |||
M1 |
M0 |
工作方式 |
功能说明 |
0 |
0 |
方式0 |
13位定时器/计数器 |
0 |
1 |
方式1 |
16位定时器/计数器 |
1 |
0 |
方式2 |
自动重载8位定时器/计数器 |
1 |
1 |
方式3 |
T0分为2个8位独立计数器,T1无方式3 |
开启定时器1:将控制寄存器TCON的TR1置1
设置定时器1的初值:TH1数据寄存器装载初值的高8位,TL1数据寄存器装载初值的低8位。
定时器用作波特率发生器时,使用上面的波特率计算公式得到初始值,或直接查看常见的波特率对应的初始值。
TH1 = 0xFD;// 11.0592MHz的工作频率,9600的波特率 对应的初始值。
4) 打开总中断 EA = 1; 打开串口中断 ES = 1;
================以下回复 qq_45096251 这位兄弟==============
两个单片机之间的串口通信仿真实验:
测试的仿真文件和代码:链接: https://pan.baidu.com/s/1PMu6dT-1DMEJllgv9gxqMA 提取码: hrcy
使用虚拟串口软件,虚拟出两对串口,例子中是com1<==>com2, com3<==>com4,其中com2,com4分别对应单片机A和单片机B。com1和com3分别是PC端与单片机A、B通信的端口。在PC端使用串口工具实现两个仿真单片机的串口通信。
测试效果:
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/200844.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...