STC89C51基础及项目第7天:串口、串口编程、串口中断

1. 初识串口(204.49)

1.1 串口基本认知

  • 串行接口、串口,串行通信接口、串行通讯接口(通常指COM接口),采用串行通信方式的扩展接口。
  • 串行接口(Serial Interface)是指数据一位一位地顺序传送。
  • 特点是通信线路简单,只要一对传输线就可实现双向通信(可直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
  • 设备间接线通信的一种方式
  • 数据一位一位地顺序传送
  • 双向通信,全双工(*面试会问,概念记住)
  • 传送速度相对较慢

1.2 关于电器标准和协议

  • 串行接口按电气标准及协议来分,包括RS-232-C、RS-422、RS485 等。
  • RS-232-C、RS-422 与 RS-485 标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

RS-232

  • 也称标准串口,最常用的一种串行通讯接口,如电脑主机的9针串口
  • 最高速率为20kb/s
  • 是为点对点(即只用一对 收发设备)通讯而设计的,其传送距离最大为约15米,所以其适合本地设备之间的通信
    在这里插入图片描述

RS-422

  • 由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余10个为从设备(Slave),从设备之间不能通信,所以支持点对多的双向通信
  • 其最大传输距离为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比

RS-485

  • 是从RS-422基础上发展而来的,无论四线还是二线连接方式,总线上最多接到32个设备
    在这里插入图片描述

1.3 关于串口的电平

  • 异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。
  • UART包含TTL电平的串口和RS232电平的串口。

RS232电平

  • 逻辑 1 为 -3 ~ -15V 的电压,逻辑 0 为 3 ~ 15V 的电压
  • 笔记本通过RS232电平和单片机通信
    在这里插入图片描述
    在这里插入图片描述

TTL电平

  • TTL是Transistor-Transistor Logic,即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。
  • TTL电平信号应用广泛,是因为其数据表示采用二进制规定,+5V 等价于逻辑“1”,0V等价于逻辑”0”。
  • 数字电路中,由TTL电子元器件组成电路的电平是个电压范围,规定:
    • 输出高电平>=2.4V,输出低电平<=0.4V;
    • 输入高电平>=2.0V,输入低电平<=0.8V
  • 笔记本电脑通过TTL电平与单片机通信:
    • TX发送线(端口)3.1
    • RX接收线(端口)3.0
  • USB转TTL,使用ch340通信
    在这里插入图片描述
    在这里插入图片描述
  • 上官一号
    在这里插入图片描述
    在这里插入图片描述

2. 概念思维整理补充(206.51)

  • 一套代码烧到不同的开发板,结果都一样

3. 串口编程的关键要素提点(207.52)

串口通信

3.1 串口接线方式

  • RXD:数据输入引脚,数据接收;STC89系列对应P3.0口,上官一号有单独引出
  • TXD:数据发送引脚,数据发送;STC89系列对应P3.1口,上官一号有单独引出
  • 接线方式
    在这里插入图片描述
    在这里插入图片描述

3.2 串口编程要素

印象塑造

  • 输入/输出数据缓冲器都叫做SBUF,都用99H地址码,但是是两个独立的8位寄存器
  • 代码体现为: 想要接收数据 char data = SBUF 想要发送数据 SBUF = data
    在这里插入图片描述
  • 回忆UART是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信
    速度,叫做波特率
  • 对于电脑来说,别人做好了软件,鼠标点点点就能配置好,而单片机的波特率配置需要我们写
    代码
  • 点点点配置什么,我们代码也要配置对应参数
    在这里插入图片描述
  • 直接写代码先玩一下再学概念和数据时序(208.53)

4. 编程实现每秒发送数据给电脑(208.53)

  • 代码(14./串口通信01_发送一个字符a给PC)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
										//而".h"会首先在当前工作目录或指定的包含路径下查找。

sfr AUXR=0x8E;

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
}

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 main()
{
	char data_msg='a';
	
	//配置C51串口的通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		SBUF=data_msg;
	}
}

5. 串口编程寄存器分析(209.54)(*多听多理解)

  • 8421码学会

