本篇博文目录:
- 一.通信相关概念
- 1.通信的三种分类
- (1) 串行通信和并行通信
- (2) 同步传输和异步传输
- (3) 半双工,全双工,单工
- 2.常见的通信方式及其分类
- 二.串口通信相关概念
- 1.串口通信
- 2.串口通信的几个比较重要的参数
- 3.串口的硬件接线
- (1) DB9和DB25
- (2) 交叉线和直通线
- (3) USB转串口
- (4) RS232和RS485
- 4.STM32串口工作过程分析
- (1) 波特率控制
- (2) 收发控制
- (3) 数据存储转移部分
- 三.STM32串口通信发送,接收和重定向代码(标准库版本)
一.通信相关概念
1.通信的三种分类
通信按数据传输方式进行分类,可以分为串行通信和并行通信;按通信机制方式进行分类,可以分为同步传输和异步传输。按照数据传输方向和能力进行分类,可以分为单工,半双工和全双工。
(1) 串行通信和并行通信
串行通信:数据是按照单个比特一个接一个地传输的,需要在数据位之间插入控制位,以确保数据的传输正确。串行通信的优点是使用较少的导线,在长距离传输时更稳定,但传输速度相对较慢。
并行通信:数据是同时传输多个比特,需要更多的导线来实现。并行通信的优点是传输速度快,但成本高且容易产生电源噪声等干扰。
备注:图片来源于https://www.eefocus.com/e/526547.html
(2) 同步传输和异步传输
同步通信:发送方和接收方使用共同的时钟信号进行数据传输和控制,这种方式的数据传输速度较快,但需要在发送和接收端维护相同的时钟,并且相对复杂。
异步通信:在数据传输过程中不需要任何时钟同步信号,而是在数据之间通过特定的协议进行数据同步。这使得异步通信更加简单易用,但传输速率相对较慢,数据传输可靠性也较差。
(3) 半双工,全双工,单工
单工(Simplex):单工通信只能在一个方向上进行信息传输,即信息只能从一个方向的发送器传送到另一个方向的接收器,无法进行双向传输。例如,电视广播和无线电广播就是一种单向通信,只能通过广播信号发送信息,而无法接收信息反馈。
半双工(Half Duplex):半双工通信只能在同一时间内进行单向传输,即信息只能从一个方向的发送器传送到另一个方向的接收器,不能同时进行双向传输。例如,对讲机就是一个典型的半双工通信设备,同一时间内只能有一方进行发言,另一方只能听取,不能同时说话和听取。
全双工(Full Duplex):全双工通信可以在同一时间内进行双向传输,即信息可以同时从两个方向进行传输,例如电话通信、网络通信等。在全双工通信中,每个通信实体都拥有一个发送器和一个接收器,能够在不影响对方通信的情况下进行同时的双向数据传输。
备注:图片来源于https://zhuanlan.zhihu.com/p/361655746
2.常见的通信方式及其分类
- 同步/异步+串行/并行
类型 | 描述 | 通信协议 | 数据传输速率 | 传输距离 | 应用 |
---|---|---|---|---|---|
同步串行通信 | 发送方与接收方在时钟方面保持同步,逐位地按照固定的时间间隔同时发送或接收数据 | SPI、I2C、Microwire等 | Mbps | 通常不超过一个设备的长度 | 短距离通信、芯片与芯片之间的通信 |
异步串行通信 | 发送方和接收方不需要保持时钟同步,每个数据字节之间有一个起始位和一个或多个停止位,实现了帧同步 | UART、RS-232等 | Kbps | 通常不超过几十米 | 数据采集、远程控制、较短距离通信 |
同步并行通信 | 发送方和接收方在时钟上保持同步,同时传输多个数据位 | ISA总线 | Mbps | 通常不超过一个设备的长度 | 高速数据传输 |
异步并行通信 | 发送方和接收方不需要时钟同步,在特定情况下可以同时传送多个数据位 | USB、PCI总线等 | Gbps | 通常不超过几米 | 高速数据传输、外部存储设备 |
- 其他通信:
类型 | 描述 | 通信协议 | 数据传输速率 | 传输距离 | 应用 |
---|---|---|---|---|---|
USB通信 | 通过USB接口连接各种外部设备,支持异步传输、同步传输和流传输等多种工作模式 | USB 1.x/2.x/3.x | Gbps | 通常不超过几米 | 外部设备连接、高速数据传输 |
CAN通信 | 用于工业自动化和汽车电子领域的数据总线标准,支持多个节点之间的数据传输 | CAN 2.0B | Mbps | 数百米至数千米 | 工业自动化、汽车电子 |
Ethernet通信 | 基于帧格式的网络协议,用于互联网和局域网通信,支持快速、可靠的数据传输 | IEEE 802.3 | Gbps | 通常不超过几百米 | 网络通信、数据中心、局域网 |
WiFi通信 | 基于无线局域网技术的通信方式,通过无线网络实现设备之间的数据传输 | IEEE 802.11 | Gbps | 通常不超过几十米 | 移动设备、家庭网络、公共场所 |
Bluetooth通信 | 短距离、低功耗的无线通信技术,主要用于移动设备之间的数据传输 | Bluetooth 4.0/5.0 | Mbps | 通常不超过几十米 | 移动设备、家庭娱乐、智能家居 |
二.串口通信相关概念
1.串口通信
① 串口通信是指通过串行通信接口来进行数据传输的一种通信方式。串口通信有两种基础协议:同步协议和异步协议,其中异步协议广泛应用于单片机开发中。
② 在异步协议中,通信双方的时钟并不需要进行同步,而是利用起始位和停止位来判断每一个字节的开始和结束。异步通信中最常用的协议是UART(Universal Asynchronous Receiver-Transmitter),UART只需要使用一根发送线和一根接收线即可完成单向或双向的串口通信。
③ 串口通信可以实现点对点和多点通信,实现简单,价格低廉,但数据传输速率较慢,一般采用的通信速率为115200bps以下。在单片机开发中,串口通信被广泛应用于与电脑之间的数据传输、与传感器之间的数据传输以及与其他单片机之间的数据传输等场景。(注意有时终线的是同步,没有时钟线的是异步,同步串行通信数据帧如下图所示 )
例如:MCU单片机给PC机发送一个字符 ‘A’,的通信过程大致如下:
- 首先是硬件连接,只需要三根线分别是RXD,TXD和GND;需要注意的是MCU的RXD和PC的TXD进行连接,MCU的TXD和PC的RXD进行连接(这里的引脚是真实引脚,交叉线)。
- MCU进行数据发送,只需要MCU的TXD和PC机的RXD进行工作,发送一个字符’A’的波形如下图所示,首先是起始位通常都是低电平,然后后面1~8(0 ~ 7)位为数据位,其中第八位(0 ~ 7 中的第7位)可能为奇偶校验位,看用户是否进行设置,在0 ~ 7 位中只有0和6是高电平,这里高电平就是我们发送的有效数据,将高电平对应位的二进制转换成十进制2^0 + 2^6 = 1+64 = 65 会发现这里的65正好对应ASCLL字符’A’, 接着最后一位为停止位(通常为高电平)表示本次数据发送结束。
- 在首次进行串口通信的时候,我们常常会发现会出现乱码或数据接收不到的情况,这通常原因就是MCU和PC二者之间的串口参数配置不一致导致的,比如波特率,这里的波特率指的就是发送数据的速度,比如波特率9600表示1s钟发送9600个这样的数据。
- 上面内容如果还不明白可以通过这个视频进行学习https://www.bilibili.com/video/BV1y34y147s5/,进一步了解串口通信
2.串口通信的几个比较重要的参数
串口通信的几个比较重要的参数包括:
-
波特率(Baud Rate):表示通信双方之间每秒传输的比特数,通常用bps(bits per second)来表示。波特率越高,数据传输速度越快,但同时也会增加误码率,降低传输可靠性。
-
数据位(Data Bits):表示每个字节中所包含的数据位数,常见的取值有5、6、7和8位。数据位的选择取决于数据所包含的信息量以及传输的精度要求。
-
停止位(Stop Bits):表示每个字节传输结束后,发送方在发送线上保持的电平状态,通常有1位和2位两种取值。停止位的作用是为了告诉接收方当前数据传输已经结束。
-
校验位(Parity):在数据传输过程中,加入一个校验位来判断数据是否正确传输。常见的校验方式有奇偶校验、偶校验、无校验等,其中奇偶校验最为常用。
以上参数需要双方进行协商确定,并在通信时保持一致,否则数据传输将会出现错误。在单片机开发中,常常使用的串口通信参数为波特率9600bps、8数据位、1停止位、无校验位。
3.串口的硬件接线
(1) DB9和DB25
串口线的接口标准通常有DB9和DB25两种,其中DB9有9根引脚,适用于较少信号的串口设备,而DB25有25根引脚,适用于需要传输大量信号的串口设备。在串口线的选择时,需要根据具体的设备类型和接口标准进行选择。同时,还需要注意串口线的长度和屏蔽效果,以确保数据传输的稳定性和可靠性。
- DB9
DB9是一种常用的串口接口标准,也称作DE-9(D形9针),其中“D”指代连接器的外形类似于字母D。DB9接口共有9个引脚,分别为:
- DCD(Data Carrier Detect) - 数据载波检测
- RD(Data Receive) - 数据接收
- TD(Data Transmit) - 数据发送
- DTR(Data Terminal Ready) - 数据终端就绪
- GND(Signal Ground) - 路径接地
- DSR(Data Set Ready) - 数据集就绪
- RTS(Request To Send) - 请求发送
- CTS(Clear To Send) - 清除发送
- RI(Ring Indicator) - 响铃指示
DB9接口广泛应用于串口设备之间的通信,如串口打印机、调制解调器、路由器、终端设备等。在使用DB9接口时,需要注意接口针脚的正确连接和通信参数的正确设置,以确保数据传输的稳定和可靠。
- DB25
DB25是一种串口接口标准,也称作DE-25(D形25针),其中“D”指代连接器的外形类似于字母D。DB25接口共有25个引脚,与DB9接口相比,其拥有更多的信号引脚,能够支持更复杂的通信方式。DB25接口的引脚定义如下:
1.protective ground(保护接地)
2.TxD(Data Transmit) - 数据发送
3.RxD(Data Receive) - 数据接收
4.RTS(Request To Send) - 请求发送
5.CTS(Clear To Send) - 清除发送
6.DSR(Data Set Ready) - 数据集就绪
7.Signal ground(Signal Ground) - 信号地
8.CD(Carrier Detect) - 载波检测
9.RI(Ring Indicator) - 响铃指示
10.交替行选通
11.设备选择1
12.设备选择2
13.设备选择3
14.设备选择4
15.线路就绪
16.时钟
17.端口就绪
18.暂停
19.申请握手
20.结束握手
21.数据线就绪1
22.数据线就绪2
23.数据线就绪3
24.数据线就绪4
25.保留
DB25接口广泛应用于串口设备之间的通信,如计算机、打印机、调制解调器、路由器、终端设备等。在使用DB25接口时,需要注意接口针脚的正确连接和通信参数的正确设置,以确保数据传输的稳定和可靠。
(2) 交叉线和直通线
串口的连接需要结合实际原理图来连接,看是采用的交叉线方式还是直通线方式,如果采用交叉线方式就把单片机的TXD与其他设备的RXD相连,RXD与TXD相连;如果采用的是直通线就把单片机的TXD与其他设备的TXD相连,RXD与RXD相连。
- 交叉线
标准串口引脚2是RX,引脚3是TX,(即九针串口的2号引脚连接的是主控芯片的RXD引脚,3号引脚连接的是主控芯片的TXD引脚),如果单片机开发板的串口和电脑串口都是标准串口,则俩串口应该用交叉线连接。(单片机的TXD连接电脑的RXD;单片机的RXD连接串口的RXD)
- 直通线
不过现实中直通线居多,为了配合直通线的使用,在画板的时候,把主控芯片的RXD引脚连接至串口的3脚(RXD实际是TXD),主控芯片的TXD引脚连接至串口的2脚(TXD实际是RXD),这样这个开发板上的串口就不是标准串口了,即2变成TX,3变成RX,和标准串口连接时当然应该使用直连线了。(单片机的TXD(实际是RXD)和电脑的TXD相连;单片机的RXD(实际是TXD)和电脑RXD相连)
备注:参考了这篇博文:https://new.qq.com/rain/a/20210527A01UNY00
(3) USB转串口
① USB转串口是指通过USB接口将计算机与串口设备进行连接的一种方式。由于现代笔记本电脑大多已经取消了串口接口,而很多应用场合仍需要使用串口设备进行通信,因此USB转串口逐渐成为了一种常见的解决方案。
② USB转串口一般需要使用专门的转换器,这种转换器内部会集成一个串口芯片和USB芯片,可以实现USB和串口之间的信号转换和协议转换。在使用时,用户只需要将USB转串口转换器插入计算机的USB接口上,就可以通过串口通信软件来访问串口设备进行数据传输。
③ 需要注意的是,不同的USB转串口转换器支持的串口标准可能不同,如RS232、TTL等,因此在选择转换器时需要确认其支持的串口标准是否符合自己的需求。同时,在使用USB转串口转换器时也需要特别注意其驱动程序是否已经安装,以及通信参数的设置是否正确。
(4) RS232和RS485
- 串口通信采用的电平为TTL电平,这种电平输出的高电平最低为2.4V,低电平最高为0.4V,如果在传输的过程中受到干扰(静电),可能将某一个低电平变高,所以串口通信通常的距离很短,一般为1m之内。
- 所以串口通信常用于MCU与MCU或MCP和PC之间进行通信
- 为了提供通信的稳定性有了RS232和RS485通信,所谓的RS232通信指的是将TTL电平转换为RS232电平,这里的转换常用负逻辑,意思就是TTL的+5V转换为-12V,0V转换为12V这样就是-12V表示高电平,+12V表示低电平(二者之间可以分别进行转换),并且RS232的电平范围要比TTL电平大一些,从而提高通信的稳定。
- RS232的通信距离可以达到15m,但是速率仅有20k,也就是19200的波特率
- 针对一些工业环境或者对距离有更远的要求,RS232就力不从心了,所以这时就可以考虑采用RS485通信;RS485通信就是在MCU外加一个485转换芯片,RS485采用差分信号,传递的高低电平通过RS485_A和RS485_B线来传递,当RS485_A>RS485_B时为高电平,当RS485_A<RS485_B时为低电平。
- 因为RS485采用差分信号,所以即使受到干扰也没有什么影响。
- RS485传输距离可达1200米并且传输频率也远远高于RS232,可达50M
4.STM32串口工作过程分析
串口外设的架构图从下至上,我们看到串口外设主要由三个部分组成,分别是波特率的控制部分①、收发控制部分②及数据存储转移部分 ③。
(1) 波特率控制
波特率,即每秒传输的二进制位数,用 b/s (bps)表示,通过对时钟的控制可以改变波特率。在配置波特率时,我们向波特比率寄存器 USART_BRR 写入参数,修改了串口时钟的分频值USARTDIV 。 USART_BRR寄存器包括两部分,分别是DIV_Mantissa (USARTDIV 的整数部分)和DIVFraction (USARTDIV的小数)部分,最终,计算公式为USARTDIV=DIV_Mantissa+(DIVFraction/16)。USARTDIV 是对串口外设的时钟源进行分频的,对于USART1 ,由于它是挂载在 APB2 总线上的,所以它的时钟源为 fPCLK2 ;而 USART2、3 挂载在APB1 上,时钟源则为 fPCLK1 ,串口的时钟源经过 USARTDIV 分频后分别输出作为发送器时钟及接收器时钟 ,控制发送和接收的时序。
(2) 收发控制
围绕着发送器和接收器控制部分,有好多个寄存器:CR1、CR2、CR3、SR,即USART的三个控制寄存器 (Control Register)及一个 状态寄存器 (StatusRegister)。通过向寄存器写入各种控制参数 ,来控制发送和接收,如奇偶校验位,停止位等,还包括对USART中断的控制;串口的状态在任何时候都可以从状态寄存器中查询得到。
(3) 数据存储转移部分
收发控制器根据我们的寄存器配置,对 数据存储转移部分 的 移位寄存器 进行控制。当我们需要发送数据时,内核或 DMA 外设(一种数据传输方式)把数据从内存(变量)写入到 发送数据寄存器 TDR 后, 发送控制器 将适时地自动把数据从 TDR 加载到 发送移位寄存器 ,然后通过 串口线 Tx ,把数据 一位一位 地发送出去,在数据从 TDR 转移到 移位寄存器 时,会产生 发送寄存器TDR 已空事件 TXE ,当数据从 移位寄存器 全部发送出去时,会产生数据 发送完成事件 TC ,这些事件可以在 状态寄存器 中查询到。而 接收数据 则是一个 逆过程 ,数据从 串口线 Rx 一位一位地输入到 接收移位寄存器 ,然后自动地转移到 接收数据寄存器 RDR ,最后用内核指令或 DMA读取到内存(变量)中。
三.STM32串口通信发送,接收和重定向代码(标准库版本)
- usart1.c
#include "usart1.h"
#include <stdbool.h>
#if USART1_RX_EN
uint8_t USART1_RX_Buffer[USART1_RX_MAX] = {0};// 接收缓存
uint8_t USART1_RX_Index = 0; // 接收下标
uint8_t USART1_RX_OverFlag = 0;// 接收完成
#endif // 初始化中断优先级
static void NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;// 中断控制器NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 配置优先分组// 中断源NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;// 抢占优先级NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// 子优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;// 使能中断NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;// 初始化NVICNVIC_Init(&NVIC_InitStructure);}// 初始化串口
void USART1_Config(){// 初始化GPIO_InitTypeDef和USART_InitTypeDef结构体对象GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 开启USART1和GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);// 初始化GPIO9(PA9作为TXD)GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;// 初始化的引脚PA9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 设置GPIO的速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIO// 初始化GPIO10(PA10作为RXD)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//初始化的引脚PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;// 浮空输入模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 设置GPIO的速率为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIO// 初始化USART1USART_InitStructure.USART_BaudRate = 115200;// 波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 数据位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 停止位USART_InitStructure.USART_Parity = USART_Parity_No ;// 奇偶校验位0// 无硬件流模式:在无硬件流控制模式下,数据通信没有任何限制,也就是说,当接收缓冲区溢出时会发生数据丢失USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 指定硬件流模式为无硬件流模式USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 工作模式:同时启用串口的发送和接收模式USART_Init(USART1, &USART_InitStructure);// 初始化串口USART_Cmd(USART1, ENABLE);// 串口使能#if USART1_RX_EN// 使能串口接收中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);// 接收数据不为空的中断// 串口中断优先级NVIC_Configuration();
#endif}/***************** 发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{/* 发送一个字节数据到USART */USART_SendData(pUSARTx,ch);/* 等待发送数据寄存器为空 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{uint8_t i;for(i=0; i<num; i++){/* 发送一个字节数据到USART */Usart_SendByte(pUSARTx,array[i]); }/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}/***************** 发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){}
}/***************** 发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{uint8_t temp_h, temp_l;/* 取出高八位 */temp_h = (ch&0XFF00)>>8;/* 取出低八位 */temp_l = ch&0XFF;/* 发送高八位 */USART_SendData(pUSARTx,temp_h); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);/* 发送低八位 */USART_SendData(pUSARTx,temp_l); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{/* 发送一个字节数据到串口 */USART_SendData(USART1, (uint8_t) ch);/* 等待发送完毕 */while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return (ch);
}///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{/* 等待串口输入数据 */while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(USART1);
}
- usart1.h
#ifndef __usart1_h
#define __usart1_h
#include "stm32f10x.h"
#include <stdio.h>
#define USART1_RX_EN 1
#define USART1_RX_MAX 255#if USART1_RX_EN
extern uint8_t USART1_RX_Buffer[USART1_RX_MAX];// 接收缓存
extern uint8_t USART1_RX_Index; // 接收下标
extern uint8_t USART1_RX_OverFlag;// 接收完成
#endif void USART1_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
#endif /*__usart1_h */
- stm32f10x_it.c
// 串口中断服务函数
void USART1_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET){ ucTemp = USART_ReceiveData(USART1);// 防止下标越界if(USART1_RX_Index >=USART1_RX_MAX){USART1_RX_Index = 0;}if(ucTemp != '#'){USART1_RX_Buffer[USART1_RX_Index++] = ucTemp;}// 发送的结束符if(ucTemp == '#'){USART1_RX_Buffer[USART1_RX_Index++] = ucTemp;USART1_RX_OverFlag = 1;// 发送完成}} USART_ClearITPendingBit(USART1,USART_IT_RXNE); // 清除中断标识【可以加;可不加】
}
- main.c
#include "stm32f10x.h"
#include "led.h"
#include "usart1.h"
#include "string.h"const char cmd1[8] = "on#";
const char cmd2[8] = "off#";
int main(void){// 初始化串口USART1_Config();// 初始化LEDledInit();ledOnOrOff(LED_ON);while(1){if(USART1_RX_OverFlag == 1){// 解析命令if(strstr((const char*)USART1_RX_Buffer,cmd1)!= NULL){ledOnOrOff(LED_ON);printf("灯已开");}if(strstr((const char*)USART1_RX_Buffer,cmd2)!= NULL){ledOnOrOff(LED_OFF);printf("灯已关");}// 重置接收标识USART1_RX_Index = 0;USART1_RX_OverFlag = 0;memset(USART1_RX_Buffer,0,USART1_RX_MAX);// 将缓存请0}}}