07:串口通信二

1、与波特率之相关的寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j8DCWyk5-1720973827296)(https://i-blog.csdnimg.cn/direct/e3d9031f80624989965e2c7032d80458.png)]

如图,与串口通信相关的寄存器主要是SCON和PCON寄存器。

2、PCON寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8GFUA4e-1720973827300)(https://i-blog.csdnimg.cn/direct/1fde62fb51c7497e970738a39950ff7f.png)]

SMOD:为1时,通信方式1,2,3波特率加倍,为0时不加倍。
SMOD0:帧错误检测位,为1时,SCON寄存器中的SM0用于帧错误检测。为0时,用于指定的串口工作方式

3、SCON寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ROY56emb-1720973827301)(https://i-blog.csdnimg.cn/direct/ca0be444120045fa9fb3f54e7c9a1364.png)]

SM0:当PCON的SMOD0为1时,这个位用于帧错误检测。当SMOD0为0时,则用于通信方式的选择
SM0,SM1:
0     0           工作方式0 
0     1           工作方式1           8位异步位数据传输(波特率可配)
1     0           工作方式2           9位(波特率不可配)
1     1           工作方式3           9位(波特率可配)
SM2:允许方式2,3多机控制
REN:运行接收控制位,为1,允许接收;为0,禁止接收。
TB8/RB8先不管它
T1:发送中断请求
R1:接收中断请求
  • 工作方式0的波特率 = 系统晶振/12 = 11.0592MHz/12=11059200/12
  • 工作方式1的波特率 = (2^SMOD/32)*定时器1的溢出率
  • 工作方式2的波特率 = (2^SMOD/64)*11.0592MHz
  • 工作方式3的波特率 = (2^SMOD/32)*定时器1的溢出率

定时器1的溢出率:

  • 工作在12T时:溢出率 = 11.0592MHz / 12 / (256 - TH1)
  • 工作在6T时:溢出率 = 11.0592MHz / 6 / (256 - TH1)

4、配置的代码分析

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xBF;		//定时器1时钟为Fosc/12,即12T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}
PCON &= 0X7F;代表PCON变为0xxx xxxx,则最高位变为0,其他位不变。因为PCON的初始值为00x1 0000,所以PCON = 00x1 0000,代表波特率不加倍
SCON = 0x50; 代表0101 0000。8位数据传输(波特率可变),可以读取数据
AUXR = 0X01; 代表降低时钟对外界的辐射
TMOD &= 0X0F;
TMOD |= 0X20; 代表TMOD = 0010 0000,选用定时器1,且为8位自动重装,低8位溢出时,自动将高8位的值给低8位
TL1 = 0XFD;
TH1 = 0XFD; 代表1111 1101,波特率 = (2^0/32)*(11059200/12/(256 - 253)) = 9600,则波特率为9600
TR1 = 1;打开定时器1。

5、向PC发送一段字符串

代码①:

#include <REGX52.H>
#include "intrins.h"

sfr AUXR = 0X8E;

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x40;		//8位数据,可变波特率
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	TR1 = 1;		//启动定时器1
}

void sendByte(char data_sj)
{
	SBUF = data_sj;/*SBUF开始向外发送数据,注意是开始,串口要经过一段时间才能发送完毕*/
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void main()
{
	UartInit();
	while(1)
	{
		sendString("wohaoshuai");
		Delay1000ms();
	}
}

出现的现象:

wwwoohhwwo

  • 为什么是乱的喃?
    因为啊,往SBUF向外发送数据需要移位寄存器进行操作,而移位寄存器操作也是需要时间的,假设需要10us。当第一个数据才开始发送,都还没有发送完毕,第二个数据又写入开始发送了,所以会出现发送的数据出现乱排序。
    例如:我们去医院排队看病一样,医生才开始给第一个病人看病,都还没有看完。第二个病人突然冲进来,要求医生给第二个病人看病。这样就把医生看病顺序打乱。

  • 解决方法1:
    只需要在输出SBUF后面添加一个延迟函数,等SBUF将第一个数据输出完毕后,然后在开始输出第二个数据。

代码①:

void sendByte(char data_sj)
{
	SBUF = data_sj;//开始输出数据
	Delay10ms();//给一个时间,等待输出完毕
}
  • 解决方法2:
    通过中断请求标志位
    在这里插入图片描述

如图,当使用工作方式1(8位数据传输),当8位数据通过移位寄存器输出结束时,TI变为1。例如第一个w用8位二进制数据表示,当第8位通过移位寄存器输出结束也代表第一个w输出完毕。此时TI变为1
代码②:

void sendByte(char data_sj)
{
	SBUF = data_sj;
	while(!TI);/*还没有输出完毕,TI=0,则一直困在这个循环里面,当输出完毕时,TI=1,退出循环,进入下一行程序*/
	TI = 0;
}

当单片机发送数据完毕后,TI会自动变为1,但是TI = 0的时候才会开始发送数据。所以发送完毕后要手动给他置0

6、PC机向单片机发送字符控制LED1灯的亮灭

#include <REGX52.H>
#include "intrins.h"

sfr AUXR = 0X8E;
sbit LED1 = P3^7;

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void UartInit(void)		//波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x40;		//8位数据,可变波特率
	REN = 1;				//运行单片机读取数据
	AUXR = 0x01;	
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	TR1 = 1;		//启动定时器1
}

void main()
{
	char mark;//第一一个mark来承接字符
	LED1 = 1;
	UartInit();
	while(1)
	{
		Delay300ms();
		mark = SBUF;  //单片机开始读取字符,还没有读取完毕
		if(RI == 1)  //表示单片机已经读取完成了,RI会自动变为1。而RI = 0,单片机才会开始读取字符,所以读取完成后要手动置0
		{
			RI = 0;//手动置0
			if(mark == 'o')//如果读取的字符是O
			{
					LED1 = 0; //开灯
			}
			if(mark == 'c')
			{
					LED1 = 1;
			}
		}
	}
}

RI是接收中断请求标志位,单片机已经读取完成了,RI会自动变为1。而RI = 0,单片机才会开始读取字符,所以读取完成后要手动置0

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值