单片机串口通信包含波特率计算(12M/11.0592M)与SMOD加倍

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

很多同学在学习单片机过程中遇到了很多的问题,比如串口,串口是单片机的一块重要知识,学不好学不会都将对单片机而言是一种缺憾,最近我遇到了问题,怎样设置波特率,SMOD是波特率加倍的一个困惑之地,见后续


正文内容

一、了解什么是波特率,干什么用?

官方:

指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps……。

我的理解:

波特率就是单片机发送和接收数据时双方的一种约定,是用高低电平代表数字0-1传送数据比特(bit)的一种方式,比如我们采用9600波特率发送数据,意思是一秒可以传送9600个bit,8个bit为一个字节(byte),可以换算,比如我们发送1个字节的数据0X0A(展开:00001010,十进制也就是10),这个波特率让串口端口引脚高低电平变换00001010高低电平切换,每1/9600秒发送一个0-1电平。

**

关于为什么加这种约定:

**就是为了避免通信双方约定波特率不同,导致误码,A以1/9600的速率发送了 01 B接受时用1/4800速率接受,这样只能接受到电平1,因此B获得到的电平是1,漏掉了0,这就是波特率不同导致的问题。

二、计算波特率与串口通信

1.公式

请添加图片描述在这里插入图片描述
上面就是计算波特率的通用公式,任何请况下都能使用,可以理解一下。

2.12M和11.05926M晶振区别

我们用上面的11.05926M时,计算9600波特率时,得到的TH1极其接近250,但是当我们用12M晶振时,计算9600波特率便会发现,TH1=249.5多点,而转换为16进制,没法带小数部分,因此就要进行四舍五入,将TH1近似为250或者249,此处我近似为249,作为初始装载值,但是由于一点一点误差的积累,便带来了某个数据的错误,因此就出现了个别数据的误码。

要避免上述误码,可以换一种波特率,比如4800,自己可以尝试计算一下TH1就行,看看多大的误差。肯定越小越好!
在这里插入图片描述
偶尔误码情况!

3.串口通信收发代码

直接运行即可,加了一个串口中断,发送数据时可引发中断,上传数据包中的数据!我这是12M****晶振下的串口收发!读者注意!!!
如果是11.0592M晶振请用上面公式计算TH1=TL1的初始值

