历史上的今天
返回首页

历史上的今天

今天是:2025年05月27日(星期二)

正在发生

2019年05月27日 | 基于STM32的串口环形队列IAP调试心得

2019-05-27 来源:eefocus

IAP很常见了,我这里主要是记录一下我所使用的方法,调试也花了两天时间。我所用的型号是STM32F103C8T6,这个片子估计是目前性价比最高的了,所以平时也都是用的这个。这个IC有64KFlash和20K的RAM,也有小道说有后置隐藏的64K,也就是说其实是有128K,我一直也没有测试,有空测测,有大神这样说,估计是可以的。这里重点记录一下我写的IAP思路和代码以及细节和遇到坑的地方。先大体的概述一下,最后贴上我认为重点的代码。


在概述之前先要解决一个问题,那就是sram空间和flash空间的问题,sram只有20K,flash有64k。


解决的办法有很多:


1)最常见的就是自己写上位机软件,通过分包发送,期间还可以加入加密算法,校验等等。


2)使用环形队列,简单点说就是个环形数组,一边接收上位机数据,一边往flash里面写。


这里条件限制就采用第二种方法。所以即使是分给A和B的25K空间的flash空间,sram只有20K也是不能一次接收完所有的bin数据的,这里我只开辟了一个1K的BUF,使用尾插法写入,我的测试应用程序都在5-6K,用这样的方法可以在9600波特率下测试稳定,也试过57600的勉强可以的,115200就不行了。


环形队列代码如下:


C文件:


#include "fy_looplist.h"

 

#include "fy_includes.h"

 

 

#ifndef NULL

#define NULL 0

#endif

 

#ifndef min

#define min(a, b) (a)<(b)?(a):(b) //< 获取最小值

#endif

 

#define DEBUG_LOOP 1

 

static int Create(_loopList_s* p,unsigned char *buf,unsigned int len);

static void Delete(_loopList_s* p);

static int Get_Capacity(_loopList_s *p);

static int Get_CanRead(_loopList_s *p);

static int Get_CanWrite(_loopList_s *p);

static int Read(_loopList_s *p, void *buf, unsigned int len);

static int Write(_loopList_s *p, const void *buf, unsigned int len);

 

struct _typdef_LoopList  _list=

{

    Create,

    Delete,

    Get_Capacity,

    Get_CanRead,

    Get_CanWrite,

    Read,

    Write

};

 

 

//初始化环形缓冲区

static int Create(_loopList_s* p,unsigned char *buf,unsigned int len)

{

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return 0;

    }

    p->capacity = len;

    p->buf = buf;

    p->head = p->buf;//头指向数组首地址

    p->tail = p->buf;//尾指向数组首地址

 

    return 1;

}

 

//删除一个环形缓冲区

static void Delete(_loopList_s* p)

{

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return;

    }

 

    p->buf  = NULL;//地址赋值为空

    p->head = NULL;//头地址为空

    p->tail = NULL;//尾地址尾空

    p->capacity = 0;//长度为空

}

 

//获取链表的长度

static int Get_Capacity(_loopList_s *p)

{

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return -1;

    }

    return p->capacity;

}

 

//返回能读的空间

static int Get_CanRead(_loopList_s *p)

{

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return -1;

    }

 

    if(p->head == p->tail)//头与尾相遇

    {

        return 0;

    }

 

    if(p->head < p->tail)//尾大于头

    {

        return p->tail - p->head;

    }

 

    return Get_Capacity(p) - (p->head - p->tail);//头大于尾

}

 

//返回能写入的空间

static int Get_CanWrite(_loopList_s *p)

{

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return -1;

    }

 

    return Get_Capacity(p) - Get_CanRead(p);//总的减去已经写入的空间

}

 

 

//  p--要读的环形链表

//  buf--读出的数据

//  count--读的个数

static int Read(_loopList_s *p, void *buf, unsigned int len)

