【基于串口发送+接收】的【串口发送HEX数据包】
一、在Serial.c文件中添加收发HEX数据包部分
-
HEX数据包格式:此处为【固定包长】、【含包头包尾】、【包头FF】、【载荷数据4字节】、【包尾FE】
1、在Serial.c中初始化函数
Serial_Init
//为了收发数据包定义两个缓存区的数组//只存储发送或接收的载荷数据uint8_t Serial_TxPacket[4];uint8_t Serial_RxPacket[4];//如果收到一个数据包就置RxFlaguint8_t Serial_RxFlag;void Serial_Init(void){//第一步:RCC开启USART和GPIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//第二步:GPIO初始化,把TX配置成复用输出,RX配置成输入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复用推挽输出供USART1的TX使用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上拉输入供USART1的RX使用//第三步:配置USARTUSART_InitTypeDef USART_InitSturucture;USART_InitSturucture.USART_BaudRate = 9600;//波特率(Init函数内部自动算出波特率对应分配系数)USART_InitSturucture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制USART_InitSturucture.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//选择发送(接收)模式,若都需要则用‘|’并起来USART_InitSturucture.USART_Parity = USART_Parity_No;//校验位USART_InitSturucture.USART_StopBits = USART_StopBits_1;//停止位USART_InitSturucture.USART_WordLength = USART_WordLength_8b;//字长USART_Init(USART1,&USART_InitSturucture);//目前配置:9600波特率、8位字长、无校验、1位停止位、无流控、只有发送模式//第四步:开启中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出//第五步:配置NVIC//RXNE标志位一但置1,就会向NVIC申请中断//之后可用在中断函数里接收数据//中断函数名称查找启动文件【startup_stm32f10x_md.s】NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组NVIC_InitTypeDef NVIC_InitStructure;//初始化NVIC的USART1通道NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);//第六步:开启USART,初始化完成USART_Cmd(USART1,ENABLE);}//对Serial_RxFlag封装Get函数//实现读取后自动清除功能uint8_t Serial_GetRxFlag(void){if(Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;}//暂时不需要中断//中断函数接收数据void USART1_IRQHandler(void){//判断标志位,如果置1说明收到数据,进入ifif(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET){USART_ClearITPendingBit(USART1,USART_IT_RXNE);}}//发送数据函数//调用这个函数可以从TX引脚发送一个字节数据void Serial_SendByte(uint8_t Byte){//Byte变量写入发送数据寄存器TDR,TDR再传递给发送移位寄存器//最后一位一位地把数据移出到TX引脚,完成数据的发送USART_SendData(USART1,Byte);//等待TDR的数据全部转移到移位寄存器//数据还在TDR时仍写入数据会产生数据覆盖//【USART_FLAG_TXE】发送数据寄存器空标志位while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );//数据寄存器不空则一直循环,数据寄存器空则跳出循环}//发送数组/*第一个参数:数组首地址(数组名)第二个参数:指定传输多少字节*/void Serial_SendArray(uint8_t *Array,uint16_t Length){uint16_t i;for(i = 0; i < Length; i ++)//for循环执行Length次,可以对Array数据进行遍历{Serial_SendByte(Array[i]);//依次取出数组Array的每一项通过SendByte发送}}//发送字符串//字符串自带一个结束标志位(数据0),因此不需要传递长度参数void Serial_SendString(char *String){uint8_t i;for(i = 0; i < String[i] != 0; i ++)//循环条件用结束标志位判断{Serial_SendByte(String[i]);//依次取出字符串String的每一项通过SendByte发送}}/*以十进制拆分数据函数的返回值=X^Y*/uint32_t Serial_Pow(uint32_t X,uint32_t Y){uint32_t Result = 1;while( Y -- )//循环Y次{Result *= X;//Result累乘Y次X(X的Y次方)}return Result;}//发送数字(显示十进制字符串形式的数字)//不同进制的转换参考【OLED.h】void Serial_SendNumber(uint32_t Number,uint8_t Length){//把Number的个、十、百位以十进制拆分开//转换成字符数字对应的数据并依次发送uint8_t i;for(i = 0; i < Length ; i ++)//循环Length次{Serial_SendByte(Number / Serial_Pow(10 , Length - i - 1) % 10 + '0' );}//i的遍历从0开始,10^0是个位;10^1是十位;10^2是百位,以此类推……/*假设Length为2——第一次循环【2-0-1】=1,10^1是十位——第二次循环【2-1-1】=0,10^0是个位*///要以字符的形式显示需要加【偏移】(查ASCII码表)——0x30或'0"//参数会以十进制从高位到低位依次发送}/*【fputc】是【printf】函数的底层printf函数在打印的时候,就是不断调用fputc函数一个个打印的把fputc重定向到串口,那printf自然就输出到串口了*///移植printf函数int fputc(int ch , FILE *f){//把fputc重定向到串口Serial_SendByte( ch );return ch;}
2、在Serial.c中编写发送数据包函数
Serial_SendPacket
//调用函数,TxPacket数组的4个数据就会自动加上包头包尾发送void Serial_SendPacket(void){//发送包头0xFFSerial_SendByte(0xFF);//依次发送4个载荷数据Serial_SendArray(Serial_TxPacket,4);//发送包尾0xFESerial_SendByte(0xFE);}
3、在Serial.h中声明初始化函数
Serial_Init和发送数据包函数
Serial_SendPacket和
发送/缓存数组
extern uint8_t Serial_TxPacket[];//声明数组外部可调用(数组声明可以不用数量)void Serial_SendPacket(void);//声明数组外部可调用(数组声明可以不用数量)extern uint8_t Serial_TxPacket[];//发送缓存数组extern uint8_t Serial_RxPacket[];//接收缓存数组
4、在主循环中编写程序主体
int main(void){OLED_Init();Serial_Init();//初始化之后先填充缓存区数组Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;//SendPacker取出数组内容加上包头包尾一起发送Serial_SendPacket();while(1){}}
实现功能:上电后初始化串口,填充缓存区数组,通过SendPacket函数取出数组内容加上包头包尾发送
串口调试软件显示:
FF 01 02 03 04 FE
【基于串口发送HEX数据包】的【串口收发HEX数据包】
1、在Serial.c中初始化函数
Serial_Init
#include "stm32f10x.h" // Device header#include <stdio.h>//为了收发数据包定义两个缓存区的数组//只存储发送或接收的载荷数据uint8_t Serial_TxPacket[4];uint8_t Serial_RxPacket[4];//如果收到一个数据包就置RxFlaguint8_t Serial_RxFlag;void Serial_Init(void){//第一步:RCC开启USART和GPIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//第二步:GPIO初始化,把TX配置成复用输出,RX配置成输入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复用推挽输出供USART1的TX使用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上拉输入供USART1的RX使用//第三步:配置USARTUSART_InitTypeDef USART_InitSturucture;USART_InitSturucture.USART_BaudRate = 9600;//波特率(Init函数内部自动算出波特率对应分配系数)USART_InitSturucture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制USART_InitSturucture.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//选择发送(接收)模式,若都需要则用‘|’并起来USART_InitSturucture.USART_Parity = USART_Parity_No;//校验位USART_InitSturucture.USART_StopBits = USART_StopBits_1;//停止位USART_InitSturucture.USART_WordLength = USART_WordLength_8b;//字长USART_Init(USART1,&USART_InitSturucture);//目前配置:9600波特率、8位字长、无校验、1位停止位、无流控、只有发送模式//第四步:开启中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出//第五步:配置NVIC//RXNE标志位一但置1,就会向NVIC申请中断//之后可用在中断函数里接收数据//中断函数名称查找启动文件【startup_stm32f10x_md.s】NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组NVIC_InitTypeDef NVIC_InitStructure;//初始化NVIC的USART1通道NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);//第六步:开启USART,初始化完成USART_Cmd(USART1,ENABLE);}//获取接收标志位RxFlag//实现读取后自动清除功能uint8_t Serial_GetRxFlag(void){if(Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;}//发送数据函数//调用这个函数可以从TX引脚发送一个字节数据void Serial_SendByte(uint8_t Byte){//Byte变量写入发送数据寄存器TDR,TDR再传递给发送移位寄存器//最后一位一位地把数据移出到TX引脚,完成数据的发送USART_SendData(USART1,Byte);//等待TDR的数据全部转移到移位寄存器//数据还在TDR时仍写入数据会产生数据覆盖//【USART_FLAG_TXE】发送数据寄存器空标志位while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );//数据寄存器不空则一直循环,数据寄存器空则跳出循环}//发送数组/*第一个参数:数组首地址(数组名)第二个参数:指定传输多少字节*/void Serial_SendArray(uint8_t *Array,uint16_t Length){uint16_t i;for(i = 0; i < Length; i ++)//for循环执行Length次,可以对Array数据进行遍历{Serial_SendByte(Array[i]);//依次取出数组Array的每一项通过SendByte发送}}//发送字符串//字符串自带一个结束标志位(数据0),因此不需要传递长度参数void Serial_SendString(char *String){uint8_t i;for(i = 0; i < String[i] != 0; i ++)//循环条件用结束标志位判断{Serial_SendByte(String[i]);//依次取出字符串String的每一项通过SendByte发送}}/*以十进制拆分数据函数的返回值=X^Y*/uint32_t Serial_Pow(uint32_t X,uint32_t Y){uint32_t Result = 1;while( Y -- )//循环Y次{Result *= X;//Result累乘Y次X(X的Y次方)}return Result;}//发送数字(显示十进制字符串形式的数字)//不同进制的转换参考【OLED.h】void Serial_SendNumber(uint32_t Number,uint8_t Length){//把Number的个、十、百位以十进制拆分开//转换成字符数字对应的数据并依次发送uint8_t i;for(i = 0; i < Length ; i ++)//循环Length次{Serial_SendByte(Number / Serial_Pow(10 , Length - i - 1) % 10 + '0' );}//i的遍历从0开始,10^0是个位;10^1是十位;10^2是百位,以此类推……/*假设Length为2——第一次循环【2-0-1】=1,10^1是十位——第二次循环【2-1-1】=0,10^0是个位*///要以字符的形式显示需要加【偏移】(查ASCII码表)——0x30或'0"//参数会以十进制从高位到低位依次发送}/*【fputc】是【printf】函数的底层printf函数在打印的时候,就是不断调用fputc函数一个个打印的把fputc重定向到串口,那printf自然就输出到串口了*///移植printf函数int fputc(int ch , FILE *f){//把fputc重定向到串口Serial_SendByte( ch );return ch;}//调用函数,TxPacket数组的4个数据就会自动加上包头包尾发送void Serial_SendPacket(void){//发送包头0xFFSerial_SendByte(0xFF);//依次发送4个载荷数据Serial_SendArray(Serial_TxPacket,4);//发送包尾0xFESerial_SendByte(0xFE);}
2、在Serial.c中编写发送中断函数
USART1_IRQHandler
//中断函数//用状态机执行接收逻辑(根据状态机顺序图)//接收数据包然后把载荷数据存在【RxPacket】数组void USART1_IRQHandler(void){//定义一个标志当前状态的静态变量RxState(只在本函数使用)static uint8_t RxState = 0;//指示数据接收到哪一个了static uint8_t pRxPacket = 0;//判断标志位,如果置1说明收到数据,进入ifif(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET){//获取RxDatauint8_t RxData = USART_ReceiveData(USART1);//根据RxState的不同进入不同的处理程序if(RxState == 0)//状态0:进入等待包头程序{if(RxData == 0xFF)//收到包头{RxState = 1;//转移到状态1//未收到包头,RxState仍为0pRxPacket = 0;//提前清零为状态1做准备}}else if(RxState == 1)//状态1:进入接收数据程序{//每进一次状态,数据转存一次缓存数组,同时存的位置++//依次接收4个数据存在RxPacket数组//第pRxPacket个数据等于RxData,将RxData存在接收数组里Serial_RxPacket[pRxPacket] = RxData;pRxPacket ++;//移动到下一位置if(pRxPacket >= 4)//4个载荷数据已经收完了{RxState = 2;//转移到状态2}}else if(RxState == 2)//状态2:进入等待包尾程序{if(RxData == 0xFE)//收到包尾{RxState = 0;//转移到状态0//未收到包头,RxState仍为2//此时代表一个数据包已经接收到,置一个接收标志位Serial_RxFlag =1;}}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}}
3、在Serial.h中声明初始化函数
Serial_Init和
发送/接收数组
//声明数组外部可调用(数组声明可以不用数量)extern uint8_t Serial_TxPacket[];//发送缓存数组extern uint8_t Serial_RxPacket[];//接收缓存数组
4、在主循环中编写程序主体
int main(void){OLED_Init();Serial_Init();//初始化之后先填充缓存区数组Serial_TxPacket[0] = 0x01;Serial_TxPacket[1] = 0x02;Serial_TxPacket[2] = 0x03;Serial_TxPacket[3] = 0x04;//SendPacker取出数组内容加上包头包尾一起发送Serial_SendPacket();while(1){//不断读取RxFlagif(Serial_GetRxFlag() == 1)//收到了数据包{OLED_ShowHexNum(1,1,Serial_RxPacket[0],2);OLED_ShowHexNum(1,4,Serial_RxPacket[1],2);OLED_ShowHexNum(1,7,Serial_RxPacket[2],2);OLED_ShowHexNum(1,10,Serial_RxPacket[3],2);}}}
实现功能:上电后初始化串口,填充缓存区数组,通过串口调试软件发送
FF xx xx xx xx FE
串口调试软件显示:
FF xx xx xx xx FE
【最终功能】串口收发HEX数据包
在主循环中编写程序主体
uint8_t KeyNum;int main(void){OLED_Init();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)//按键按下执行发送{//变换之前填充的缓存区数据Serial_TxPacket[0] ++;Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;//SendPacker取出数组内容加上包头包尾一起发送Serial_SendPacket();OLED_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);}//不断读取RxFlagif(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);}}}
实现功能:上电后初始化串口,填充缓存区数组,按下按键缓存区数组数据++,发送到串口助手上;
并且通过串口调试软件发送
FF xx xx xx xx FE,
串口调试软件显示:
FF xx xx xx xx FE