51单片机——USART半双工模式通讯-波特率可选1.2版本

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版权协议,转载请附上原文出处链接和本声明。