1、优化《51单片机——UART串口通讯-波特率可选,增添定时器2产生波特率1.1版本》里的代码。
2、主要修改了串口发送和串口中断里的代码,且增加了串口发送标识位、串口接收完标识位、接收缓存50字节。
3、此代码工作在半双工模式。
4、使用系统输出函数之后要使用自定义函数发送则需要延迟2毫秒,要不然会出错。
测试结果:定时器1、定时器2波特率均已验证OK。4800bps~115200bps

main.c
#include "uart.h"
#include <stdio.h>
#include "delay.h"
#define UART_T1 1//1的话就用定时器1产生波特率,0则为定时器2产生波特率
//备注,此串口是半双工模式
//备注,delay经调试至少需要2毫秒延迟(在使用系统输出函数时)
void main(void)//puts自带换行
{
if(UART_T1)
{
UART_T1_init_config(1);//初始化串口波特率最大为57600
ES=0;//要使用printf了,先关闭串口中断
TI=1;//使用系统函数前需要将TI置1
puts("1、定时器2产生波特率,测试结果pass");
printf("2、定时器2产生波特率,测试结果pass\n");
ES=1;//启动串口中断
delay_ms(2);//不知道为啥需要延迟一下,要不然接收数据会出错
UART_Send_string(RX);//发送默认数据
}
else
{
UART_T2_init_config(1);//初始化串口波特率最大为115200
ES=0;//要使用printf了,先关闭串口中断
TI=1;//使用系统函数前需要将TI置1
puts("1、定时器2产生波特率,测试结果pass");
printf("2、定时器2产生波特率,测试结果pass\n");
ES=1;//启动串口中断
delay_ms(2);//不知道为啥需要延迟一下,要不然接收数据会出错
UART_Send_string(RX);//发送默认数据
}
while(1)
{
if(flag_rx)
{
UART_Send_string(RX);
flag_rx=0;
}
}
}delay.c和delay.h
太简单了,自行处理吧。
uart.h
#ifndef __UART_H__
#define __UART_H__
#include <reg52.h>
//Mode_n_bps输入示例,定时器1没有115200
// bps4800 输入1
// bps9600 输入2
// bps19200 输入3
// bps57600 输入4
// bps115200 输入5
extern bit flag_rx;
extern unsigned char RX[53];//最大保存50个字节
//串口初始化装载,使用定时器1产生波特率,默认为9600bps
void UART_T1_init_config(unsigned char Mode_n_bps);
//串口初始化装载,使用定时器2产生波特率
//串口工作方式1,8位uart且波特率可变,且允许串口接收数据//默认波特率9600bps
void UART_T2_init_config(unsigned char Mode_n_bps);
//串口发送数据
void UART_Send_1Byte_Data(unsigned char DAT);
//串口发送字符串
void UART_Send_string(unsigned char* c);
#endif // !__UART_H__uart.c
#include "uart.h"
sfr IPH_ =0xB7;//中断优先级高位寄存器
sfr SADEN_ =0xB9;//从机地址掩码寄存器
sfr SADDR_ =0xA9;//从机地址控制寄存器
static bit flag_tx=0;//发送空闲表示位
bit flag_rx = 0;//接收忙标识位
unsigned char RX[53]={"最大只能发送二十五个汉字,结尾必须要有回车,切记哈\r\n"};//最大别超过50个字节
//串口初始化装载,使用定时器1产生波特率
//串口工作方式1,8位uart且波特率可变,且允许串口接收数据
//定时器1工作方式2,8位自动重装模式
void UART_T1_init_config(unsigned char Mode_n_bps)//默认波特率9600bps
{
SCON = 0x50;//串口工作方式1,8位uart且波特率可变,且允许串口接收数据
PCON = 0x10;//最高位SMOD=1表示串口工作模式1、2、3下加倍波特率(默认PCON=0x10,波特率不加倍)
SADEN_ = 0x00;//不使用
SADDR_ = 0x00;//不使用
TI=0;//清除
RI=0;//清除
EA = 1;//打开总中断
ES = 1;//打开串口中断
//串口1中断优先级0
IPH_ = 0x00;
IP = 0x00;
TMOD |= 0x20;//开启定时器T1,并使用工作方式2(8位自动重装)
switch(Mode_n_bps)
{
case 1:
TL1 = 250;//计数初值设置波特率4800bps
TH1 = 250;//装载固定初值,当TL1加满后会自动把TH1的值装载进去
break;//bps4800
case 2:
TL1 = 253;//计数初值设置波特率9600bps
TH1 = 253;//装载固定初值,当TL1加满后会自动把TH1的值装载进去
break;//bps9600
case 3:
PCON |= 0x80;//SM0D=1,波特率加倍
TL1 = 253;//计数初值设置波特率19200bps
TH1 = 253;//装载固定初值,当TL1加满后会自动把TH1的值装载进去
break;//bps19200
case 4:
PCON |= 0x80;//SM0D=1,波特率加倍
TL1 = 255;//计数初值设置波特率57600bps
TH1 = 255;//装载固定初值,当TL1加满后会自动把TH1的值装载进去
break;//bps57600
default://默认bps9600
TL1 = 253;//计数初值设置波特率9600bps
TH1 = 253;//装载固定初值,当TL1加满后会自动把TH1的值装载进去
break;//bps9600
}
ET1 = 0;//禁止定时器1发出中断
TR1 = 1;//启动定时器1
}
//串口初始化装载,使用定时器2产生波特率
//串口工作方式1,8位uart且波特率可变,且允许串口接收数据
void UART_T2_init_config(unsigned char Mode_n_bps)//默认波特率9600bps
{
SCON = 0x50;//串口工作方式1,8位uart且波特率可变,且允许串口接收数据
T2CON |= 0x30;//定时器2用作串口波特率发生器且16位自动重装模式
SADEN_ = 0x00;//不使用,默认值
SADDR_ = 0x00;//不使用。默认值
TI=0;//清除,建议清除一下
RI=0;//清除
EA = 1;//打开总中断
ES = 1;//打开串口中断
//串口1中断优先级0
IPH_ = 0x00;//默认值
IP = 0x00;//默认值
//T2MOD = 0x02;//T2OE :T2 输出允许位,当 T2OE=1 的时候,允许时钟输出到 P1.0。(仅对80C54 / 80C58 有效)
//DCEN:向下计数允许位。 DCEN = 1 是允许 T2 向下计数,否则向上计数。
switch (Mode_n_bps)
{
case 1:
TL2 = 0xb8;//计数初值设置波特率4800bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xb8;
RCAP2H = 0xff;
break;//bps4800
case 2:
TL2 = 0xdc;//计数初值设置波特率9600bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xdc;
RCAP2H = 0xff;
break;//bps9600
case 3:
TL2 = 0xee;//计数初值设置波特率19200bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xee;
RCAP2H = 0xff;
break;//bps19200
case 4:
TL2 = 0xfa;//计数初值设置波特率57600bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xfa;
RCAP2H = 0xff;
break;//bps57600
case 5:
TL2 = 0xfd;//计数初值设置波特率115200bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xfd;
RCAP2H = 0xff;
break;//bps115200
default://默认bps9600
TL2 = 0xdc;//计数初值设置波特率9600bps
TH2 = 0xff;//装载固定初值
RCAP2L = 0xdc;
RCAP2H = 0xff;
break;//bps9600
}
TR2 = 1;//启动定时器2
}
//串口发送数据
void UART_Send_1Byte_Data(unsigned char DAT)
{
while(flag_tx);//等待之前的数据发送完成
flag_tx=1;
SBUF = DAT;
//如果使用while(!TI);
//可能会出现TI=1时刚好进入串口中断,然后串口中断将TI=0;此时程序会卡死在while里的
}
//串口发送字符串
void UART_Send_string(unsigned char *c)
{
do
{
UART_Send_1Byte_Data(*c++);//带*c表示的是值,c表示的是地址
}while (*c != '\0');
}
//串口中断函数,中断号4
//单片机串口只缓存1字节8位数据
void UART_IT(void) interrupt 4
{
static unsigned char num=0;
if (TI) //发送标志位
{
TI = 0;
flag_tx=0;
//使用printf()/puts()/putchar()(stdio.h)的时候,需要ES=0;TI=1;printf();(TI=0,printf使用后会自动清除)最后ES=1。
}
if (RI) //接收标志位
{
RX[num]=SBUF;//将接收的数据先保存起来
//把串口接收到的值在重新发送回去,如果接收数据很大有丢失数据风险
RI = 0;
if(RX[num]=='\n')
{
num++;
RX[num]='\0';//为的是使用字符串发送数据
flag_rx = 1;//数据接收完毕
num=0;//清零,使其下次得到的数据开始从0开始存储
}
else
{
switch(++num)
{
case 53:num=0;break;
}
}
}
}
版权声明:本文为qq_44829055原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。