stm32单片机开发四、USART“串口通信“

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述串口的空闲状态时高电平,起始位是低电平,来打破空闲状态的高电平
必须要有停止位,停止位一般为一位高电平
串口常说的数据为8N1,其实就是8个数据位(固定的),N就是none,也就是0个校验位,1个停止位
如果说有校验位,那么就会在8位数据位后加一位校验位,共9位数据,然后时1位停止位
如果使用了奇校验,那么包括校验位在内的9位数据会出现奇数个1,比如如果你传输0000 1111,目前总共4个1,是偶数个,那么校验位就需要再补一个1,连同校验位就是0000 1111 1,总共5个1,保证1为奇数,如果数据是0000 1110,此时3个1,是奇数个,那么校验位就补一个0,连同校验位就是0000 11100,总共还是3个1,1的个数为奇数。发送方,在发送数据后,会补一个校验位,保证1的个数为奇数,接收方,在接收数据后,会验证数据位和校验位,如果1的个数还是奇数,就认为数据没有出错,如果在传输中,因为干扰,有一位由1变成0,或者由0变成1了,那么整个数据的奇偶特性就会变化

在这里插入图片描述在转移的同时,置一个RXNE标志位,我们检查这个标志位,就可以知道是不是收到数据了,这个标志位也可以申请中断
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>uint8_t Serial_RxData;		//定义串口接收的数据变量
uint8_t Serial_RxFlag;		//定义串口接收的标志位变量/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*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_Init,配置USART1/*中断输出配置*/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);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数: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 ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止,'\0'也可以写为0{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累乘到结果}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发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:获取串口接收标志位* 参    数:无* 返 回 值:串口接收标志位,范围:0~1,接收到数据后,标志位置1,读取后标志位自动清零*/
uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag == 1)			//如果标志位为1{Serial_RxFlag = 0;return 1;					//则返回1,并自动清零标志位}return 0;						//如果标志位为0,则返回0
}/*** 函    数:获取串口接收的数据* 参    数:无* 返 回 值:接收的数据,范围:0~255*/
uint8_t Serial_GetRxData(void)
{return Serial_RxData;			//返回接收的数据变量
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*///单字节接受,没有太大意义,后面会有做字节接受
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{Serial_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量Serial_RxFlag = 1;										//置接收标志位变量为1USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"uint8_t RxData;			//定义用于接收串口数据的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化/*显示静态字符串*/OLED_ShowString(1, 1, "RxData:");/*串口初始化*/Serial_Init();		//串口初始化Serial_SendString("helloworld\r\n");//发送字符串,\r\n代表了换行//发送字符串,常用的方法char string[100];sprintf(string,"Num=%d\r\n"666);   //使用sprintf进行格式化字符串Serial_SendString(string);//使用可变参数发送函数进行发送Serial_Printf("Num=%d\r\n"888);while (1){//串口接受部分,轮询, //单字节接受,没有太大意义,后面会有做字节接受if (Serial_GetRxFlag() == 1)			//检查串口接收数据的标志位{RxData = Serial_GetRxData();		//获取串口接收的数据Serial_SendByte(RxData);			//串口将收到的数据回传回去,用于测试OLED_ShowHexNum(1, 8, RxData, 2);	//显示串口接收的数据}}
}

传输中文

Serial_Printf("你好,世界\r\n");

第一种使用UTF-8进行传输
在这里插入图片描述在程序中直接写汉字有时候编译器会报错,所以需要给编译器输入一个参数
在这里插入图片描述在这里插入图片描述接受方采用UTF-8进行解码,可以看到中文

第二种使用GB2312编码

在这里插入图片描述
修改完之后,此时还是UTF-8格式,需要删除汉字,然后关闭文件,重新打开文件
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

串口收发数据包(多字节数据)

串口收发hex数据包
#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的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*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_Init,配置USART1/*中断输出配置*/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);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数: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累乘到结果}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发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中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);		//清除标志位}
}

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);}}
}

在这里插入图片描述

串口收发文本
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>char Serial_RxPacket[100];				//定义接收数据包数组,数据包格式"@MSG\r\n"
uint8_t Serial_RxFlag;					//定义接收数据包标志位/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*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_Init,配置USART1/*中断输出配置*/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);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数: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累乘到结果}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发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中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);		//清除标志位}
}
#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指令{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;			//处理完成后,需要将接收数据包标志位清零,否则将无法接收后续数据包}}
}

包头为@,包尾为换行符
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

数据包格式

在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Vue的项目启动指令分析

通过Vue CLI脚手架创建的项目&#xff0c;默认的启动项目方式是 npm run serve 这里的serve是可以修改的。 在创建的项目目录中&#xff0c;找到package.json 双击打开&#xff0c;找到scripts部分 在scripts部分&#xff0c;有一个"serve"键值对&#xff0c;这里的…

课时115:sed命令_进阶实践_高阶用法2

2.2.4 高阶用法2 学习目标 这一节&#xff0c;我们从 暂存实践、其他实践、小结 三个方面来学习。 暂存实践 简介 我们可以在缓存空间和暂存空间中进行数据的简单读取&#xff0c;还可以对数据进行一些复杂性的编辑操作常见的高阶命令P 打印模式空间开端至\n内容&#xff0…

力扣每日一题108:将有序数组转换为二叉搜索树

题目 简单 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也…

【VS Code安装及远程服务器】(未完待续)

目录 一、Python 安装及设置1.1 Python安装1.2 Python设置 二、VScode 安装2.1 VScode安装2.2 中文界面设置及解决中文显示乱码问题2.2.1 中文界面设置2.2.2 解决中文显示乱码问题 2.3 VScode环境变量配置2.4 VScode添加到右键2.5 VScode终端&#xff0c;创建、激活虚拟环境&am…

