【STM32】STM32标准库学习笔记(一)——串口通信
前言
关于STM32的串口通信之前的文章里有介绍过,传送门如下。
串口通信(往期)传送门
【串口通信】K210与STM32串口通信、K210与OpenMV串口通信
但是我觉得之前这一份代码还不够好用,因此我重制了一份。
本篇博文主要介绍STM32(标准库)如何进行串口通信,使用的STM32型号为STM32F103ZET6,我将串口通信代码划分为了两部分,一部分为硬件部分,一部分为软件部分。 软件部分与库无关,不管是HAL库还是标准库都能用,硬件部分则是兼容HAL库或者标准库,本篇主要介绍标准库的串口通信。 事实上,在其他平台,只要配置好串口,软件部分的代码也是能够通用的。
串口通信
串口通信思路
串口通信的思路就是发送端将数据打包发送,接收端从帧头开始接收数据,为了防止帧头和发送数据混在一起,所以设置两个帧头。当两个帧头都满足条件时才会继续接收,设置有效数据长度位是为了方便灵活接收不同长度的数据,比如有效数据长度位是2,则有data[0]和data[1]两个数据,最后再接收一位校验位,若校验通过则解析并保存数据。
| 帧头1 | 帧头2 | 有效数据长度位 | data[0] | …… | data[n] | 校验位 |
|---|
软件部分代码设计
依据上面的串口通信思路,同时为了方便定义变量,定义结构体来保存各类变量, 在需要定义新变量的时候使用结构体定义即可。这部分保存在uartprotocol.h中,完整源码在文章末尾。
// 串口发送相关结构体
typedef struct
{
uint8_t head1; // 帧头1
uint8_t head2; // 帧头2
uint8_t length; // 有效数据长度
uint8_t cnt; // 总数据长度
uint8_t data[40]; // 有效数据数组
uint8_t transmit_data[50]; // 实际发送的数组 附带上帧头1 帧头2 有效数据长度位 校验位
}DataTransmit;
// 串口接收相关结构体
typedef struct
{
uint8_t head1; // 帧头1
uint8_t head2; // 帧头2
uint8_t length; // 有效数据长度
uint8_t cnt; // 总数据长度
uint8_t state; // 接收状态
uint8_t i; // 有效数据下标
uint8_t data; // 接收数据缓冲位
uint8_t receive_data[50]; // 实际接收的数组 附带上帧头1 帧头2 有效数据长度位 校验位
}DataReceive;
// 接收数据解析相关结构体
typedef struct
{
uint16_t x; // 目标x轴坐标
uint16_t y; // 目标y轴坐标
uint8_t color; // 目标颜色标志位
uint8_t shape; // 目标形状标志位
uint8_t flag; // 目标标志位
}TargetProperty;
串口发送部分
定义串口发送数据结构体初始化函数 Data_Transmit_Init
此函数带入参数为结构体,帧头,有效数据长度,在 while 循环前初始化对应的结构体即可设置结构体对应发送数据的帧头以及有效数据长度位。
/********************************************************************
串口发送数据结构体初始化函数 Data_Transmit_Init
各参数作用
DataTransmit *data: 选择要初始化的串口发送数据结构体
head1: 帧头1
head2: 帧头2
length: 有效数据长度
********************************************************************/
void Data_Transmit_Init(DataTransmit *data, uint8_t head1, uint8_t head2, uint8_t length)
{
data -> head1 = head1;
data -> head2 = head2;
data -> length = length;
data -> cnt = length + 4;
for(uint8_t i = 0; i < length; i++)
{
data -> data[i] = 0;
}
for(uint8_t j = 0; j < length + 4; j++)
{
data -> transmit_data[j] = 0;
}
}
定义串口发送数据打包函数 Data_Pack
此函数带入参数为结构体,在串口发送函数之前(串口发送函数在硬件部分),将要发送的结构体打包即可。
/********************************************************************
串口发送数据打包函数 Data_Pack
各参数作用
DataTransmit *data: 选择要打包的串口发送数据结构体
********************************************************************/
void Data_Pack(DataTransmit *data)
{
data -> transmit_data[0] = data -> head1;
data -> transmit_data[1] = data -> head2;
data -> transmit_data[2] = data -> length;
for(uint8_t i = 0; i < data -> length; i++)
{
data -> transmit_data[3+i] = data -> data[i];
}
uint8_t sum = 0;
for(uint8_t j = 0; j < data -> length + 3; j++)
{
sum = sum + data -> transmit_data[j];
}
data -> transmit_data[data -> length + 3] = sum;
}
串口接收部分
定义串口接收数据结构体初始化函数 Data_Receive_Init
此函数带入参数为结构体,在 while 循环前初始化对应的结构体即可设置结构体对应接收数据的帧头,在接收函数中,只有满足帧头条件才会进行接收。
/********************************************************************
串口接收数据结构体初始化函数 Data_Receive_Init
各参数作用
DataReceive *data: 选择要初始化的串口接收数据结构体
head1: 帧头1
head2: 帧头2
********************************************************************/
void Data_Receive_Init(DataReceive *data, uint8_t head1, uint8_t head2)
{
data -> head1 = head1;
data -> head2 = head2;
data -> length = 0;
data -> cnt = 0;
data -> state = 0;
data -> i = 0;
data -> data = 0;
for(uint8_t j = 0; j < 50; j++)
{
data -> receive_data[j] = 0;
}
}
定义串口接收数据函数 Data_Receive
此函数带入参数为结构体以及被接收的数据,在串口中断函数中(串口中断函数在硬件部分),使用该函数接收数据即可,其工作过程如下。
每次根据不同的状态 state 进入不同的 if
进入状态0后,若传入的数据等于帧头1,则保存该数据,然后进入状态1。
进入状态1后,若传入的数据等于帧头2,则保存该数据,然后进入状态2。
进入状态2后,判断需要接收的有效数据长度是否小于40,若小于,则保存该数据以及总数据长度,然后进入状态3。
进入状态3后,开始接收有效数据长度个数据,接收完毕会进入状态4。
进入状态4后,保存最后一位校验位,然后状态置0,等待下一次接收。
不满足以上条件均会将状态置0重新开始接收。
/********************************************************************
串口接收数据函数 Data_Receive
各参数作用
DataReceive *data: 选择要接收的串口接收数据结构体
buf: 接收数据
********************************************************************/
void Data_Receive(DataReceive *data, uint8_t buf)
{
if(data -> state == 0 && buf == data -> head1)
{
data -> state = 1;
data -> receive_data[0] = buf;
}
else if(data -> state == 1 && buf == data -> head2)
{
data -> state = 2;
data -> receive_data[1] = buf;
}
else if(data -> state == 2 && buf < 40)
{
data -> state = 3;
data -> length = buf;
data -> cnt = buf+4;
data -> receive_data[2] = buf;
}
else if(data -> state == 3 && data -> length > 0)
{
data -> length = data -> length - 1;
data -> receive_data[3 + data -> i] = buf;
data -> i = data -> i + 1;
if(data -> length == 0)
{
data -> state = 4;
}
}
else if(data -> state == 4)
{
data -> receive_data[3 + data -> i] = buf;
data -> state = 0;
data -> i = 0;
}
else
{
data -> state = 0;
data -> i = 0;
}
}
串口接收数据解析部分
此部分根据个人需求自行定义,这里举例我的一个定义,提供一些思路。
首先是初始化函数,定义的结构体中的变量都需要初始化为0
/********************************************************************
串口接收数据结构体初始化函数 Target_Init
各参数作用
TargetAttribute *target: 选择要初始化的结构体
********************************************************************/
void Target_Init(TargetProperty *target)
{
target -> x = 0;
target -> y = 0;
target -> color = 0;
target -> shape = 0;
target -> flag = 0;
}
然后是数据解析部分,带入参数为接收数据的结构体,以及解析结果保存的结构体,进入该函数后首先会对接收数据进行和校验,和校验通过则保存数据。
/********************************************************************
串口接收数据解析函数 Target_Parse
各参数作用
DataReceive *data: 选择被解析的结构体
TargetProperty *target: 选择解析完成保存的结构体
********************************************************************/
void Target_Parse(DataReceive *data, TargetProperty *target)
{
uint8_t sum = 0;
uint8_t i = 0;
while(i < data -> cnt - 1)
{
sum = sum + data -> receive_data[i];
i = i + 1

本文详细介绍了STM32F103ZET6的串口通信实现,包括软件部分的结构体设计、数据打包与发送、接收解析,以及硬件部分的初始化和中断处理。使用标准库实现,适用于多种串口配置,提供了完整的测试模板和源码示例。
——串口通信&spm=1001.2101.3001.5002&articleId=126652321&d=1&t=3&u=9a104f1403f5412bb10a48ddfe41635f)
2618

被折叠的 条评论
为什么被折叠?



