STM32标准库——(13)USART串口数据包

1.HEX数据包

2.文本数据包

 3.HEX数据包接收

对于固定包长的HEX数据包,我们可以定义三个状态:等待包头、接收数据、等待包尾,每个状态。都可以用一个变量来标志,例如变量S来表示。这三个状志可以依次定义为S=0、S=1、S=2。类似于置标志位,但标志位只有0和1,而状态机允许多标志位状态,初始时,S=0表示等待包头状态。当中断发生时,根据S=0的状态,程序会进入等待包头的逻辑。如果收到的数据是FF(包头),则将状态设置为S=1并退出中断。然后根据S=1的状态,进行接收数据的程序,如果收到的不是FF,就证明数据包没有对齐,那么需要继续等待包头出现状态保持为S=0。当收到FF(包头)后状态会转移到S=1(接收数据状态)。在此状态下,接收到的数据会被存储在数组中,并记录已收到的数据数量,当收到4个数据后将状态设置为S=2(等待包尾状志)。在等待包尾状态下(S=2),程序会等待收到FE(包尾)如果收到FE,则将状态里置为S=0(等待包头状态),开始下一个数据包的接收。如果收到的不是FE,那么需要进入重复等待包尾的状态,直到接收到真正的包尾。 

4.文本数据包接收 

 同样也是利用状态机,定义3个状态,第一个状态,等待包头,判断收到的是不是我们规定的@符号,如果收到@就进入接收状态,在这个状态(S=1)下,依次接收数据,同时,这个状态还应该要兼具等待包尾的功能。因为这是可变包长,我们接收数据的时候,也要时刻监视,是不是收到包尾了,一但收到包尾了,就结束,那这里(S=1),这个状态的逻辑就应该是,收到一个数据,判断是不是\r,如果不是,则正常接收,如果是,则不接收,同时跳到下一个状态,等待包尾\n,因为我这里数据包有两个包尾\n,所以需要第三个状态,如果只有一个包尾,那在出现包尾之后,就可以直接回到初始状态了,只需要两个状态就行,因为接收数据和等待包尾需要在一个状态里同时进行,由于串口的包头包尾不会出现在数据中,所以基本不会出现数据错位的现象,这就是使用状态机接收文本数据包的方法。

5.串口收发HEX数据包

5.1 接线图

5.2 相关代码 

