目录
前言
数据包
1.HEX数据包
2.文本数据包
C编程实现stm32收发数据包
1.HEX数据包的收发
2.文本数据包的收发
前言
前面几期讲解了USART串口发送数据和接收数据的原理,那本期在前面的基础上学习stm32 USART串口发送和接收数据包。本期包括两个项目,分别是USART发送HEX数据包和USART发送文本数据包。(视频:[9-4] USART串口数据包_哔哩哔哩_bilibili [9-5] 串口收发HEX数据包&串口收发文本数据包_哔哩哔哩_bilibili)
本期项目工程代码以上传至百度网盘可自行下载。
链接:https://pan.baidu.com/s/1EDoAsXeJFw-mfhj0DQAnUA?pwd=0721
提取码:0721
数据包
数据包我们其实都不陌生的,比如在网上在发送文件或消息给对方的时候实际上就是发送一个数据包,这个数据包是以编码后的形式来去传输的。数据包可以分为三个部分,分别是起始区域,数据存储区域,终止区域。当检测到起始区域的时候就开始对数据包进行读取操作,直到终止区域就结束读取操作,这里我们学习stm32要用到的数据包有两种,分别是HEX数据包和文本数据包。下面就分别进行一一讲解。
1.HEX数据包
这一类数据包实际上是单片机与其他设备之间通讯的传输流数据包,是机器之间通讯的数据,其是一种十六进制的数据。就数据包来说可以分为有固定长度的数据包和无固定长度的数据包。
对于HEX数据包去定义长度是为了避免包头和包尾出现与数据域的数据值重复(比如包头尾0xFF 紧接着后面的数据又出现0xFF,或者结尾尾0xFE的前面一个数据也是0xFE),如果不定义长度的话这会导致接收的时候不知道哪里是开头,这会使得数据接收出问题,所以去定义长度,当读取到达我们要求的长度的时候就结束。
- 固定包长,含包头包尾
- 可变包长,含包头包尾
HEX数据包的接收要分三步走,第一就是去检查包头,如果拿到了包头的话就可以继续读取操作跳转到S=1步骤,如果没有拿到的话就会原路返回出于一直等待状态。然后就是接收数据域的数据,假如是固定长度的数据,那就进行数据量统计,边获取边统计,统计到足够的时候就结束,跳转到S=2包尾。
2.文本数据包
同样的,文本数据包也是分为有固定长度的数据包和无固定长度的数据包。
- 固定包长,含包头包尾
- 可变包长,含包头包尾
对于文本数据包的获取,我们同样也是需要去定义包头和包尾,但是对于文本数据包一般包头和包尾出都是相对比较特殊的字符,所以基本上不会涉及到数据域与包头和包尾发送冲突,可以去用不固定长度数据的形式来作为数据包。其接收过程也是跟HEX的过程一样的。
C编程实现stm32收发数据包
本次实验的代码是在上一期的基础上进行修改就可以实现数据包的发送,如果有相关函数以及操作不了解的可以查看上一期内容:stm32入门-----USART串口通讯(上——实践篇)-CSDN博客
1.HEX数据包的收发
先看实验现象:
这个是串口接收和发送的数据。
OLED屏幕的显示结果如下: 电路连线图如下,当按下按键的时候stm32就会向电脑发送数据包。
工程文件:
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 Serial_sendbyte(uint8_t byte);
void Serial_sendarray(uint8_t * array,uint16_t length);
void Serial_sendstring(char* string);
void Serial_sendnumber(uint32_t num,uint8_t length);
void Serial_sprintf(char* format,...);
void Serial_sendpacket();
#endif // !__
main.c文件代码如下
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"
uint8_t data;
uint8_t flag; //中断结束标志
uint8_t keynum;
uint8_t Serial_getflag();
uint8_t Serial_getdata();
int main(void)
{
OLED_Init();
Serial_init();
Key_init();
//要发送的初始数据包
Serial_Txpacket[0] = 0x01;
Serial_Txpacket[1] = 0x02;
Serial_Txpacket[2] = 0x03;
Serial_Txpacket[3] = 0x04;
OLED_ShowString(1, 1, "Txpacket:");
OLED_ShowString(3, 1, "Rxpacket:");
while (1) {
keynum = Keynum();
//点击按键单片机发送给电脑
if (keynum == 1) {
//对数据进行每一个+1处理
Serial_Txpacket[0]++;
Serial_Txpacket[1]++;
Serial_Txpacket[2]++;
Serial_Txpacket[3]++;
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);
}
// 这里是单片机接收电脑的数据
if (Serial_getflag() == 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);
}
}
}
//获取中断结束标志位
uint8_t Serial_getflag() {
if (flag) {
flag = 0;
return 1;
}
return 0;
}
//获取数据
uint8_t Serial_getdata() {
return data;
}
//中断读取接收,这是一个一个byte来接收的
void USART1_IRQHandler() {
static uint8_t Rxstate = 0;//局部静态变量表示S,0,1,2对应的状态
static uint8_t curpacket = 0;
//先判断标志位
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
uint8_t Rxdata = USART_ReceiveData(USART1);//获取一个byte的数据
// S=0 进入包头位置
if (Rxstate == 0) {
if (Rxdata == 0xFF) { //进入到下一个状态 S=1 开始读取数据
Rxstate = 1;
curpacket = 0;
}
}
//S=1 读取数据状态
else if (Rxstate == 1) {
Serial_Rxpacket[curpacket++] = Rxdata;
if (curpacket >= 4) {
Rxstate = 2;
}
}
// S=2 数据读取结束状态
else if (Rxstate == 2) {
if (Rxdata == 0xFE) {
Rxstate = 0;
flag = 1; //中断结束falg=1
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断标志位
}
}
2.文本数据包的收发
实验现象:
这里会去检测发送的文本数据包,如果发送的是 @ON 那就表示开灯 ,如果发送@OFF 是表示关灯,如果发送其他的数据包会显示指令错误。下面是串口调试助手的现象。
USART串口发送文本数据包
发送@ON 数据的时候就表示开灯,现象如下:
发送@OFF 表示关灯,现象如下:
电路连线图:
工程文件:
main.c文件代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "Key.h"
#include <string.h>
uint8_t data;
uint8_t flag;
uint8_t keynum;
uint8_t Serial_getflag();
uint8_t Serial_getdata();
int main(void)
{
OLED_Init();
Serial_init();
Key_init();
LED_init();
OLED_ShowString(1, 1, "Txpacket:");
OLED_ShowString(3, 1, "Rxpacket:");
while (1) {
if (flag == 1) {
OLED_ShowString(4, 1, " "); //擦除第四行
OLED_ShowString(4, 1, Serial_Rxpacket);
//字符串匹配用string库里面的strcmp函数,大家都再熟悉不过了
if (strcmp(Serial_Rxpacket, "ON")==0) {
LED1_ON();
Serial_sendstring("LED_ON_OK\r\n");
OLED_ShowString(2, 1, " "); //擦除
OLED_ShowString(2, 1, "LED_ON_OK");
}
else if (strcmp(Serial_Rxpacket, "OFF")==0) {
LED1_OFF();
Serial_sendstring("LED_OFF_OK\r\n");
OLED_ShowString(2, 1, " "); //擦除
OLED_ShowString(2, 1, "LED_OFF_OK");
}
else {
Serial_sendstring("ERROR_COMMAND\r\n");
OLED_ShowString(2, 1, " "); //擦除
OLED_ShowString(2, 1, "ERROR_COMMAND");
}
flag = 0;
}
}
}
//获取数据
uint8_t Serial_getdata() {
return data;
}
//中断读取接收,这是一个一个byte来接收的
void USART1_IRQHandler() {
static uint8_t Rxstate = 0;//局部静态变量表示s,0,1,2对应的状态
static uint8_t curpacket = 0;
//先判断标志位
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
uint8_t Rxdata = USART_ReceiveData(USART1);
//状态0
if (Rxstate == 0) {
if (Rxdata == '@' && flag==0) { //进入到下一个状态
Rxstate = 1;
curpacket = 0;
}
}
//状态1
else if (Rxstate == 1) {
if (Rxdata == '\r')
Rxstate = 2; //接收完成跳转到状态2
else
Serial_Rxpacket[curpacket++] = Rxdata;
}
//状态2
else if (Rxstate == 2) {
if (Rxdata == '\n') {
Rxstate = 0;
Serial_Rxpacket[curpacket] = '\0';//给字符串添加终止符
flag = 1;
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断标志位
}
}
以上就是本期的全部内容了,我们下一期见!
今日壁纸: