虫子 串口 基本操作

举报
虫子VV 发表于 2022/06/23 00:32:56 2022/06/23
【摘要】 小码农不敢接受妖媚子(想多活几年),但是串口还是可以传送数据的 并行通信 串行通信 ==同步:== ==异步:== 串口通信:收发一个字节(只能用时间来同步) 串口通讯的相关术语 STC串口1通信的寄存器 1.方式设置以及中断标志寄存器 2.电源控制寄存器中的波特率加倍功能寄存器 3.数据收发缓存寄存器SBUF 4.辅助寄存器AUXR控制定时器分频,独立波特率发3生器以及串口2的相关控制 ...

小码农不敢接受妖媚子(想多活几年),但是串口还是可以传送数据的

并行通信

image-20211103095439589

==多位数据同时传输==

通讯介质通常为TTL,差分(低压差分芯片)

例如:LCO1602,ADC0804,LVDS(液晶显示)等

串行通信

image-20211103100244185

==多位数据排队传输,任意IO口都可以做数据收发==

通信介质通常为TTL,差分(485),无线电

==同步:==

74HC595(单工),IIC(半双工),SPI

==异步:==

单线归零、1- Wire 、 CAN 总线、无线电、UART (含 DMX512 、 modbus )。

image-20211103135859645

​ UART 是单片机的独立模块。我们设置好模块的参数,需要发送什么内容,扔给串口模块就行。 CPU 不需要按位读写。该模块是指定的管脚输入输出。目前, STC 所有型号串口1 的输入是 RxD1=P3.0, , 输出是 TxD1=P3.1 。
​ STC15 开发板使用 STC15W4K16S4 芯片,串口有4个,其中串口3的输入是 RxD3= P0.0, , 输出是 TxD3= P0.1 。与矩阵键盘共用了两个 IO

串口通信:收发一个字节(只能用时间来同步)

CPU 往 SBUF 中写入一个 byte , CPU 可以跑去执行其他函数。如果发送完成,串口模块向 CPU 申请中断(T1);

==串口收发一位数据,依靠严格的时间来保证收发同步==

如果串口模块接收完一个字节,也向 CPU 申请中断( RI ),CPU 可以从 SBUF 中读取数据。

image-20211103143231367

串口通讯的相关术语

**==1.位采样脉冲:==**接收器把每个 bit 的时间平均等份,对线路电平进行采样,确定接收到的是高电平还是低电平,才能进行下一位数据的采样。 STC89 是 16 等份判断7、8 、9(三选二)。STC15W 和 STC8 系列是4等份(四选三,官方资料写错成 16 了)

image-20211103150605251

==2.波特率:==: 是指1秒钟的时间内,串口通信线路上面,发送的数据位数, , 简单理解成1秒钟内电平跳变多少次
常用的波特率有 9600 等, STC 下载软件里面可以选择的就有很多种波特率。我们后面要学的 DMX512 就是 250000bps .

**==波特率的本质,是设置信号时间点。==**到点就按时发送或者接收一位。串口全双工通信是没有时钟脉冲的,只能依靠晶振脉冲、定时器的溢出脉冲。

==3.波特率哪里来:==波特率来自定时器的溢出,独立波特率发生器,以及系统时钟分频

image-20211103153308124

STC串口1通信的寄存器

1.方式设置以及中断标志寄存器

image-20211103170309057

2.电源控制寄存器中的波特率加倍功能寄存器

image-20211103170553340

3.数据收发缓存寄存器SBUF

image-20211103175929951

4.辅助寄存器AUXR控制定时器分频,独立波特率发3生器以及串口2的相关控制

image-20211103181324915

5.独立波特率发生器寄存器BRT,用于保存该定时器的初值

image-20211103181544848

6.与串口通信相关的中断寄存器IE,中断优先级IPH,IP

image-20211103181507083

image-20211103181734848

串行口1工作模式1:8位UART,波特率可变

image-20211103182218019

image-20211103182415029

==收发波特率需要一致==

==收发波特率不一致,导致RX端不能正常接收:==

串口1

1.配置 SCON ,选择方式1,8 位可变波特率,允许接收。

SM0 = 0;SM1 = 1;REN = 1;

image-20211109160732243

PCON &= 0x3f;

image-20211105101658525

2.配置定时器2为 :16 位自动重装模式, 不申请中断 AUXR |= 0x01;AUXR |= 0x14; 实际上也可以写成AUXR |= 0x15;

image-20211109151003904

3.计算定时器2的溢出率:

波特率是 9600 ,每位采样4次。就需要定时器2每秒钟溢出 9600*4 次 =38400

4.配置定时器2初值

定时器2溢出需要的脉冲数是:24000000 ÷ 38400=625. 那么,定时器2的预装初值就是:65536-625=64911=0xFD8F

image-20211109151920616

5.启动定时器2、闭合串口1中断开关、闭合总中断开关。ES = 1

image-20211109153337500

6.中断中读取SBUF,清 RI 标志,然后返回给计算机,等待发送完毕,防重叠最少时间=4 次*10 位* 定时器时间 。再清除 TI

void Uart1_Init()
{
	SM0 = 0;
	SM1 = 1;
	REN = 1;
	//上面三条语句就等于操作SCON |= 0x50;
	PCON &= 0x3f;
	AUXR |= 0x01;
	AUXR |= 0x14;
	T2H = 0xfd;
	T2L = 0x8f;
	ES = 1;
}

串口1中断函数

串口1中断号

image-20211109153833945

==在中断里面我们干什么,进中断第一件事就是先把接收标志,发送标志给软件清零,然后再做一系列操作==

image-20211116100733554

void Uart1_Routine() interrupt 4
{
	if (RI) 
	{
		RI = 0;//先把接收标志给清零
		Uart1_Data = SBUF;
	}
	if (TI) 
	{
		TI = 0;//先把发送标志给清零
	}
}

image-20211116103654929

串口中断让单片机软复位,实现自动烧录 IAP_CONTR |= 0xe0

监控串口通信内容,如果在 9600 波特率下,连续收到 10 个字节都是 0x7f, 那么让单片机软复位,去支持 ISP 监控区域代码。实现烧录程序的目的。单片机不断电就可下载程序,方便项目调试

image-20211116143023750

==上面我们故意用最低波特率与最高波特率都是1200,小于9600,我就点了下载,(板子是上电的)没有用板子的开关复位,就是通过纯软件复位,等了几秒没有反应,说明我们用9600下载的方式是不支持1200下载的==

==9600很慢的(哈哈)下载了==

==我们发现只要最低波特率到9600就可以软复位了,越高会下载越快==

void Uart1_Routine() interrupt 4
{
	//记录接收命令流变量
	static char Uart1_ser_n = 0; 
	if (RI) 
	{
		RI = 0;//先把接收标志给清零
		Uart1_Data = SBUF;		
		if (SBUF == 0x7f) //9600波特率
		{
			//接收命令为0x7f时就++
			Uart1_ser_n++;
			if(Uart1_ser_n > 10)//大于10基本就是确定是下载命令
			{
				Uart1_ser_n = 0;
				//然后执行软复位
				IAP_CONTR |= 0xe0;	
			}
		}
	}
	if (TI) 
	{
		TI = 0;//先把发送标志给清零
	}
}

上面是发送一个字节,现在我们发送字符串

先了解一下SBUF

image-20211116153852566

为什么一直是0xFF,眼看也不是1啊是不是 ,我们用逻辑分析仪来抓取看看

image-20211116173231545

延时后

image-20211116173933293

完美,为了让你直观的看到美,我采集一下给你们看看

image-20211116174911254

我们调整上一个字节发送完成后马上发送下一个字节

我们给他一个标志位,前面一个字节发送完成后,标志位修改,然后发第二个字节,以此类推

image-20211117104324729

//串口发送底层驱动的函数
void Uart1_Up_Data_Drive()
{	
	//串口1标志为1才能发送字节,
	//可以让数据有效的发送
	if(Uart1_Flag)
	{
		Uart1_Flag = 0;
		SBUF = 1;
	}
}

//串口1中断
void Uart1_Routine() interrupt 4
{
	//记录接收命令流变量
	static char Uart1_ser_n = 0; 
	if (RI) 
	{
		RI = 0;//先把接收标志给清零	
		if (SBUF == 0x7f) //9600波特率
		{
			//接收命令为0x7f时就++
			Uart1_ser_n++;
			if(Uart1_ser_n > 10)//大于10基本就是确定是下载命令
			{
				Uart1_ser_n = 0;
				//然后执行软复位
				IAP_CONTR |= 0xe0;	
			}
		}
	}
	if (TI) 
	{
		TI = 0;//先把发送标志给清零
		//数据全部发送完成后标志位置1
		//置1后是准备发送后一个字节
		Uart1_Flag = 1;
		//调用串口发送函数
		Uart1_Up_Data_Drive();
	}
}

发字符串

image-20211117125410690

==单片机没有数据接收,串口标志就不会置1,电压就发不出去==