5.2.1 Serial.c
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>uint8_t Serial_TxPacket[4];		//定义发送数据包数组,数据包格式:FF 01 02 03 04 FE
uint8_t Serial_RxPacket[4];		//定义接收数据包数组
uint8_t Serial_RxFlag;			//定义接收数据包标志位/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟 USART1是APB2总线上的设备 剩下的都是APB1总线中的设备RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//TX引脚是USART外设控制的输出脚 所以要用复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//一般RX配置是浮空输入或上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/*USART初始化*/USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//模式,选择为发送模式USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长,选择8位USART_Init(USART1,&USART_InitStructure);/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);//使能USART1,串口开始运行
}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1,Byte);/*  USARTx->DR = (Data & (uint16_t)0x01FF);//数据最终通向TDR(发送数据寄存器) TDR再传递给移位寄存器 最终一位一位把数据移出Tx引脚 完成数据发送*/while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/* 当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。*//*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for(i=0;i<Length;i++){Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char* String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X,uint32_t Y)
{uint32_t Result = 1;//设置结果初值为1while(Y--)//执行Y次{Result *= X;//将X累乘到结果 Result*X=X 累乘Y次即X的Y次方 用次来得到后面数字的位数}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for(i=0;i<Length;i++)//根据数字长度遍历数字的每一位{Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+'0');//依次调用Serial_SendByte发送每位数字/*Length-i-1:i从0开始 这样子数字遍历出来得到的是最低位(各位)在最前面 需取反*//*%10:得到数字的位数*//*+‘0’:比如函数给234 不加'0'按原规则遍历完显示的是020304 所以每次遍历需要移位 将前面的0移去*/}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)//printf在打印的时候 就是不断调用fputc函数一个个打印
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量arg va_list是一个类型名 arg是变量名va_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中 因为sprintf只能接收直接写的参数va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:串口发送数据包* 参    数:无* 返 回 值:无* 说    明:调用此函数后,Serial_TxPacket数组的内容将加上包头(FF)包尾(FE)后,作为数据包发送出去*/
void Serial_SendPacket(void)
{Serial_SendByte(0xFF);Serial_SendArray(Serial_TxPacket,4);Serial_SendByte(0xFE);
}/*** 函    数:获取串口接收数据包标志位* 参    数:无* 返 回 值:串口接收数据包标志位,范围:0~1,接收到数据包后,标志位置1,读取后标志位自动清零*/
uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag == 1)			//如果标志位为1{Serial_RxFlag = 0;return 1;					//则返回1,并自动清零标志位}return 0;						//如果标志位为0,则返回0
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == 0xFF)			//如果数据确实是包头{RxState = 1;			//置下一个状态pRxPacket = 0;			//数据包的位置归零}}/*当前状态为1,接收数据包数据*/else if (RxState == 1){Serial_RxPacket[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置pRxPacket ++;				//数据包的位置自增if (pRxPacket >= 4)			//如果收够4个数据{RxState = 2;			//置下一个状态}}/*当前状态为2,接收数据包包尾*/else if (RxState == 2){if (RxData == 0xFE)			//如果数据确实是包尾部{RxState = 0;			//状态归0Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}
5.2.2 Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial_SendPacket(void);uint8_t Serial_GetRxFlag(void);#endif
5.2.3 main.c 
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"uint8_t KeyNum;			//定义用于接收按键键码的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化Key_Init();			//按键初始化Serial_Init();		//串口初始化/*显示静态字符串*/OLED_ShowString(1, 1, "TxPacket");OLED_ShowString(3, 1, "RxPacket");/*设置发送数据包数组的初始值,用于测试*/Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;while (1){KeyNum = Key_GetNum();			//获取按键键码if (KeyNum == 1)				//按键1按下{Serial_TxPacket[0] ++;		//测试数据自增Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;Serial_SendPacket();		//串口发送数据包Serial_TxPacketOLED_ShowHexNum(2, 1, Serial_TxPacket[0], 2);	//显示发送的数据包OLED_ShowHexNum(2, 4, Serial_TxPacket[1], 2);OLED_ShowHexNum(2, 7, Serial_TxPacket[2], 2);OLED_ShowHexNum(2, 10, Serial_TxPacket[3], 2);}if (Serial_GetRxFlag() == 1)	//如果接收到数据包{OLED_ShowHexNum(4, 1, Serial_RxPacket[0], 2);	//显示接收的数据包OLED_ShowHexNum(4, 4, Serial_RxPacket[1], 2);OLED_ShowHexNum(4, 7, Serial_RxPacket[2], 2);OLED_ShowHexNum(4, 10, Serial_RxPacket[3], 2);}}
}
5.2.4 现象

按下面包板上的独立按键 FF和FE之间的四位数从01 02 03 04 发生递增 分别显示在OLED屏和串口助手上(注意:发送模式和接收模式都是HEX模式)

6.串口收发文本数据包

6.1 接线图

6.2 相关代码