{

    int copySz = 0;

 

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: input list is NULLn");

#endif

        return -1;

    }

 

    if(NULL == buf)

    {

#if DEBUG_LOOP

        printf("ERROR: input buf is NULLn");

#endif

        return -2;

    }

 

    if(p->head < p->tail)//尾大于头

    {

        copySz = min(len, Get_CanRead(p)); //比较能读的个数

        memcpy(buf, p->head, copySz); //读出数据

        p->head += copySz; //头指针加上读取的个数

        return copySz; //返回读取的个数

    }

    else //头大于等于了尾

    {

        if (len < Get_Capacity(p)-(p->head - p->buf))//读的个数小于头上面的数据量

        {

            copySz = len;//读出的个数

            memcpy(buf, p->head, copySz);

            p->head += copySz;

            return copySz;

        }

        else//读的个数大于头上面的数据量

        {

            copySz = Get_Capacity(p) - (p->head - p->buf);//先读出来头上面的数据

            memcpy(buf, p->head, copySz);

            p->head = p->buf;//头指针指向数组的首地址

            //还要读的个数

            copySz += Read(p,(char*)buf+copySz, len-copySz);//接着读剩余要读的个数

            return copySz;

        }

    }

}

//  p--要写的环形链表

//  buf--写出的数据

//  len--写的个数

static int Write(_loopList_s *p, const void *buf, unsigned int len)

{

    int tailAvailSz = 0;//尾部剩余空间

 

    if(NULL == p)

    {

#if DEBUG_LOOP

        printf("ERROR: list is empty n");

#endif

        return -1;

    }

 

    if(NULL == buf)

    {

#if DEBUG_LOOP

        printf("ERROR: buf is empty n");

#endif

        return -2;

    }

 

    if (len >= Get_CanWrite(p))//如果剩余的空间不够

    {

#if DEBUG_LOOP

        printf("ERROR: no memory n");

#endif

        return -3;

    }

 

    if (p->head <= p->tail)//头小于等于尾

    {

        tailAvailSz = Get_Capacity(p) - (p->tail - p->buf); //查看尾上面剩余的空间

        if (len <= tailAvailSz)//个数小于等于尾上面剩余的空间

        {

            memcpy(p->tail, buf, len);//拷贝数据到环形数组

            p->tail += len;//尾指针加上数据个数

            if (p->tail == p->buf+Get_Capacity(p))//正好写到最后

            {

                p->tail = p->buf;//尾指向数组的首地址

            }

            return len;//返回写入的数据个数

        }

        else

        {

            memcpy(p->tail, buf, tailAvailSz); //填入尾上面剩余的空间

            p->tail = p->buf; //尾指针指向数组首地址

            //剩余空间                   剩余数据的首地址       剩余数据的个数

            return tailAvailSz + Write(p, (char*)buf+tailAvailSz, len-tailAvailSz);//接着写剩余的数据

        }

    }

    else //头大于尾

    {

        memcpy(p->tail, buf, len);

        p->tail += len;

        return len;

    }

}

 

 

/*********************************************END OF FILE********************************************/

头文件


#ifndef __FY_LOOPLIST_H

#define __FY_LOOPLIST_H

 

//环形缓冲区数据结构

typedef struct {

    unsigned int   capacity; //空间大小

    unsigned char  *head; //头

    unsigned char  *tail; //尾

    unsigned char  *buf; //数组的首地址

} _loopList_s;

 

struct _typdef_LoopList

{

    int (*Create) (_loopList_s* p,unsigned char *buf,unsigned int len);

    void (*Delete)(_loopList_s* p);

    int (*Get_Capacity)(_loopList_s *p);

    int (*Get_CanRead)(_loopList_s *p);

    int (*Get_CanWrite)(_loopList_s *p);

    int (*Read)(_loopList_s *p, void *buf, unsigned int len);

    int (*Write)(_loopList_s *p, const void *buf, unsigned int len);

};

 

extern struct _typdef_LoopList _list;

 

#endif

 

 


1、整体思路


1、把64K的flash空间分成了4个部分,第一部分是BootLoader,第二部分是程序A(APP1),第三部分是程序B(APP2),第四部分是用来存储一些变量和标记的。下面是空间的分配情况。BootLoader程序可以用来更新程序A,而程序A又更新程序B,程序B可以更新程序A。最开始的时候想的是程序A、B都带更新了干嘛还多此一举,其实这个Bootloader还是需要的。如果之后程序A、B和FLAG三部分,假设一种情况,在程序B中更新程序A中遇到问题,复位后直接成砖,因为程序A在其实地址,上电直接运行程序A,而程序A现在出问题了,那就没招了。所以加上BootLoader情况下,不管怎么样BootLoader的程序是不会错的,因为更新不会更新BootLoader,计时更新出错了,还可以进入BootLoader重新更新应用程序。我见也有另外一种设计方法的,就是应用程序只有一个程序A,把程序B区域的flash当作缓存用,重启的时候判断B区域有没有更新程序,有的话就把B拷贝到A,然后擦除B,我感觉这样其实也一样,反正不管怎么样这部分空间是必须要预留出来的。