#include "reg51.h"			 //
#include <intrins.h>
typedef unsigned int u16;	 
typedef unsigned char u8;
unsigned int array[]={0XAA, 0X01 ,0X1A, 0X05,0X01, 0X00, 0X00, 0X11, 0X01, 0X01,0X02 ,0X00 ,0X00 ,0X11 ,0X01 ,0X01,0X03 ,0X00 ,0X00 ,0X11 ,0X01 ,0X01,0X04, 0X00,0X00 ,0X11 ,0X01 ,0X01 ,0X05 ,0X00, 0X00 ,0X11, 0X01, 0X01 ,0XFF,0X11,0X01,0X20};//根据实际数据情况,我这是随便写的!嘿嘿
unsigned int array1[2];
sbit led = P2^1;
sbit led1 = P2^0;
void Delay5ms()		//@12.000MHz
{
	unsigned char i, j;
	i = 59;
	j = 90;
	do
	{
		while (--j);
	} while (--i);
}
void Delay1000ms()		//@12.000MHz
{
	unsigned char i, j, k;
	_nop_();
	_nop_();
	i = 46;
	j = 153;
	k = 245;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void usartinit()
{
	SCON=0X50;//ÔÊÐí´®ÐнÓÊÜ£¬Ñ¡Ôñ·½Ê½¶þ½øÐÐ8λ×Ô¶¯ÖØ×°ÔØ
	TMOD=0X20;//Æô¶¯¶¨Ê±Æ÷1 ¹¤×÷ģʽ2 8λ×Ô¶¯ÖØ×°ÔÔ
	PCON=0X80;//²¨ÌØÂʱ¶Æµ
	//4800 4800波特率情况下无误码率
	//TH1=0XF3;
	//TL1=0XF3;
	//9600 装载值,存在个别数据误码 
	TH1=0XF9;
	TL1=0XF9;
	
	TR1=1;
	ES=1;//´ò¿ª´®¿ÚÖжÏ
	EA=1;//´ò¿ª×ÜÖжÏ
}	

void usart() interrupt 4  //²»ÒªÂ©µôÀ¨ºÅ ´®¿ÚÖжÏ4
{
	u8 reivedata;
	u8 num;
	if(RI)//接收数据,RI变成1
	{
		reivedata=SBUF;接受的东西存放到reivedata中,这里没使用
		RI=0;//接受中断清空
		for(num=0;num<38;num++)
		{
			SBUF = array[num];//中断控制循环发送数据包
			Delay5ms();//必须加一定延时,否则发送不完也会导致误码情况的发生!
		}
		led=~led;//灯反转 检测是否进入串口中断!
	}
	if(TI)
	{
		TI=0;//清空发送中断标志位
	}
}
void main()
{	
	u8 i;
	usartinit();//初始化
	while(1)
	{
		Delay1000ms();//时间偏长了
		for(i=0;i<38;i++)
		{
			SBUF = array[i];//循环发送数据包
			Delay5ms();
		}
		led1=~led1;//检测while()循环是否正常,防止程序卡死到其他位置
	}
}

三、总结

欢迎大家学习交流!不足之处恳请批评指正,一起交流,一起进步! 人生没有终点,即使在苦难中痛不欲生!

清华yuan镜像:pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple/

  • 11
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用51单片机接收ESP8266模块发送的串口数据的测试代码,其中单片机晶振为11.0592KHZ,波特率为9600: ```c #include <reg52.h> #define FOSC 11059200L #define BAUD 9600 #define TIMER_1MS_CNT (65536-FOSC/12/1000) // 1ms定时器计数值 sbit LED = P1^0; // 定义P1.0为LED输出口 void InitUART(); // 串口初始化函数 void InitTimer(); // 定时器初始化函数 void UART_ISR() interrupt 4; // 串口中断服务函数 void Timer_ISR() interrupt 1; // 定时器中断服务函数 unsigned char RX_BUF[32]; // 接收缓冲区 unsigned char RX_CNT = 0; // 接收计数器 void main() { InitUART(); // 初始化串口 InitTimer(); // 初始化定时器 EA = 1; // 全局中断使能 while(1); } void InitUART() { TMOD &= 0xF0; // 定时器1工作在16位自动重载模式 TMOD |= 0x20; TH1 = TL1 = -(FOSC/12/BAUD); // 波特率设置 PCON &= 0x7F; // SMOD=0,波特率加倍 SCON = 0x50; // 串口工作在模式1,允许接收 ES = 1; // 串口中断使能 RI = 0; // 接收标志位清零 } void InitTimer() { TMOD &= 0x0F; // 定时器0工作在模式1 TMOD |= 0x10; TH0 = TIMER_1MS_CNT / 256; // 1ms定时器初值设置 TL0 = TIMER_1MS_CNT % 256; ET0 = 1; // 定时器中断使能 TR0 = 1; // 定时器0开始计时 } void UART_ISR() interrupt 4 { if(RI) // 如果接收到数据 { RI = 0; // 接收标志位清零 RX_BUF[RX_CNT++] = SBUF; // 将接收到的数据存入缓冲区 if(RX_CNT >= 32) RX_CNT = 0; // 超出缓冲区大小则清零 } } void Timer_ISR() interrupt 1 { TH0 = TIMER_1MS_CNT / 256; // 1ms定时器计数值重新赋值 TL0 = TIMER_1MS_CNT % 256; LED = !LED; // 指示灯翻转 if(RX_CNT) // 如果接收缓冲区中有数据 { unsigned char i; for(i=0; i<RX_CNT; i++) // 循环读取所有数据 { SBUF = RX_BUF[i]; // 将数据发送回去 while(!TI); // 等待发送完成 TI = 0; // 发送标志位清零 } RX_CNT = 0; // 清空接收缓冲区 } } ``` 以上代码中,使用了定时器和串口中断来实现中断驱动的串口接收。每次接收到数据后,将数据存入缓冲区中,并在定时器中断中周期性地将缓冲区中的数据发送回去。注意,此代码中只是将接收到的数据原封不动地返回,实际应用中需要根据具体需求进行处理。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值