6. 串口编程01_白己实现串口初始化(210.55)(*多听多理解)

  • 代码(14./串口通信01_发送一个字符a给PC2)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
										//而".h"会首先在当前工作目录或指定的包含路径下查找。
sfr AUXR=0x8E;

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x40;//选择串口工作方式1,REN不使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;   //定时器1开始工作
}

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 main()
{
	char data_msg='a';
	
	//配置C51串口的通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		SBUF=data_msg;
	}
}

7. 串口编程02_发送字符串(211.56)

  • 代码(14./串口通信02_发送字符串给PC)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
										//而".h"会首先在当前工作目录或指定的包含路径下查找。
sfr AUXR=0x8E;

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x40;//选择串口工作方式1,REN不使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
}

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 sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
		
}
void main()
{
	//配置C51串口的通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		sendString("Hello,Jessie!\r\n");//串口中要写\r\n
	}
}

8. 串口通信编程03_PC发送指令控制LED(212.57)

  • 代码(14./串口通信03_PC通过串口点亮LED)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
										//而".h"会首先在当前工作目录或指定的包含路径下查找。
sfr AUXR = 0x8E;
sbit D5  = P3^7;

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
}

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 sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}		
}

void main()
{
	char cmd;
	D5=1;
	
	//配置C51串口的通信方式
	UartInit();
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		sendString("Hello,Jessie!\r\n");//串口中\r\n换行
		if(RI == 1){//怎么知道收到了数据,查询RI的值,如果RI是1 (收到数据后由便件置1)
			RI=0;     //须软件复位,清除接收中断标志位
			cmd=SBUF; //读数据给cmd
			if(cmd == 'o'){
				D5=0;		//点亮D5
			}
			if(cmd == 'c'){
				D5=1;		//熄灭D5
			}
		}
	}
}

9. 串口通信编程04_串口中断实时控制LED(213.58)

  • 代码(14./串口通信04_PC串口中断控制LED)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
										//而".h"会首先在当前工作目录或指定的包含路径下查找。
sfr AUXR = 0x8E;
sbit D5  = P3^7;
char cmd;

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
	
	EA    = 1;//开启总中断
	ES		= 1;//开启串口中断
}

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 sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
}

void main()
{
	D5=1;
	
	//配置C51串口的通信方式
	UartInit();
	while(1)
	{								//像心跳包
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		sendString("Hello,Jessie!\r\n");//串口中要写\r\n
	}
}

void UartHepler() interrupt 4//中断有点像多任务线程
{
	if(RI)			  //中断处理函数中,对于接收中断的响应
	{
			RI=0;     //须软件复位,清除接收中断标志位
			cmd=SBUF; //读数据给cmd
			if(cmd == 'o'){
				D5=0;		//点亮D5
			}
			if(cmd == 'c'){
				D5=1;		//熄灭D5
			}
	}
	
	if(TI);
}

10. 小白玩串口控制的ASSII避坑(214.59)

  • 代码(14./串口通信04_PC串口中断控制LED2)
void UartHepler() interrupt 4//中断有点像多任务线程
{
	if(RI)			  //中断处理函数中,对于接收中断的响应
	{
			RI=0;     //须软件复位,清除接收中断标志位
			cmd=SBUF; //读数据给cmd
			if(cmd == 1){
				D5=0;		//点亮D5
			}
			if(cmd == 0){
				D5=1;		//熄灭D5
			}					//1和0要用HEX模式发送
								//如果要用文本模式,字符1和0对应ASCII码的十进制为49和48
	}
	
	if(TI);
}
  • ASCII表
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

11. 串口支持单词型指令控制(215.60)

  • 串口接收通常是字节流的方式进行的,即每个字节逐个传输和接收。要接收一个完整的字符串,你需要在接收端进行缓冲和拼接,以便将连续的字节组合成一个字符串。以下是一种常见的方法:
  1. 创建一个字符数组(缓冲区),用于存储接收到的字节。缓冲区的大小应该足够大,以容纳你预期的最大字符串长度,同时需要一个额外的字节来存储字符串的终止符,通常是 null 终止符 \0。
