章节说明
STM32 IAP固件升级实验分为以下的章节(加粗的字体是本章节的内容):
一、Flash和RAM的区域划分、工程建立、程序分散加载、程序烧写
二、Stm32 bootloader、application、firmware 程序的分析和编写
三、使用DMA收发串口的不定长数据
四、通信协议的设计
五、STM32 IAP程序的设计
六、上位机的程序的编写
一、前言
为了能使上位机和下位机能进行可靠的通信,所以需要设计一个相对可靠的协议。当然设计的协议也不会太复杂,但是该有的功能还是得有。数据头部,控制指令,数据长度,数据,校验码。这种类型通讯协议用于串口相对还是比较简单便捷的,当然也可以设计的很复杂例如:加入版本控制,物理设备号等等。在本文中就不涉及太复杂的东西了,就设计一个相对完整又相对简单的协议。话不多说,接下来进入正题吧。
二、通讯格式
设计的通讯格式为问答式(即一问一答的方式),分为控制指令,和应答指令。
1、控制指令格式
- 首部 : 所有的数据包都要加包头:0xA5A5
- 父指令: 根据需求来设计
- 子指令: 根据需求来设计
- 长度 : 数据长度字节,用来指定该帧中携带数据的长度(单位是长度)
- 数据 : 该帧中写带的数据内容
- 校验码: 校验码使用校验和的方式;校验和是子指令到校验和之间的所有字节之和,超出 2 字节的进位忽略
2、应答指令格式
- 首部 : 所有的数据包都要加包头:0xA5A5
- 父指令: 根据需求来设计
- 子指令: 根据需求来设计
- 长度 : 数据长度字节,用来指定该帧中携带数据的长度(单位是长度)
- 数据 : 该帧中写带的数据内容
- 校验码: 校验码使用校验和的方式;校验和是子指令到校验和之间的所有字节之和,超出 2 字节的进位忽略
三、控制指令设计
1、查询指令/复位指令
2、程序更新控制指令
3、数据传送指令
四、应答指令设计
1、查询应答指令
2、状态指令
扫描二维码关注公众号,回复: 9651854 查看本文章
五、帧管理程序设计
1、帧管理数据结构
typedef struct
{
u8 *buffer;//指向一块buff,用于存储一帧的数据
u16 head; //数据存储在头部的位置
u16 count; //接收到的数据
u16 BUFFERLENGTH; //buffer的最大长度
u16 MIN_FRAME_LEN; //最小帧
FRAME_VALIDATE ValidateFrame; //这个是一个回调函数的指针,主要是处理帧的回掉函数
} FrameBufferStr;
2、帧管理程序
void Append_Frame_Buffer(FrameBufferStr *frame, u8 *input, u16 length)
{
u16 i = 0;
//s16 head=0;
u16 mlen=0;
// 如果上传数据长度比缓存总长度还要长
if (length > frame->BUFFERLENGTH)
{
// 此处可添加错误代码
return;
}
/* 需要将后面的数据copy到前面 */
if((u16)frame->count + length > frame->BUFFERLENGTH)
{
/* frame->head 记录frame->buffer的头部,frame->count记录接收到数据的尾部*/
memmove(frame->buffer, frame->buffer+frame->head, frame->count - frame->head);
/* 重新计算接收到数据的尾部 */
frame->count = frame->count - frame->head;
/* 头部指向为0 */
frame->head = 0;
/* 将后面的数据清零 */
memset(frame->buffer + frame->count, 0, frame->BUFFERLENGTH - frame->count);
/* 后面需要添加的数据超出了fream的范围 */
if((frame->count + length) > frame->BUFFERLENGTH)
{
// index out of buffer range
/* 将数据清零 */
frame->count=0;
frame->head=0;
return;
}
}
if(length != 0)
{
/* 复制后面添加进来的数据 */
memcpy(frame->buffer+frame->count, input, length);
frame->count+=length;
}
i = 0;
while(frame->head + i < frame->count)
{
/* 调用回调函数,解析接收数据,一个一个往下迭代,直到找到需要校验的起始头部 */
mlen = frame->ValidateFrame(frame->buffer + frame->head + i, frame->count - frame->head - i);
if(mlen > 0)
{
/* 返回的长度比buffer的总长度长,也将frame初始化 */
if(mlen > frame->BUFFERLENGTH)
{
frame->head = 0;
frame->count = 0;
memset(frame->buffer, 0, frame->BUFFERLENGTH);
break;
}
/* 处理完这帧数据了,将数据清零 */
frame->head = frame->head + i + mlen;
/* 如果计算完的后的frame->head后比 frame->count大,说明接收的数据有偏差*/
if(frame->head > frame->count)
{
frame->count = frame->head;
}
i = 0;
if(frame->head == frame->count)
{
/* 有偏差则需要重新将frame->buffer初始化 */
frame->head = 0;
frame->count = 0;
memset(frame->buffer, 0, frame->BUFFERLENGTH);
break;
}
}
else
{
i++;
}
}
}
说明
这个帧管理程序结合上一章的DMA+USART接收数据来使用,是个比较好用的方式。
原本想写的详细点的,但最近在毕业论文和公司的事情,实在是没时间写的很详细。感兴趣的小伙伴可以先研究上面的程序,后面有时间再补回来。