STM32 IAP固件升级(四)

章节说明

STM32 IAP固件升级实验分为以下的章节(加粗的字体是本章节的内容):

一、Flash和RAM的区域划分、工程建立、程序分散加载、程序烧写
二、Stm32 bootloader、application、firmware 程序的分析和编写
三、使用DMA收发串口的不定长数据
四、通信协议的设计
五、STM32 IAP程序的设计
六、上位机的程序的编写

一、前言

为了能使上位机和下位机能进行可靠的通信,所以需要设计一个相对可靠的协议。当然设计的协议也不会太复杂,但是该有的功能还是得有。数据头部,控制指令,数据长度,数据,校验码。这种类型通讯协议用于串口相对还是比较简单便捷的,当然也可以设计的很复杂例如:加入版本控制,物理设备号等等。在本文中就不涉及太复杂的东西了,就设计一个相对完整又相对简单的协议。话不多说,接下来进入正题吧。

二、通讯格式

设计的通讯格式为问答式(即一问一答的方式),分为控制指令,和应答指令。

1、控制指令格式

  1. 首部 : 所有的数据包都要加包头:0xA5A5
  2. 父指令: 根据需求来设计
  3. 子指令: 根据需求来设计
  4. 长度 : 数据长度字节,用来指定该帧中携带数据的长度(单位是长度)
  5. 数据 : 该帧中写带的数据内容
  6. 校验码: 校验码使用校验和的方式;校验和是子指令到校验和之间的所有字节之和,超出 2 字节的进位忽略

2、应答指令格式

  1. 首部 : 所有的数据包都要加包头:0xA5A5
  2. 父指令: 根据需求来设计
  3. 子指令: 根据需求来设计
  4. 长度 : 数据长度字节,用来指定该帧中携带数据的长度(单位是长度)
  5. 数据 : 该帧中写带的数据内容
  6. 校验码: 校验码使用校验和的方式;校验和是子指令到校验和之间的所有字节之和,超出 2 字节的进位忽略

三、控制指令设计

1、查询指令/复位指令




2、程序更新控制指令




3、数据传送指令

四、应答指令设计

1、查询应答指令



2、状态指令



五、帧管理程序设计

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接收数据来使用,是个比较好用的方式。
原本想写的详细点的,但最近在毕业论文和公司的事情,实在是没时间写的很详细。感兴趣的小伙伴可以先研究上面的程序,后面有时间再补回来。

猜你喜欢

转载自www.cnblogs.com/gulan-zmc/p/12364166.html