51单片机—串口
一、 串口基本认知
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成 本,特别适用于远距离通信,但传送速度较慢
- 是设备间接线通信的一种方式
- 数据一位一位地顺序传送
- 双向通信,全双工

-
- 半双工:只能单向通讯
- 全双工:可以双向通讯
- 传送速度相对较慢
1.1 关于电器标准和协议
串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485 标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。
- RS-232
也称标准串口,最常用的一种[串行通讯接口,比如我们的电脑主机的9针串口 ,最高速率为20kb/s
RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其传送距离最大为约15米。所以RS-232适合本地设备之间的通信

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

1.2 关于串口的电平
经常听说的UART
异步串行是指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通信

1.3 串口通信
1.3.1 串口接线方式
RXD:数据输入引脚,数据接受;STC89系列对应P3.0口,上官一号有单独引出
TXD:数据发送引脚,数据发送;STC89系列对应P3.1口,上官一号有单独引出
接线方式

1.3.2 串口编程要素
- 印象塑造
输入/输出数据缓冲器都叫做SBUF, 都用99H地址码,但是是两个独立的8位寄存器
代码体现为: 想要接收数据 char data = SBUF 想要发送数据 SBUF = data

回忆UART是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信速度,叫做波特率 (刘翔和我跑步)
对于电脑来说,别人做好了软件,鼠标点点点就能配置好,而苦逼单片机的波特率配置需要我们写代码
点点点配置什么,我们代码也要配置对应参数

直接写代码先玩一下再学概念和数据时序
这部分内容位实操,直接看视频

通过视频弄清楚相关寄存器的配置以及串口的工作模式

字符 'a' 是如何从单片机上传到PC的
a的ASSII码是97,16进制就是0x61, 二进制是01010001,这个8位就是数据位串口工作模式1,一帧数据有10位,起始位(0),数据位,停止位(1)
那么a的一帧数据就是 0 1000 1010 1 起始位,a的低位到高位,停止位
- 除了速度要求,还要有数据格式,双方 暗号 对上了再发数据,所以有起始位,和停止位 的概念

一个字节有8位,比如字母‘a’的ASSII码是十进制97,二进制是 0110 0001 ,一次从地位到高位发送,接收也是

1.4 编程实现
#include "reg52.h"
#include "intrins.h"
//每隔一秒给PC发送一个字符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) //9600bps@11.0592MHz
{//8421//0111 1111PCON &= 0x7F; //波特率不倍速//这行代码是为了设置波特率不倍速传输。PCON寄存器的第7位(SMOD位)为波特率倍速控制位,置0表示不倍速,即波特率按正常速度传输。//0101 0000 0101 B7B6SCON = 0x50; //8位数据,可变波特率// 1100 1111 // 降低辐射AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12TAUXR &= 0xFE; //串口1选择定时器1为波特率发生器TMOD &= 0x0F; //清除定时器1模式位// 0010 000 定时器1 10TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFD; //设定定时初值TH1 = 0xFD; //设定定时器重装值/* 在8位自动重装模式(模式2)下,定时器1每次从TL1计数到0xFF(即256次计数),* 然后溢出,并自动从TH1中重新装载初值。这样,定时器1就会在0xFD到0xFF之间循环计数。* 设定TL1和TH1为0xFD,使定时器1从0xFD开始计数到0xFF,每次计数3个时钟周期(256 - 0xFD = 3)。* 这样可以实现特定的时间间隔,用于控制串口通信的波特率。*/ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}void main()
{char data_msg = 'a';UartInit();while(1){Delay1000ms();SBUF = data_msg;}
}
- 简化
void UartInit(void) //9600bps@11.0592MHz
{AUXR = OX01;// 降低辐射//0101 0000 0101 B7B6SCON = 0x40; //8位数据,可变波特率TMOD &= 0x0F; //清除定时器1模式位// 0010 000 定时器1 10TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFD; //设定定时初值TH1 = 0xFD; //设定定时器重装值/* 在8位自动重装模式(模式2)下,定时器1每次从TL1计数到0xFF(即256次计数),* 然后溢出,并自动从TH1中重新装载初值。这样,定时器1就会在0xFD到0xFF之间循环计数。* 设定TL1和TH1为0xFD,使定时器1从0xFD开始计数到0xFF,每次计数3个时钟周期(256 - 0xFD = 3)。* 这样可以实现特定的时间间隔,用于控制串口通信的波特率。*/ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1
}
- 串口发送一个字符串
#include "reg52.h"
#include "intrins.h"
//每隔一秒给PC发送一个字符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 Delay10ms() //@11.0592MHz
{unsigned char i, j;i = 18;j = 235;do{while (--j);} while (--i);
}void UartInit(void) //9600bps@11.0592MHz
{//8421//0111 1111PCON &= 0x7F; //波特率不倍速//这行代码是为了设置波特率不倍速传输。PCON寄存器的第7位(SMOD位)为波特率倍速控制位,置0表示不倍速,即波特率按正常速度传输。//0101 0000 0101 B7B6SCON = 0x50; //8位数据,可变波特率 配置串口工作模式//1100 1111AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12TAUXR &= 0xFE; //串口1选择定时器1为波特率发生器TMOD &= 0x0F; //清除定时器1模式位// 0010 000 定时器1 10模式TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFD; //设定定时初值TH1 = 0xFD; //设定定时器重装值/* 在8位自动重装模式(模式2)下,定时器1每次从TL1计数到0xFF(即256次计数),* 然后溢出,并自动从TH1中重新装载初值。这样,定时器1就会在0xFD到0xFF之间循环计数。* 设定TL1和TH1为0xFD,使定时器1从0xFD开始计数到0xFF,每次计数3个时钟周期(256 - 0xFD = 3)。* 这样可以实现特定的时间间隔,用于控制串口通信的波特率。*/ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1}void sendByte(char date_msg)
{SBUF = date_msg;while(!TI);TI =0;
}
void sendString(char* str)
{ while(*str != '\0'){sendByte(*str);str++;}}
void main()
{//char data_msg = 'a';UartInit();while(1){Delay1000ms();sendString("hellod World\r\n");}
}
在代码中,SBUF = date_msg; 和 while(!TI); TI = 0; 这两行的作用是通过串口发送一个字节的数据,并等待发送完成。具体解释如下:
SBUF = date_msg;
-
SBUF是串口数据缓冲寄存器。将date_msg的值写入SBUF寄存器,表示将要通过串口发送这个字节数据。
while(!TI);
-
TI是串口发送中断标志位(Transmit Interrupt flag)。当发送缓冲区中的数据被移位寄存器取走并发送完成时,硬件自动将TI置1,表示发送完成。while(!TI);表示等待TI被置1,即等待当前字节的数据发送完成。如果TI为0,则等待(忙等待)。
TI = 0;
-
- 将
TI置0,以便下次发送时能够再次检测到发送完成状态。
- 将
这段代码的延时作用在于确保在发送下一个字节之前,当前字节已经通过串口发送完毕,避免数据冲突和丢失。也就是说,while(!TI); 是一种同步机制,确保串口发送数据的顺序和完整性。
-
- 串口中断
#include "reg52.h"
#include "intrins.h"
//每隔一秒给PC发送一个字符sfr AUXR = 0x8E;
sbit D5 = P3^7;char cmd;
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 Delay10ms() //@11.0592MHz
{unsigned char i, j;i = 18;j = 235;do{while (--j);} while (--i);
}
void UartInit(void) //9600bps@11.0592MHz
{//8421//0111 1111PCON &= 0x7F; //波特率不倍速//这行代码是为了设置波特率不倍速传输。PCON寄存器的第7位(SMOD位)为波特率倍速控制位,置0表示不倍速,即波特率按正常速度传输。//0101 0000 0101 B7B6SCON = 0x50; //8位数据,可变波特率 配置串口工作模式 REN使能接收//1100 1111AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12TAUXR &= 0xFE; //串口1选择定时器1为波特率发生器TMOD &= 0x0F; //清除定时器1模式位// 0010 000 定时器1 10模式TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFD; //设定定时初值TH1 = 0xFD; //设定定时器重装值/* 在8位自动重装模式(模式2)下,定时器1每次从TL1计数到0xFF(即256次计数),* 然后溢出,并自动从TH1中重新装载初值。这样,定时器1就会在0xFD到0xFF之间循环计数。* 设定TL1和TH1为0xFD,使定时器1从0xFD开始计数到0xFF,每次计数3个时钟周期(256 - 0xFD = 3)。* 这样可以实现特定的时间间隔,用于控制串口通信的波特率。*/ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1ES = 1; // 开启串口中断EA = 1; // 开启总中断}
void sendByte(char date_msg)
{SBUF = date_msg;while(!TI); // 发送 TI 接收RITI =0;
}
void sendString(char* str)
{ while(*str != '\0'){sendByte(*str);str++;}}
void main()
{//char data_msg = 'a';D5 = 1; //默认高电压UartInit();while(1){Delay1000ms();//往发送缓冲区写入数据,就完成数据的发送sendString("hellod World\r\n");}
}
void Uart_Handler() interrupt 4
{if(RI){ //中断处理函数中,对于接收中断的响应RI = 0; //清除接收中断标志位cmd = SBUF;if(cmd == 'O'){D5 = 0; //点亮 D5}if(cmd == 'C'){D5 = 1; //熄灭 D5}}if(TI);}
- 字符串作比较
#include "reg52.h"
#include "intrins.h"
#include <string.h>
//每隔一秒给PC发送一个字符#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;char cmd[SIZE];
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 Delay10ms() //@11.0592MHz
{unsigned char i, j;i = 18;j = 235;do{while (--j);} while (--i);
}void UartInit(void) //9600bps@11.0592MHz
{//8421//0111 1111PCON &= 0x7F; //波特率不倍速//这行代码是为了设置波特率不倍速传输。PCON寄存器的第7位(SMOD位)为波特率倍速控制位,置0表示不倍速,即波特率按正常速度传输。//0101 0000 0101 B7B6SCON = 0x50; //8位数据,可变波特率 配置串口工作模式 REN使能接收//1100 1111AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12TAUXR &= 0xFE; //串口1选择定时器1为波特率发生器TMOD &= 0x0F; //清除定时器1模式位// 0010 000 定时器1 10模式TMOD |= 0x20; //设定定时器1为8位自动重装方式TL1 = 0xFD; //设定定时初值TH1 = 0xFD; //设定定时器重装值/* 在8位自动重装模式(模式2)下,定时器1每次从TL1计数到0xFF(即256次计数),* 然后溢出,并自动从TH1中重新装载初值。这样,定时器1就会在0xFD到0xFF之间循环计数。* 设定TL1和TH1为0xFD,使定时器1从0xFD开始计数到0xFF,每次计数3个时钟周期(256 - 0xFD = 3)。* 这样可以实现特定的时间间隔,用于控制串口通信的波特率。*/ET1 = 0; //禁止定时器1中断TR1 = 1; //启动定时器1ES = 1; // 开启串口中断EA = 1; // 开启总中断}void sendByte(char date_msg)
{SBUF = date_msg;while(!TI); // 发送 TI 接收RITI =0;
}
void sendString(char* str)
{ while(*str != '\0'){sendByte(*str);str++;}}
void main()
{//char data_msg = 'a';D5 = 1; //默认高电压UartInit();while(1){Delay1000ms();//往发送缓冲区写入数据,就完成数据的发送sendString("hellod World\r\n");}
}void Uart_Handler() interrupt 4
{static int i = 0; //静态的局部变量,只被函数初始化调用一次if(RI){ //中断处理函数中,对于接收中断的响应RI = 0; //清除接收中断标志位cmd[i] = SBUF;i++;if(i == SIZE){i = 0}if(strstr(cmd,"op")){ // strcmp 比较D5 = 0; //点亮 D5i = 0;memset(cmd,'\0',SIZE);}if(strstr(cmd,"cl")){D5 = 1; //熄灭 D5i = 0;memset(cmd,'\0',SIZE);}}if(TI);}
- 串口的协议
- 波特率:10位 1位起始位,8位数据位,1位停止位
字符‘a’,阿斯克码表:97 十六进制为:61 二进制:0110 0001 (起+数据位+停止位 )
二、 蓝牙模块
蓝牙模块,又叫做蓝牙串口模块
串口透传技术
透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据及协议栈工作原理,只要通过串口编程获得数据即可

2.1 Wifi模块-ESP-01s
蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计
2.1.1 AT指令
简介
AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符)。
每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结尾,响应或上报以回车换行为结尾。
2.1.2 初始配置和验证
ESP-01s出厂波特率正常是115200, 注意:AT指令,控制类都要加回车,数据传输时不加回车
- 上电后,通过串口输出一串系统开机信息,购买的部分模块可能电压不稳,导致乱码,以 ready 为准
- 上电后发送AT指令测试通信及模块功能是否正常
ATOK
- 通过一下命令配置成9600波特率
AT+UART=9600,8,1,0,0
2.1.3 入网设置
- 设置工作模式
AT+CWMODE=3 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模OK
- 以设备模式接入家中路由器配置
AT+CWJAP="301","IG301666"//指令
WIFI CONNECTED //结果
WIFI GOT IP //结果
- 查询IP地址
AT+CIFSR //指令
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"4e:75:25:0d:ae:2f"
+CIFSR:STAIP,"192.168.0.148"
+CIFSR:STAMAC,"4c:75:25:0d:ae:2f"Ok

2.1.4 连接到 TCP server
- 开关网络助手,设立TCP服务器

1. 连接服务器
AT+CIPSTART="TCP","192.168.3.13",8880 //指令,注意双引号逗号都要半角(英文)输入
CONNECT //结果:成功
OK /
2. 发送数据
AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节)
>CLCA // 看到大于号后,输入消息,CLCA,不要带回车
Response :SEND OK //结果:成功
//注意,这种情况下,每次发送前都要先发送AT+CIPSEND=长度 的指令,再发数据!
2.1.5 透传
上一节每次发送数据都要进行字符长度设定,如果设置成透传,就有点像蓝牙模块的玩法
AT+CIPMODE=1 //开启透传模式
Response :OK
AT+CIPSEND //带回车
Response: > //这个时候随意发送接收数据咯
退出透传模式
//在透传发送数据过程中,若识别到单独的⼀包数据 “+++”,则退出透传发送
2.1.6 单片机帮你做这一切
- AT指令
#include "reg52.h"
#include "intrins.h"
#include <string.h>#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];// code 指定字符存储位置,以防存储空间过大
code char LJWL[] = "AT+CWJAP=\"123\",\"123456789\"\r\n"; code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.164.25\",8888\r\n"; char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[] = "AT+CIPSEND\r\n";void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 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;while(!TI);TI = 0;
}void sendString(char* str)
{while( *str != '\0'){sendByte(*str);str++;}
}void main()
{D5 = 1;//配置C51串口的通信方式UartInit();while(1){//Delay1000ms();//往发送缓冲区写入数据,就完成数据的发送//sendString("lxl shuai\r\n");sendString(LJWL);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(LJFWQ);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(TCMS);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(SJCS);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();}
}void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位cmd[i] = SBUF;i++;if(i == SIZE){i = 0;}if(strstr(cmd,"en")){D5 = 0;//点亮D5i = 0;memset(cmd,'\0',SIZE);}if(strstr(cmd,"se")){D5 = 1;//熄灭D5i = 0;memset(cmd,'\0',SIZE);}}if(TI);
}
- 通过TCP通信点灯
#include "reg52.h"
#include "intrins.h"
#include <string.h>#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
char cmd[SIZE];code char LJWL[] = "AT+CWJAP=\"123\",\"123456789\"\r\n";
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.164.25\",8888\r\n"; char TCMS[] = "AT+CIPMODE=1\r\n";
char SJCS[] = "AT+CIPSEND\r\n";void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 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;while(!TI);TI = 0;
}void sendString(char* str)
{while( *str != '\0'){sendByte(*str);str++;}
}void main()
{int mark = 0;D5 = 1;//配置C51串口的通信方式UartInit();while(1){//Delay1000ms();//往发送缓冲区写入数据,就完成数据的发送//sendString("chenlichen shuai\r\n");if(mark == 0){sendString(LJWL);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(LJFWQ);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(TCMS);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();sendString(SJCS);Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();Delay1000ms();mark = 1;}else{sendString("lxl shuai\r\n");Delay1000ms();}}
}void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位cmd[0] = SBUF;if(cmd[0] == '1'){D5 = 0;//点亮D5}if(cmd[0] == '0'){D5 = 1;//熄灭D5}}if(TI);
}
注意:有时间发送AT指令,会出现错误。连接不上服务器。IP会连不上。
优化连接过程
#include "reg52.h"
#include "intrins.h"
#include <string.h>#define SIZE 12
sfr AUXR = 0x8E;
sbit D5 = P3^7;
sbit D6 = P3^6;char buffer[SIZE];
code char LJWL[] = "AT+CWJAP=\"123\",\"123456789\"\r\n"; //入网指令
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.164.25\",8888\r\n"; //连接服务器指令
char TCMS[] = "AT+CIPMODE=1\r\n"; //透传指令
char SJCS[] = "AT+CIPSEND\r\n"; //数据传输开始指令
char RESET[] = "AT+RST\r\n"; //重启模块指令
char AT_OK_Flag = 0; //OK返回值的标志位
char AT_Connect_Net_Flag = 0; //WIFI GOT IP返回值的标志位void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 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;while(!TI);TI = 0;
}void sendString(char* str)
{while( *str != '\0'){sendByte(*str);str++;}
}void main()
{int mark = 0;D5 = D6 = 1;//灭状态灯//配置C51串口的通信方式UartInit();Delay1000ms();//给espwifi模块上电时间//发送联网AT指令并等待成功sendString(LJWL); // 连接wifi//while(!AT_Connect_Net_Flag);while(!AT_OK_Flag);AT_OK_Flag = 0;//发送连服务器指令并等待成功sendString(LJFWQ); // 连接服务器while(!AT_OK_Flag);AT_OK_Flag = 0;//发送透传模式指令并等待成功sendString(TCMS); //透传指令while(!AT_OK_Flag);AT_OK_Flag = 0;//发送数据传输指令并等待成功sendString(SJCS); //发送数据传输while(!AT_OK_Flag);if(AT_Connect_Net_Flag){D5 = 0;//点亮D5,代表入网成功}if(AT_OK_Flag){D6 = 0;//点亮D6,代表连接服务器并打开透传模式成功}while(1){Delay1000ms();//“心跳包”sendString("lxl shuai\r\n");}
}void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == 'F'){i = 0;}buffer[i++] = tmp;//入网成功的判断依据WIFI GOT IPif(buffer[0] == 'W' && buffer[5] == 'G'){AT_Connect_Net_Flag = 1;memset(buffer, '\0', SIZE);}//连接服务器等OK返回值指令的判断if(buffer[0] == 'O' && buffer[1] == 'K'){AT_OK_Flag = 1;memset(buffer, '\0', SIZE);}//联网失败出现FAIL字样捕获 重启模块if(buffer[0] == 'F' && buffer[1] == 'A'){for(i=0;i<5;i++){D5 = 0;Delay1000ms();D5 = 1;Delay1000ms();}sendString(RESET);memset(buffer, '\0', SIZE);}//灯控指令if(buffer[0] == 'L' && buffer[2] == '1'){D5 = 0;//点亮D5memset(buffer, '\0', SIZE);}if(buffer[0] == 'L' && buffer[2] == '0'){D5 = 1;//熄灭D5memset(buffer, '\0', SIZE);}if(i == 12) i = 0;}}
3.5. 7 ESP-01s当服务器
USB转TTL插入电脑,TX--RX RX-TX VCC-3.3V GDN-GND
查询IP地址:AT+CIFSR\
//1 配置成双模
AT+CWMODE=2
Response :OK
//2 使能多链接
AT+CIPMUX=1
Response :OK
//3 建立TCPServer
AT+CIPSERVER=1 // default port = 333
Response :OK
//4 发送数据
AT+CIPSEND=0,4 // 发送4个字节在连接0通道上
>abcd //输入数据,不带回车
Response :SEND OK
//• 接收数据
+IPD, 0, n: xxxxxxxxxx //+IPD是固定字符串 0是通道,n是数据长度,xxx是数据
//断开连接
AT+CIPCLOSE=0
Response :0, CLOSED OK
//ESP-01s工作在路由模式,课程查询路由器IP地址192.168.4.1,使用的服务器默认端口号333
//ESP-01s收到收到数据op/cl给上官一号,实现D6led的亮/灭
#include "reg52.h" // 包含reg52.h头文件,定义51系列单片机的特殊功能寄存器
#include "intrins.h" // 包含intrins.h头文件,定义_nop_()等内联汇编函数
#include <string.h> // 包含string.h头文件,用于字符串操作#define SIZE 12 // 定义缓冲区大小为12
sfr AUXR = 0x8E; // 定义特殊功能寄存器AUXR的地址
sbit D5 = P3^7; // 定义D5为P3端口的第7位
sbit D6 = P3^6; // 定义D6为P3端口的第6位char buffer[SIZE]; // 定义一个字符数组buffer,大小为SIZE// 一些AT命令字符串
char LYMO[] = "AT+CWMODE=2\r\n"; // 设置工作在路由模式
char DLJ[] = "AT+CIPMUX=1\r\n"; // 使能多链接
char JLFW[] = "AT+CIPSERVER=1\r\n"; // 建立TCP服务器,默认端口333
char FSSJ[] = "AT+CIPSEND=0,5\r\n"; // 发送数据指令// 标志位,用于记录不同的状态
char AT_OK_Flag = 0; // OK返回值的标志位
char AT_Connect_Net_Flag = 0; // WIFI GOT IP返回值的标志位
char Client_Connect_Flag = 0; // 客户端连接标志位// 初始化串口,波特率9600bps@11.0592MHz
void UartInit(void)
{AUXR = 0x01; // 设置定时器1的工作模式SCON = 0x50; // 设置串口工作方式1,REN使能接收TMOD &= 0xF0; // 清除定时器1的工作模式位TMOD |= 0x20; // 设置定时器1为8位自动重装模式TH1 = 0xFD; // 设置定时器1初值,波特率9600TL1 = 0xFD; TR1 = 1; // 启动定时器1EA = 1; // 开启总中断ES = 1; // 开启串口中断
}// 延时1000毫秒
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;while(!TI);TI = 0;
}// 发送字符串
void sendString(char* str)
{while( *str != '\0'){sendByte(*str);str++;}
}// 主函数
void main()
{int mark = 0;D5 = D6 = 1; // 熄灭状态灯UartInit(); // 配置串口通信方式Delay1000ms(); // 给ESP8266模块上电时间sendString(LYMO); // 发送设置路由模式命令while(!AT_OK_Flag); // 等待OK返回AT_OK_Flag = 0; sendString(DLJ); // 发送使能多链接命令while(!AT_OK_Flag); // 等待OK返回AT_OK_Flag = 0; sendString(JLFW); // 发送建立TCP服务器命令while(!Client_Connect_Flag); // 等待客户端连接AT_OK_Flag = 0; if(Client_Connect_Flag){D5 = 0; // 点亮D5,表示有客户端接入D6 = 0; }while(1){sendString(FSSJ); // 发送发送数据指令Delay1000ms();Delay1000ms();sendString("Hello"); // 发送数据Delay1000ms();Delay1000ms();}
}// 串口中断处理函数
void Uart_Handler() interrupt 4
{static int i = 0; // 静态变量i,只初始化一次char tmp;if(RI) // 接收中断处理{RI = 0; // 清除接收中断标志位tmp = SBUF; // 读取接收到的数据if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == '0' || tmp == ':'){i = 0; // 如果接收到特定字符,重置缓冲区索引}buffer[i++] = tmp; // 将接收到的数据存入缓冲区if(buffer[0] == 'W' && buffer[5] == 'G'){AT_Connect_Net_Flag = 1; // 判断是否收到"WIFI GOT IP"memset(buffer, '\0', SIZE); // 清空缓冲区}if(buffer[0] == 'O' && buffer[1] == 'K'){AT_OK_Flag = 1; // 判断是否收到"OK"memset(buffer, '\0', SIZE); // 清空缓冲区}if(buffer[0] == '0' && buffer[2] == 'C'){Client_Connect_Flag = 1; // 判断是否收到客户端连接memset(buffer, '\0', SIZE); // 清空缓冲区}if(buffer[0] == ':' && buffer[1] == 'o' && buffer[2] == 'p'){D5 = 0; // 点亮D5memset(buffer, '\0', SIZE); // 清空缓冲区}if(buffer[0] == ':' && buffer[1] == 'c' && buffer[2] == 'l'){D5 = 1; // 熄灭D5memset(buffer, '\0', SIZE); // 清空缓冲区}if(i == 12) i = 0; // 如果缓冲区满了,重置索引}
}
代码功能解释
- 头文件和宏定义:
-
- 包含
reg52.h和intrins.h头文件,定义了一些必要的寄存器和内联函数。 - 使用
#define SIZE 12定义缓冲区大小为12。
- 包含
- 全局变量和AT命令字符串:
-
- 定义了一些全局变量和AT命令字符串,用于配置ESP8266模块。
- 串口初始化函数
UartInit:
-
- 配置51单片机的串口,设置波特率为9600bps,并启用串口中断。
- 延时函数
Delay1000ms:
-
- 实现了一个大约1000毫秒的延时函数。
- 发送数据函数
sendByte和sendString:
-
sendByte:发送一个字节的数据。sendString:发送一个字符串。
- 主函数
main:
-
- 初始化串口,并配置ESP8266模块为路由模式、多链接模式和建立TCP服务器。
- 在有客户端连接后,点亮指示灯并不断发送数据。
- 串口中断处理函数
Uart_Handler:
-
- 处理接收到的数据,根据不同的返回值设置相应的标志位,并根据特定指令控制指示灯的状态。
相关文章:
51单片机—串口
一、 串口基本认知 串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单&a…...
vue 通过 this.$refs 创建方法i向子组件传参让子组件更新
在Vue中,this.$refs主要用于访问子组件的实例或者DOM元素。通过this.$refs,你可以调用子组件的方法或者访问其数据属性,从而实现子组件的更新。以下是一些使用this.$refs向子组件传参并触发更新的常见方法: 1. 直接调用子组件的方…...
Java设计模式以及代理模式
一、设计模式 1.JAVA六大设计原则 JAVA设计模式提供六个基本原则,分别是: 开闭原则(OCP) - The Open-Closed Principle单一职责原则(SRP) - Single Responsibility Principle里氏替换原则(LSP) - Liskov Substitution Principle依赖倒置原则(DIP) - D…...
Elasticsearch 索引库管理:查询、修改与删除
Elasticsearch 是一个高度可扩展的开源全文搜索和分析引擎,它允许用户通过 RESTful API 进行数据的索引、搜索、更新和管理。在 Elasticsearch 中,索引库(Index)是存储数据的基本单元。本文将介绍如何查询、修改和删除 Elasticsea…...
视频大怎么压缩小?分享3种视频压缩方法
视频大怎么压缩小?视频文件过大时,压缩视频不仅能帮助我们节省宝贵的存储空间,使其更容易在有限容量的设备中保存,还能显著提升传输效率,特别是在网络条件有限或需要快速分享视频内容的场合。通过专业的压缩工具&#…...
springboot项目搭建集成 redis/跨域/远程请求
目录 一,创建maven项目 1,左上角file > new > maven project 2,next 到 创建 Group id 和 Artifact id编辑编辑 二,配置springboot 1,配置pom文件, 2,创建启动类 3ÿ…...
lvs详解及实例配置
目录 1.什么是负载均衡 1.1为什么用负载均衡 1.2.负载均衡类型 1.2.1.四层负载均衡 1.2.2.七层负载均衡 1.3 四层和七层的区别 2.LVS介绍 2.1LVS 的优势与不足 2.2LVS 核心组件和专业术语 3.ipvsadm命令 4.LVS集群中的增删改 4.1.管理集群服务中的增删改 4.2.管理集…...
DAY41-动态规划-买卖股票
LeetCode121. 买卖股票的最佳时机 public int maxProfit(int[] prices) {//dp数组含义:0为持有该股票,1为不持有该股票int[][] dp new int[prices.length][2];dp[0][0]-prices[0];dp[0][1]0;for(int i1;i<prices.length;i) {dp[i][0]Math.max(dp[i-…...
【C#】StringComparer
什么是“文化” 在 .NET 中,“文化”(Culture)指的是与语言、地区、和区域设置相关的特定信息集合。这些信息包括了日期和时间的格式、数字的表示方式、货币符号、字符串比较规则等等。文化的概念在软件开发中特别重要,因为应用程…...
阿里云服务器远程登录问题解决指南
前言 在使用阿里云服务器时,可能会遇到无法通过密码进行远程登录的情况。这种情况通常是因为操作系统默认禁用了密码登录功能。本文将介绍如何解决这一问题,确保能够顺利地通过密码进行远程登录。 问题描述 当尝试通过Workbench进行密码登录时&#x…...
Datawhale X 魔搭 AI夏令营(2024第四期)AIGC方向 学习笔记
粗谈 这一期是使用可图lora微调进行文生图任务的比赛 文生图也算是跨模态了,输入Prompt文本,输出图片。Prompt很重要,分为prompts和negative prompts。可以指导结果图片的生成。 lora可以参考我上期夏令营的文章:Datawhale AI 夏令营&…...
VScode前端环境搭建
前言 VScode是企业中最常用的前端开发工具,本文描述如何利用VScode搭建前端开发环境 一、安装VScode 下载Vscode 点击前往下载页面 安装 安装时一直点击下一步即可 二、环境配置 1)更改语言 点击拓展搜索Chinese后下载第一个,下载完后…...
Python自动化测试之selenium - 元素定位
元素定位 元素定位就是通过元素的信息或者元素层级结构来定位元素。当定位到了重复的属性,默认会定位到第一个标签。 id定位 - find_element_by_id 同一个html页面中id值是唯一的。 模拟在百度输入框中输入想要查找的内容 python 代码解读 复制代码 from sel…...
深入探索 npm cache clean --force:清理 npm 缓存的艺术
npm 是 JavaScript 编程语言的包管理器,它是 Node.js 运行环境的默认包管理器。npm 提供了一个丰富的生态系统,包括数以百万计的可重用代码包。然而,随着时间的推移,npm 的缓存可能会变得庞大,影响性能或导致一些奇怪的…...
如何看待“低代码”开发平台的兴起?
如何看待“低代码”开发平台的兴起? 近年来,“低代码”开发平台如雨后春笋般涌现,承诺让非专业人士也能快速构建应用程序。这种新兴技术正在挑战传统软件开发模式,引发了IT行业的广泛讨论。低代码平台是提高效率的利器࿰…...
自动控制——过阻尼、欠阻尼、临界阻尼及无阻尼
自动控制——过阻尼、欠阻尼、临界阻尼及无阻尼 引言 在自动控制系统和振动分析中,系统的阻尼特性对于系统的动态响应至关重要。阻尼决定了系统在受到扰动或输入信号后,如何恢复到稳定状态。本文将详细介绍过阻尼(overdamped)、…...
从AlphaGo到AlphaPong:AI乒乓球机器人将如何改变乒乓球竞技?
在人工智能领域,谷歌DeepMind的AlphaGo曾以其超凡的围棋能力震惊了世界。而如今,DeepMind又将其技术延伸到了乒乓球领域,推出了乒乓球机器人,被网友戏称为“AlphaPong”。这一机器人使用ABB的IRB1100机械手臂,能够与普…...
荒原之梦:考研期间可以玩游戏吗?
有不少同学,特别是男同学,在考研之前,会有玩游戏的习惯,那么,对于考研的同学来说,就会产生这样一个疑问。在考研备考期间,我还可以玩游戏吗? 其实关于这个问题的答案是因人而异的&am…...
Hive SQL ——窗口函数源码阅读
前言 使用Starrocks引擎中的窗口函数 row_number() over( )对10亿的数据集进行去重操作,BE内存溢出问题频发(忘记当时指定的BE内存上限是多少了.....),此时才意识到,开窗操作,如果使用 不当,反而…...
用python的Manim 创建大括号
Brace 是 Manim 中用于创建大括号(curly braces)的一个对象类。它有几个子类,自定义了不同的功能。下面是每个类的简要解释: 1. ArcBrace 功能: 创建一个环绕弧线的括号。适用于需要围绕弧形线条的场景。用法: 通常用于图形中有…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
【QT控件】显示类控件
目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏:QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...
数据挖掘是什么?数据挖掘技术有哪些?
目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...