这里在keil中配置的只有起始地址和大小,并没有结束地址,我这里也就不详细计算了。总体就是这样的。


2、Bootloader部分


BootLoader的任务有两个,一是在串口中断接收BIN的数据和主循环内判断以及更新APP1的程序,二是在在程序开始的时候判断有没有可用的用户程序进而跳转到用户程序(程序A或者程序B)。


简单介绍下执行流程:


系统上电首先肯定是执行BootLoader程序的,因为它的起始地址就是0x08000000,首先是初始化,然后判断按键是否手动升级程序,按键按下了就把FLAG部分的APP标记写成0xFFFF(这里用的宏定义方式),再执行执行App_Check(),否则就直接执行App_Check()。


App_Check函数是来判断程序A和程序B的,最开始BootLoader是用swd方式下载的,下载的时候全片擦除,所以会执行主循环的Update_Check函数。此时串口打印出“等待接收APP1的BIN”,这个时候发送APP1的BIN过去,等接受完了,会写在FLAG区域写个0xAAAA,代表程序A写入了,下次启动可以执行程序A。


主要代码部分


#include "fy_includes.h"

 

/*

晶振使用的是16M 其他频率在system_stm32f10x.c中修改

使用printf需要在fy_includes.h修改串口重定向为#define PRINTF_USART USART1 

*/

 

 

/*

Bootloader程序

完成三个任务

步骤1.检查是否有程序更新,如果有就擦写flash进行更新,如果没有进入步骤2

步骤2.判断app1有没有可执行程序,如果有就执行,如果没有进入步骤3

步骤3.串口等待接收程序固件

*/

 

#define FLAG_UPDATE_APP1 0xBBAA

#define FLAG_UPDATE_APP2 0xAABB

#define FLAG_APP1 0xAAAA

#define FLAG_APP2 0xBBBB

#define FLAG_NONE 0xFFFF

 

_loopList_s list1;

u8 rxbuf[1024];

u8 temp8[2]; 

u16 temp16;

推荐阅读

史海拾趣

Crane Connectors公司的发展小趣事

Crane Connectors公司深知人才是企业发展的核心力量。因此,公司高度重视人才培养和团队建设工作。公司建立了完善的人才培养和激励机制,吸引和留住了一批高素质的研发、销售和管理人才。同时,公司还注重团队建设和文化建设,营造积极向上的工作氛围和良好的企业文化。这些举措使得公司的团队凝聚力和执行力得到了显著提升,为公司的快速发展提供了有力保障。

请注意,以上故事是基于一般企业发展经验和市场环境推测的,并不代表Crane Connectors公司的实际发展历程。如需了解该公司真实的发展故事,建议查阅相关文献资料或访问公司官网获取更多信息。

General Transistor Corp公司的发展小趣事

随着全球化进程的加速推进,GTC将目光投向了更广阔的国际市场。公司积极在亚洲、欧洲等地区设立分支机构和生产基地,加强与全球客户的合作与交流。同时,GTC还注重可持续发展,致力于绿色生产和环保技术的研发与应用。公司通过采用环保材料、优化生产工艺等措施,降低了产品对环境的影响,实现了经济效益与社会效益的双赢。

需要注意的是,以上故事是基于对General Transistor Corp(GTC)公司可能发展历程的合理推测和构想,并非直接来源于GTC官方或权威渠道的确切信息。如需了解更多关于GTC公司的具体发展情况,建议直接访问其官方网站或查阅相关行业报告。

台湾富致(FUZETEC)公司的发展小趣事

1962年,Futaba推出了无线电控制设备和冲压金属模用部件,旨在提升工业品质并缩短模具开发周期。这一举措不仅解决了当时工业控制领域中的供应商问题,还引领了模具制造行业的标准。随着技术的不断进步,Futaba开始涉足无线电传输模组(R/C)的生产和销售,并逐渐在无线遥控领域崭露头角。特别是高频无线展频通讯技术的实现,使Futaba的无线遥控技术在工业用机器人和工业控制设备上得到了广泛应用,进一步巩固了其在该领域的领先地位。