VisualGLM-6B微调(V100)

Visualglm-6b-CSDN博客文章浏览阅读1.3k次。【官方教程】XrayGLM微调实践&#xff0c;&#xff08;加强后的GPT-3.5&#xff09;能力媲美4.0&#xff0c;无次数限制。_visualglm-6bhttps://blog.csdn.net/u012193416/article/details/131074962?ops_request_misc%257B%2522req…

Java | Leetcode Java题解之第70题爬楼梯

题目&#xff1a; 题解&#xff1a; public class Solution {public int climbStairs(int n) {double sqrt5 Math.sqrt(5);double fibn Math.pow((1 sqrt5) / 2, n 1) - Math.pow((1 - sqrt5) / 2, n 1);return (int) Math.round(fibn / sqrt5);} }

解密中国首个“音乐版Sora” | 最新快讯

编辑部发自 AIGC 峰会 量子位公众号 QbitAI 文生图、文生音频、文生视频、AI 搜索引擎……大模型在多模态的进程可谓是愈演愈烈。 而聚焦在国内&#xff0c;有这么一家公司在 AIGC 大热潮的前后&#xff0c;单是“首个”就占了四席&#xff1a; 发布中国首个开源文本大模型国内…

转速(RPM)和角速度转换FC(CODESYS ST代码)

1、频率和转速转换功能块 频率和转速转换功能块(CODESYS ST源代码)-CSDN博客文章浏览阅读16次。1、转速和频率转换功能块转速和频率转换功能块(CODESYS ST源代码)-CSDN博客文章浏览阅读10次。1、转速/频率常用转换关系转速/频率/线速度/角速度计算FC_200 plc计算角速度-CSDN博…

qt for ios 之 ios app 发布

ios 发布&#xff0c;需要准备的发布证书&#xff0c;下面就来详细说说发布的一些流程&#xff1a; macOS中安装 appuploader可以管理证书和描述文件&#xff1a; 1&#xff09;创建 BundleID&#xff1a; 2&#xff09;创建证书&#xff1a; 这里创建两个证书&#xff1a; 一…

BiTCN:基于卷积网络的多元时间序列预测

在时间序列预测领域中&#xff0c;模型的体系结构通常依赖于多层感知器(MLP)或Transformer体系结构。 基于mlp的模型&#xff0c;如N-HiTS, TiDE和TSMixer&#xff0c;可以在保持快速训练的同时获得非常好的预测性能。基于Transformer的模型&#xff0c;如PatchTST和ittransfo…

用于密集预测任务的通道知识蒸馏

摘要 https://arxiv.org/pdf/2011.13256 知识蒸馏&#xff08;KD&#xff09;已被证明是训练紧凑密集预测模型的简单有效工具。通过从大型教师网络转移而来的额外监督来训练轻量级学生网络。大多数先前的针对密集预测任务的KD变体都在空间域中对学生网络和教师网络的激活图进行…

泛微OA表单调用JSP

表单插入JS代码,并且设置id,传入表单参数给后端,后端添加jsp文件,使用ajax调用,详细步骤如下: 对应的框内添加id属性,如下图: 然后插入js代码,代码如下: <script> jQuery(document).ready(function() {// 在表单的按钮单元格插入自定义属性:ID:btnLinkvar …

C语言学习【多个函数调用、调试程序】

C语言学习【多个函数调用、调试程序】 函数调用 /* 一个文件中包含两个函数 */#include "stdio.h"void butler(void); /* ANSI/ISO C函数原型 */ int main(void) {printf("I will summon the bulter function. \n");butler();printf…

《LTC与铁三角∶从线索到回款-人民邮电》关于铁三角不错的论述

《LTC与铁三角∶从线索到回款-人民邮电》一书中&#xff0c;关于铁三角不错的论述&#xff0c;收藏之&#xff1a;客户责任人的角色定义及核心价值 AR 的核心价值定位主要体现在三个方面&#xff1a;客户关系、 客户满意度、竞争对手 “ 压制 ” 。 维护客户关系&#x…

redis之地理空间GEO

使用中文时会乱码&#xff0c;我们可以在启动添加命令 redis-cli -a 密码 -- raw 即可 由名字知其是和地理位置相关的&#xff0c;其将三维地球划分为二维平面进行存储。 命令如下&#xff1a; geoadd name 经度 纬度 名称 &#xff1a;在name中添加一个坐标 zrang name 0…

【Linux】进程控制 之 进程创建 进程终止 进程等待 进程替换

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

Python-VBA函数之旅-oct函数

目录 一、oct函数的常见应用场景 二、oct函数使用注意事项 三、如何用好oct函数&#xff1f; 1、oct函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;神奇夜光杯-CSDN博客 一、oct函数的常见应用场景 oc…

鸿蒙UI复用

鸿蒙UI复用 简介BuilderBuilder的使用方式一Builder的使用方式二Builder的使用方式三 Component使用Component复用UI 简介 在页面开发过程中&#xff0c;会遇到有UI相似的结构&#xff0c;如果每个UI都单独声明一份&#xff0c;会产生大量冗余代码&#xff0c;不利于阅读。遇到…

数仓开发:DataX数据采集阶段

结尾有Datax的安装部署教程~ 一、Datax介绍 官网&#xff1a; DataX/introduction.md at master alibaba/DataX GitHub DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。 DataX 实现了包括 MySQL、Oracle、Oce…

【Linux 进程】 自定义shell

目录 关于shell 1.打印提示符&&获取用户命令字符​编辑 2.分割字符串 3.检查是否为内建命令 cd命令 export命令 echo命令 1.输出最后一个执行的命令的状态退出码&#xff08;返回码&#xff09; 2.输出指定环境变量 4.执行外部命令 关于shell Shell 是计算机操…