==我们可以看到接收缓冲区啥也没有,那就是因为发送缓冲区没有数据发送,单片机也就没有数据接收,标志没置1,然后单片机发送数据就没有一个触发的引子==

==单片机有数据接收,串口标志就会置1,电压就发出去==

==我们发送任意数据都可以,只要单片机接收到数据就行,然后串口标志就会置1,使得串口发送函数有用,我们就可以看到电压显示出来,但是细心的同学会发现,我的发送汉字缓冲区明明是换行回车,你这也没看到啊,那是SPI软件有点小毛病,我们换个软件==

发送汉字缓冲区有 \n\r换行回车

发送汉字缓冲区有 \n换行,无回车

发送汉字缓冲区无换行,有回车\r

发送汉字缓冲区无换行,无回车

所以知道为什么我在发送汉字缓冲区中写换行回车了吗

主要代码

Uart1_Drive.c

#include "all.h"

//需要显示串口的临时变量
bit Uart1_Flag = 1;
//汉字是固定的 直接code
//串口发送汉字数组缓存  先换行\n回车\r
u8 code Uart1_Up_Symbol_Buffer[30] = {"\n\r电压:  "};
//串口发送数字数组缓存   20个足够以后用的了
u8 xdata Uart1_Up_Num_Buffer[20];

//串口1初始化
void Uart1_Init()
{
	SM0 = 0;
	SM1 = 1;
	REN = 1;
	//上面三条语句就等于操作SCON |= 0x50;
	PCON &= 0x3f;
	AUXR |= 0x01;
	AUXR |= 0x14;
	T2H = 0xfd;
	T2L = 0x8f;
	ES = 1;
}

//串口发送底层驱动的函数
void Uart1_Up_Data_Drive()
{	
	static xdata u8 count = 0;
	//串口1标志为1才能发送字节,
	//可以让数据有效的发送
	if(Uart1_Flag)
	{
		Uart1_Flag = 0;
		count++; 
		switch (count)
		{
			case 1: SBUF = Uart1_Up_Symbol_Buffer[0];break;			
			case 2: SBUF = Uart1_Up_Symbol_Buffer[1];break;
			case 3: SBUF = Uart1_Up_Symbol_Buffer[2];break;
			case 4: SBUF = Uart1_Up_Symbol_Buffer[3];break;
			case 5: SBUF = Uart1_Up_Symbol_Buffer[4];break;			
			case 6: SBUF = Uart1_Up_Symbol_Buffer[5];break;
			case 7: SBUF = Uart1_Up_Symbol_Buffer[6];break;
			case 8: SBUF = Uart1_Up_Symbol_Buffer[7];break;
			case 9: SBUF = Uart1_Up_Symbol_Buffer[8];break;
			case 10:SBUF = Uart1_Up_Symbol_Buffer[9];break;
			case 12:SBUF = Uart1_Up_Num_Buffer[0];break;
			case 14:SBUF = Uart1_Up_Num_Buffer[1];break;
			case 16:SBUF = Uart1_Up_Num_Buffer[2];break;
			case 18:SBUF = Uart1_Up_Num_Buffer[3];break;
			case 20:SBUF = Uart1_Up_Num_Buffer[4];break;
			case 22:SBUF = Uart1_Up_Num_Buffer[5];break;
			case 24:count=0;break;
		}
	}
}

//串口1中断
void Uart1_Routine() interrupt 4
{
	//记录接收命令流变量
	static char Uart1_ser_n = 0; 
	if (RI) 
	{
		RI = 0;//先把接收标志给清零	
		//当接收到一个字节时,串口标志置1
		Uart1_Flag = 1;
		if (SBUF == 0x7f) //9600波特率
		{
			//接收命令为0x7f时就++
			Uart1_ser_n++;
			if(Uart1_ser_n > 10)//大于10基本就是确定是下载命令
			{
				Uart1_ser_n = 0;
				//然后执行软复位
				IAP_CONTR |= 0xe0;	
			}
		}
	}
	if (TI) 
	{
		TI = 0;//先把发送标志给清零
		//数据全部发送完成后标志位置1
		//置1后是准备发送后一个字节
		Uart1_Flag = 1;
		//调用串口发送函数
		Uart1_Up_Data_Drive();
	}
}

Uart_Drive.h

#ifndef Uart1_Drive
#define Uart1_Drive

//外部声明
extern void Uart1_Init();
extern bit Uart1_Flag;
extern void Uart1_Up_Data_Drive();
extern u8 xdata Uart1_Up_Num_Buffer[20];
#endif
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。