#define BUFFER_SIZE 128
char receiveBuffer[BUFFER_SIZE + 1];  // +1 用于存储 null 终止符
  1. 在串口接收中断处理函数中,当每个字节被接收时,将它存储到缓冲区,并维护一个变量来跟踪当前接收到的字节数。
void UartHelper() interrupt 4 {
    if (RI) {
        RI = 0;  // 清除接收中断标志位

        if (receiveIndex < BUFFER_SIZE) {
            receiveBuffer[receiveIndex] = SBUF;
            receiveIndex++;
        }
    }
}
  1. 在接收数据时,你可以检查是否接收到了预期的终止字符(例如,换行符 \n 或回车符 \r),以确定字符串的结束。一旦接收到终止字符,你可以将接收到的字节序列视为一个完整的字符串。
  2. 请注意,你需要在接收到终止字符后,在 receiveBuffer 中添加 null 终止符 \0,以便将它转换为 C 风格的字符串。
// 检查是否接收到终止字符
if (receiveBuffer[receiveIndex - 1] == '\n' || receiveBuffer[receiveIndex - 1] == '\r') {
    // 添加 null 终止符
    receiveBuffer[receiveIndex] = '\0';
    // 处理接收到的字符串(receiveBuffer)
    // 清空缓冲区,以便接收下一个字符串
    memset(receiveBuffer, 0, sizeof(receiveBuffer));
    receiveIndex = 0;
}
  1. 这个流程允许你在接收串口数据时将连续的字节组合成一个字符串,并在接收到终止字符后将其视为完整的字符串。接下来,你可以对接收到的字符串进行解析或执行其他操作,具体取决于你的应用需求。
  • 两种存储数据方式的区别
    在这里插入图片描述
  • 代码(串口通信05_字符串型指令如何处理)
#include "reg52.h"
#include "intrins.h"//<.h>会首先在标准库路径下查找头文件,
#include <string.h>										//而".h"会首先在当前工作目录或指定的包含路径下查找。

#define SIZE 12
sfr AUXR = 0x8E;
sbit D5  = P3^7;
char cmd[SIZE];

void UartInit() 
{
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x50;//选择串口工作方式1,REN使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
	
	EA    = 1;//开启总中断
	ES		= 1;//开启串口中断
}

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 sendByte(char data_msg)
{
	SBUF=data_msg;   //移位寄存器会耗时
	//Delay10ms();   //不准确的延时
	//while(TI == 0);//智能延时
	while(!TI);      //等待串口发送完成
	TI=0;			 //发送中断请求标志位,发送结束时硬件自动置1,后须软件复位0
}
void sendString(char* str)//直接指向字符串的内存地址
{
	while(*str != '\0'){//字符串的结尾是\0
		sendByte(*str);
		str++;
	}
}

void main()
{
	D5=1;
	
	//配置C51串口的通信方式
	UartInit();
	while(1)
	{							
		Delay1000ms();
		//往发送缓冲区写入数据,即完成数据的发送
		sendString("Hello,Jessie!\r\n");//串口中要写\r\n
	}
}

void UartHepler() interrupt 4//中断有点像多任务线程
{
	static int i=0;  //静态变量,只被初始化一次//面试时会问
	
	if(RI)				   //中断处理函数中,对于接收中断的响应
	{
			RI=0;        //须软件复位,清除接收中断标志位
			cmd[i]=SBUF; //读数据给cmd
			i++;
			if(i==SIZE)	 //使用循环缓冲区的方式存储数据
			{
				i=0;
			}
			if(strstr(cmd,"open")){
				D5=0;		//点亮D5
				i=0;
				memset(cmd,'\0',SIZE);
			}
			if(strstr(cmd,"close")){
				D5=1;		//熄灭D5
				i=0;
				memset(cmd,'\0',SIZE);
			}
	}
	
	if(TI);
}

12. 串口原理协议概念收尾(216.61)(*多听多理解)

  • 串口、校验位、中断
  • 编程出问题时,可先阅读手册,再去百度查阅
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值