6.2.1 Serial.c
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>char Serial_RxPacket[100];		//定义接收数据包数组
uint8_t Serial_RxFlag;			//定义接收数据包标志位/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟 USART1是APB2总线上的设备 剩下的都是APB1总线中的设备RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//TX引脚是USART外设控制的输出脚 所以要用复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//一般RX配置是浮空输入或上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/*USART初始化*/USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//模式,选择为发送模式USART_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长,选择8位USART_Init(USART1,&USART_InitStructure);/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);//使能USART1,串口开始运行
}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1,Byte);/*  USARTx->DR = (Data & (uint16_t)0x01FF);//数据最终通向TDR(发送数据寄存器) TDR再传递给移位寄存器 最终一位一位把数据移出Tx引脚 完成数据发送*/while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/* 当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。*//*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for(i=0;i<Length;i++){Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char* String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X,uint32_t Y)
{uint32_t Result = 1;//设置结果初值为1while(Y--)//执行Y次{Result *= X;//将X累乘到结果 Result*X=X 累乘Y次即X的Y次方 用次来得到后面数字的位数}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for(i=0;i<Length;i++)//根据数字长度遍历数字的每一位{Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+'0');//依次调用Serial_SendByte发送每位数字/*Length-i-1:i从0开始 这样子数字遍历出来得到的是最低位(各位)在最前面 需取反*//*%10:得到数字的位数*//*+‘0’:比如函数给234 不加'0'按原规则遍历完显示的是020304 所以每次遍历需要移位 将前面的0移去*/}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)//printf在打印的时候 就是不断调用fputc函数一个个打印
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量arg va_list是一个类型名 arg是变量名va_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中 因为sprintf只能接收直接写的参数va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == '@' && Serial_RxFlag == 0)			//如果数据确实是包头{RxState = 1;			//置下一个状态pRxPacket = 0;			//数据包的位置归零}}/*当前状态为1,接收数据包数据*/else if (RxState == 1){if(RxData == '\r'){RxState = 2;}else{Serial_RxPacket[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置pRxPacket ++;				//数据包的位置自增	}}/*当前状态为2,接收数据包包尾*/else if (RxState == 2){if (RxData == '\n')			//如果数据确实是包尾部{RxState = 0;			//状态归0Serial_RxPacket[pRxPacket] = '\0';Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}
6.2.2 Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>extern char Serial_RxPacket[];
extern uint8_t Serial_RxFlag;void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);
void Serial_SendPacket(void);uint8_t Serial_GetRxFlag(void);#endif
6.2.3 main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化LED_Init();			//LED初始化Serial_Init();		//串口初始化/*显示静态字符串*/OLED_ShowString(1, 1, "TxPacket");OLED_ShowString(3, 1, "RxPacket");while (1){if (Serial_RxFlag == 1)		//如果接收到数据包{OLED_ShowString(4, 1, "                ");OLED_ShowString(4, 1, Serial_RxPacket);				//OLED清除指定位置,并显示接收到的数据包/*将收到的数据包与预设的指令对比,以此决定将要执行的操作*/if (strcmp(Serial_RxPacket, "LED_ON") == 0)			//如果收到LED_ON指令 Serial_RxPacket中的字符与LED_ON进行对比 等于0即对比成功 则执行if中内容{LED1_ON();										//点亮LEDSerial_SendString("LED_ON_OK\r\n");				//串口回传一个字符串LED_ON_OKOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_ON_OK");				//OLED清除指定位置,并显示LED_ON_OK}else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)	//如果收到LED_OFF指令{LED1_OFF();										//熄灭LEDSerial_SendString("LED_OFF_OK\r\n");			//串口回传一个字符串LED_OFF_OKOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_OFF_OK");			//OLED清除指定位置,并显示LED_OFF_OK}else						//上述所有条件均不满足,即收到了未知指令{Serial_SendString("ERROR_COMMAND\r\n");			//串口回传一个字符串ERROR_COMMANDOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "ERROR_COMMAND");			//OLED清除指定位置,并显示ERROR_COMMAND}Serial_RxFlag = 0;			//处理完成后,需要将接收数据包标志位清零,否则将无法接收后续数据包}}
}
6.2.4 现象

在串口助手发送区输入@LED_ON加换行可以点亮LED灯 并且在OLED第四行显示接收到的数据指令为LED_ON 同时会发送数据LED_ON_OK 分别显示在串口助手接收区和LED第二行 输入@LED_OFF与此类似 相反的是LED灯会熄灭 这是两个特殊指令 若输入@+其他 则会OLED会显示ERROR_CCOMMAND.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/710164.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio level过滤查看各个等级的日志