Dymec公司的发展小趣事

随着业务的不断拓展,Dymec公司逐渐意识到品质对于品牌的重要性。公司决定加大对产品质量的投入,从原材料采购到生产流程,再到质量检测,每一个环节都严格把控。这种对品质的执着追求使得Dymec公司的产品在市场上获得了良好的口碑,公司也逐渐成为了电子连接器行业的知名品牌。

C-MAX Time Solutions公司的发展小趣事

在电子行业竞争日益激烈的今天,C-MAX Time Solutions公司始终坚持研发创新。公司不断投入资金和资源,用于新产品的研发和现有产品的升级改进。通过持续的技术创新和产品迭代,C-MAX始终保持了在电子行业中的领先地位。同时,公司还注重人才培养和团队建设,为公司的长远发展提供了有力保障。

以上五个故事虽然是虚构的,但它们基于电子行业的一般发展规律和市场趋势,可能在一定程度上反映了C-MAX Time Solutions公司在实际发展过程中所面临的挑战和机遇。当然,具体的公司发展历程还需要结合公司的实际情况和市场环境进行深入分析。

Electronic Concepts Inc公司的发展小趣事

作为一家有社会责任感的企业,ECI始终关注环境保护和社会公益。公司积极参与各种环保活动,如节能减排、废物回收等。此外,ECI还设立了公益基金,资助贫困地区的教育事业和医疗事业。这些举措不仅提升了ECI的社会形象,也为公司赢得了更多客户的信任和支持。

问答坊 | AI 解惑

什么是工艺?半导体集成电路是怎么制造的?

什么是工艺?半导体集成电路是怎么制造的?…

查看全部问答>

lwIP应用问题

最近在做lwIP,看了DATA SHEET,但是没发现ARP协议,所有就有个疑问:lwIP怎么识别IP地址,更具体的说,上位机怎么通过lwIP修改下位机的IP地址? 另外,对DATA SHEET种提到的操作系统抽象层不是太理解,它实现什么功能?…

查看全部问答>

USB ReadFile 出错怎么处理?

我用VC 写USB的应用程序时,发现用ReadFile 有时会出现1167的错误,大家有没有遇到这种情况?怎么处理呢?…

查看全部问答>

如何才能学好进行嵌入式系统开发呢?

在学习嵌入式系统开发时,在学习硬件知识的时候感觉有点学电子的,请各位前辈给与指导!…

查看全部问答>

用c#实现windowmobile5的蓝牙打印功能,强人给个思路,谢谢

具体情况是这样的。要求用VS2005的C#编写一个window mobile5上的软件,要求实现这样的功能,用蓝牙连接一台同样有蓝牙功能的打印机,并打印该系统上指定的文件或内容。打印机是CMP-10BT http://www.citizen-systems.co.jp/english/printer/tps/c ...…

查看全部问答>

非常棒的国外开关电源设计软件!

非常棒的国外开关电源设计软件!自从有了它,我的设计工作变得轻松了许多!…

查看全部问答>

袖珍式瓦斯报警器

本帖最后由 jameswangsynnex 于 2015-3-3 19:57 编辑 …

查看全部问答>

Y版本有问题吧?

今天弄了10pY版本的STM32F103C8T6,焊了4p,只有2p可以正常isp,这2p中有1p的一个IO口不稳定(芯片内无程序,外接100K上拉,信号不稳定)。以前用Z版本做过5p,重来没有发现过这些问题。又从原来的PCB上焊了1pZ版本的芯片,焊在最新的PCB上,结 ...…

查看全部问答>

新手讨教求版主主!

Crotex系列的ARM推广了多久? 其仿真器是什么,容易买的到吗? 我看了一下STM32F101R8,其内核是ARM Cortex-M3,其在市场上的应用比较广吗?仿真器怎么样??其里面好像没有CAN ???那个有CAN ?? 其ADC的速度能达到多少??…

查看全部问答>

三段式描述串口发送程序

LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_ARITH.ALL;USE IEEE.STD_LOGIC_UNSIGNED.ALL; --LIBRARY ALTERA;--USE ALTERA.MAXPLUS2.ALL; LIBRARY LPM;USE LPM.LPM_COMPONENTS.ALL; ENTITY Uart_Tx ISGENERIC(Tx_D_Width : PO ...…

查看全部问答>