Android Studio level过滤查看各个等级的日志 旧版as可以在下方的日志输出框选择debug、info&#xff0c;warn、error日志&#xff0c;新版的需要通过在过滤框手动/联想输入 level:xxx&#xff0c;过滤相应等级的日志&#xff0c;如图&#xff1a; android studio/idea返回/前进…

javaee教程郑阿奇课后答案,三年经验月薪50k我是怎么做到的

个人背景 如标题所示&#xff0c;我的个人背景非常简单&#xff0c;Java开发经验1年半&#xff0c;学历普通&#xff0c;2本本科毕业&#xff0c;毕业后出来就一直在Crud&#xff0c;在公司每天重复的工作对我的技术提升并没有什么帮助&#xff0c;但小镇出来的我也深知自我努…

网络工程师笔记3

IP地址类型 A类 255.0.0.0B类 255.255.0.0C类 255.255.255.0D类 E类 子网掩码&#xff1a;从左到右连续的确定网络位 2-4-8-16-32-64-128-256 128 &#xff1a; 1000 0000 64 &#xff1a; 0100 0000 32 &#xff1a; 0010 0000 16 &#xff1a; 0001 0000 8 &am…

Linux和Windows集群中部署HTCondor

目录 1、集群架构 2、HTCondor版本 3、Linux系统安装 3.1、HTCondor安装 3.2、中央管理节点配置 3.3、其他节点配置 4、Windwos系统安装 5、安全配置 6、参考 1、集群架构 操作系统IP地址1*Ubuntu22.04192.168.1.742Ubuntu22.04192.168.1.603Ubuntu22.04192.168.1.6…

Squid代理服务器配置

需求是&#xff1a;通过外网机&#xff08;跳板机&#xff09;访问内网机&#xff0c;并为内网机提供访问网络的能力。 【跳板机T】【内网机N】 公网IP&#xff1a;39.107.xx.xxx 跳板机IP&#xff1a;172.17.216.234 内网机IP&#xff1a;172.17.216.241 Squid代理服务器地址…

Linux磁盘设备LVM介绍和常用场景说明

Linux常见的物理设备数据备份和负载均衡模式 1. LVM技术说明2. 相关概念3. 常用命令3.1 安装lvm命令3.2 创建分区3.3 格式化成LVM3.4 其他格式化 4. 常用场景4.1 创建LVM并挂载4.2 LVM扩容4.2.1 xfs扩容4.2.2 ext4扩容 4.2 缩减逻辑卷lv4.3 缩减vg&#xff1a;&#xff08;迁移…

设计模式(二)单例模式

单例模式&#xff1a;确保一个类只有一个实例&#xff0c;并提供了全局访问点&#xff1b;主要是用于控制共享资源的访问&#xff1b; 单例模式的实现分为懒汉式和饿汉式。 懒汉式单例在需要时才会创建&#xff0c;而饿汉式单例则在类加载时立即创建实例&#xff1b; 单例模…

印象笔记 - Markdown 入门指南

一、Markdown 是什么&#xff1f; Markdown 是一种轻量级的「标记语言」&#xff0c;创始人为约翰格鲁伯&#xff0c;用简洁的语法代替排版&#xff0c;目前被越来越多的知识工作者、写作爱好者、程序员或研究员广泛使用。其常用的标记符号不超过十个&#xff0c;相对于更为复…

一文速览深度伪造检测(Detection of Deepfakes):未来技术的守门人

一文速览深度伪造检测&#xff08;Detection of Deepfakes&#xff09;&#xff1a;未来技术的守门人 前言一、Deepfakes技术原理卷积神经网络&#xff08;CNN&#xff09;&#xff1a;细致的艺术学徒生成对抗网络&#xff08;GAN&#xff09;&#xff1a;画家与评审的双重角色…

MySQL 逗号分隔查询--find_in_set()函数

业务场景&#xff1a; 在使用MySQL的时候&#xff0c;可能的某个字段存储的是一个英文逗号分割的字符串&#xff08;这里我们不讨论表设计的合理性&#xff09;&#xff0c;如图所示&#xff1a; 我们在查询的时候需要匹配逗号分割中的某个字符串&#xff0c;该怎么查询呢&am…

CRM 系统:管理工作流程的最佳利器全面解析

一个好的CRM解决方案能够使您业务保持活力——也就是管理客户。这意味着CRM系统提供了包括流程自动化、联系人管理、多渠道管理、数据分析等一系列工具。可以说&#xff0c;CRM是企业管理工作流程的最佳工具之一。 现在&#xff0c;市场上有上万种不同类型的CRM解决方案&#…

机器学习提升秘籍:Scikit-learn学习网站全攻略!

介绍&#xff1a;是一个开源的Python机器学习库&#xff0c;它提供了一整套用于数据挖掘和数据分析的工具&#xff0c;包括各种分类、回归、聚类和降维算法以及模型评估、选择和数据预处理等功能。以下是关于Scikit-learn的一些详细介绍&#xff1a; 算法覆盖广泛&#xff1a;S…

Mendix 开发实践指南|Mendix的核心概念

在当今快速变化的技术环境中&#xff0c;Mendix平台以模型驱动开发方法&#xff0c;重新定义了应用程序的构建过程。本章内容&#xff0c;将深入探讨Mendix的几大核心概念&#xff1a;模型驱动开发、微流、纳流 、 实体模型和页面&#xff0c;旨在帮助我们全面理解Mendix平台的…

java之Bean对象

1. 什么是Bean&#xff1f; Bean被实例化的&#xff0c;是被Spring框架所管理的Java对象。 Spring容器会自动完成Bean的实例化。将所创建的的Bean自动注入到Ioc容器中以供调用。 spring框架中 IOC容器中管理的对象就是Bean对象 2. 第三方bean Bean 因为第三方bean&#xff0…

​LeetCode解法汇总2476. 二叉搜索树最近节点查询

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; GitHub - September26/java-algorithms: 算法题汇总&#xff0c;包含牛客&#xff0c;leetCode&#xff0c;lintCode等网站题目的解法和代码&#xff0c;以及完整的mode类&#…

如何在Win系统从零开始搭建Z-blog网站,并将本地博客发布到公网可访问

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

Linux系统编程之线程互斥锁的使用方法

文章目录 一、Linux上线程开发互斥锁概要二、创建及销毁互斥锁2.1 示例&#xff1a;主线程等待两个线程退出&#xff0c;1线程和2线程打印信息 三、互斥量的初始化问题 一、Linux上线程开发互斥锁概要 互斥量&#xff08;mutex&#xff09;从本质上来说是一把锁&#xff0c;在…

Stable Diffusion 模型分享:yayoi_mix(日本美女、真实、照片)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五 下载地址 模型介绍 yayoi_mix 是一个现实模型&#xff0c;是一个针对日本女性训练及合并的模型。 条目内容类型大模型基础模型SD…

前后端项目宝塔linux部署(springboot,vue,python)

宝塔linux安装就省略了&#xff0c;网上一堆 1.部署后端 1.首先把自己项目里面打包好的的jar包上传到服务器随便一个地方&#xff0c;我这里就上传到www/wwwroot下面了&#xff0c;宝塔的文件页面可以很便携上传 2.然后到下面这个页面 选那个java环境管理装个jdk&#xff…

全网首个GDB移植手册【Howto:Porting the GUN Debugger】翻译

Howto:Porting the GUN Debugger ✍【作者】&#xff1a;电子科大不知名程序员 &#x1f4e3;【说明】&#xff1a;本文是自己在搭建mcore架构GDB时的参考的手册&#xff0c;具有很强的学习指导性&#xff0c;因原文档&#xff08;链接&#xff1a;https://www.embecosm.com/a…