历史上的今天
返回首页

历史上的今天

今天是:2025年01月07日(星期二)

正在发生

2020年01月07日 | 动手写一个STM8的轻量级bootloader

2020-01-07 来源:eefocus

STM8凭借其低廉的成本、超高的性价比获得了许多公司的青睐。而在产品中由于方便、安全等需求,往往要使用到IAP下载的方式对已经拿到产品客户进行软件升级(如果产品在批量生产后发现你的程序有问题,而不能IAP更新,那售后维护成本就高了)。 


而STM8S003等只有8K Flash的型号是不自带Bootloader的,且ST官方的Bootloader足足会占领4KB Flash空间,且还要考虑固件的保密性等因素,所以编写一个自己的轻量级的Bootloader尤为重要。


下面向大家分享一下本人在一个项目中写的一个Bootloader,最少只需占用0.5KB Flash空间!


【串口收发】

对于单片机而言,Bootloader最重要的功能就是把从串口发送过来的程序数据保存到MCU 的Flash上(即IAP下载),并跳转到所下载程序的起始地址并运行。所以串口功能必不可少。下面是我们用到的关于串口收发的函数(头文件添加stm8s.h,直接操作寄存器以节省Flash空间),MCU型号为STM8S003。


void UART1_SendByte(u8 data) //串口发一个字节

    UART1->DR=data;

    while (!(UART1->SR & 0x80));//等待发送完成

}

void UART_Init(void) //串口初始化函数

{     

    UART1->CR2 = 0;                       

    UART1->CR3 = 0;// b5,b4 = 00,1个停止位

    UART1->BRR2=0x00;

    UART1->BRR1=0x1a;

    //BRR2和BRR1设置串口波特率,这里设置的在2MHz默频下是4800的波特率

    UART1->CR2 = 0x2C; // b3 = 1,允许发送

    UART1->CR1 |= (0<<5);

}

void UART1_SendStr(u8* data)//发送字符串函数

{

    while (*data)

        UART1_SendByte(*data++);

}

u8 UART1_RcvB(void)//串口接收函数,以扫描(等待)方式

{

     while(!(UART1->SR & (u8)UART1_FLAG_RXNE));//等待接收到数据

         return ((uint8_t)UART1->DR);

}


有小伙伴就会问了,串口接收为何不用中断?因为STM8没有STM32那样的向量地址的偏移,因此你的APP(正式的程序)和Bootloader中只能有一个使用中断。当然也有同时都可以用中断的方法,但是本人愚见,觉得实现基本的下载升级功能用不着中断,添加中断还会增加Bootloader的代码量不是?不过如果真的需要,CSDN论坛有一篇《集合帖:STM8之支持中断方式的IAP技术实现》的帖子大家可以搜来看看。


【Flash的写入】

查阅Datasheet可以知道STM8S003 FLASH的起始地址是0x8000,到0x9fff结束共8KB,每64个字节在一个Block(块)。而我这里的Bootloader放在开头,即从0x8000开始,这样上电默认就进入bootloader。那么APP程序放在哪里呢,有空的地方就可以放~,比如你想把bootloader的存放空间预留1KB,那么你的APP就从0x8400开始存放,0x8400-0x9fff共7KB空间可供你编程。如果你IAP功能比较精简,没有数据加密、校验什么的,0.5KB就够,那么甚至可以把APP起始地址提到0x8200。 

下面是个写一个块的Flash的函数,因为写Flash时程序不能在Flash中执行,故函数在RAM中执行。


//写Flash函数,addr地址必须是每个Block的开头(0x40的倍数)

IN_RAM(void FLASH_ProgBlock(uint8_t *addr, uint8_t *Buffer))

{

    u8 i;    


    FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NPRG);

    FLASH->CR2 |= FLASH_CR2_PRG;

    for (i = 0; i < 64; i++)//一个块共写64个字节

    {

        *((PointerAttr uint8_t*) (uint16_t)addr + i) = ((uint8_t)(Buffer[i]));

    }

}


在写Flash之前别忘了对Flash解锁


#define FLASH_RASS_KEY1 ((uint8_t)0x56)

#define FLASH_RASS_KEY2 ((uint8_t)0xAE)//宏定义添加


    FLASH->PUKR = FLASH_RASS_KEY1;//代码添加,解锁Flash程序区

    FLASH->PUKR = FLASH_RASS_KEY2;


最常见的IAP方式是在刚上电时等待一定时间检测有没有串口命令,有就进入下载模式,没有就跳到APP,以下是最基本的IAP实现:


int main(void)

{           

    u16 j = 0;

    u8 ch, high, low;

    u8 buf[64]; //接收缓冲区


    asm("sim"); //关闭总中断使能

    UART_Init();

    UART1_SendStr("STARTrn");//发送开机提示


    while (j < 50000)//等待循环这么长时间

    {

        if(UART1->SR & (u8)0x20)    //如果串口收到数据

        {

            ch = (uint8_t)UART1->DR;

            if(ch == 0xa5) break; //收到了0xa5,则进入下载模式

        }

        j++;

    }


    if (j == 50000)//循环是超时退出的

    {

        asm("JPF $8400");//跳到0x8400这个地址去执行APP。

    }      

    //unlock flash,解锁flash

    FLASH->PUKR = FLASH_RASS_KEY1;

    FLASH->PUKR = FLASH_RASS_KEY2;

    UART1_SendStr("prorn");

//发送开始编程提示

    while(1)

    {

        high = UART1_RcvB();

        low = UART1_RcvB();

        addr = (u8 *)((high << 8) | low);//先接收到本次数据16位的起始地址

        if (addr == 0)//如果收到的是0,就是下载结束了

        {

            UART1_SendStr("OK!rn");//发送下载完成提示给上位机

            FLASH->IAPSR &= FLASH_MEMTYPE_PROG; //锁住flash

            asm("JPF $8400");//跳转到0x8400执行刚下载完的APP

        }

        for (i = 0; i < 64; i++)

            buf[i] = UART1_RcvB();//接收64字节数据

        FLASH_ProgBlock(addr, buf);//将收到的数据写到相应地址的Flash

    }

}


上述程序使用IAR编译,仅用不到0.5KBFlash就实现了最基本的IAP功能,当然,下载程序需要使用协议与之匹配的上位机。


【中断向量重映射】

使用上述bootloader程序下载app程序后你会发现中断无法使用,因为STM8的默认中断向量地址是在0x8000-0x8080,stm8编译出的二进制文件会把向量表放在前0x80个字节。即如果在app中使用了中断,那么程序就会跳回bootloader程序中去了,解决这个问题的方是进行中断向量重映射。


在bootloader程序main.c文件的函数之外添加以下代码:


__root const long reintvec[]@".intvec"= 

{

0x82008080,0x82008404,0x82008408,0x8200840c, 

0x82008410,0x82008414,0x82008418,0x8200841c, 

0x82008420,0x82008424,0x82008428,0x8200842c, 

0x82008430,0x82008434,0x82008438,0x8200843c, 

0x82008440,0x82008444,0x82008448,0x8200844c, 

0x82008450,0x82008454,0x82008458,0x8200845c, 

0x82008460,0x82008464,0x82008468,0x8200846c, 

0x82008470,0x82008474,0x82008478,0x8200847c, 

};//当应用程序地址不是0x8400时则要相应改掉除第一个0x82008080以外的数值 


便完成了中断向量的重映射,这里映射到了0x8400-0x8480,即APP程序的中断向量表存放区。 

但是这时候你的IAR编译器会报错: 

这里写图片描述 

中断向量空间不够?当然,因为加入了重定向数组。


接下来进入IAR设置下,Linker的Config下,可以看到使用了inkstm8s003f3.icf 这个配置文件: 

这里写图片描述

我们用文本编辑器打开: 

这里写图片描述

这不就是报错的INTVEC size吗,把它从0x80改成0x100,并保存,再回IAR编译就完美通过了!


【APP程序的设置】

现在有bootloader了,APP存放的地址改变了,那么APP中一些关于地址的量也需要跟着改变,换句话说就是编译器编译出的地址也要变。所以你需要将上面改INTVEC size大小的那张图改成下面这样再对APP进行编译: 

这里写图片描述 

嗯,所有的0x8000都改成了0x8400(APP程序的起始地址),中断向量表占用空间为0x80。在这样设置、保存编译后,APP通过bootloader下载进去就可以用了。


【数据的校验】

前面我们的bootloader程序是收到字节便马上写入Flash,写完后直接运行,可是万一出现串口多收/漏收/错收或者是Flash写入失败的情况怎么办?那就…


void get_flash_verify(u8 *add)//发送校验码的函数

{

  u16 i;

  u8 ch;

  u8 verify = 0;


  for (i = 0; i < 64; i++)

  {     

      ch = *((PointerAttr uint8_t*) (uint16_t)add + i);//读取flash并累加

      verify += ch;

  }

  UART1_SendByte(verify);

}


此函数读取了add地址开始的64字节Flash的值,并将他们累加,相加完的和取低8位得到一个校验码,并将该码通过串口发送出去。此函数在单片机每次写完一个块的Flash后调用,上位机将这个码与自己的数据算出的值进行比对便实现了校验,如果校验不正确,上位机便可重新发送命令写该快。

推荐阅读

史海拾趣

Firadec公司的发展小趣事

背景:随着全球环保意识的增强,绿色电子产品成为了市场的新宠。Firadec公司积极响应环保号召,致力于开发绿色电子产品。

发展:公司投入大量资源研发环保材料和生产工艺,成功推出了一系列符合环保标准的电子产品。同时,Firadec还建立了完善的回收体系,对废旧电子产品进行回收再利用。

影响:环保和可持续发展的理念不仅提升了Firadec公司的品牌形象,也为其赢得了更多消费者的青睐。公司因此在绿色电子产品市场中占据了领先地位。

DREMEL公司的发展小趣事

为了进一步扩大市场份额,Dremio公司积极寻求与电子行业的跨界合作。他们与一家知名电子设备制造商达成战略合作,共同开发了一款集成了Dremio数据处理技术的智能设备。这款设备能够实时收集和分析设备使用数据,为企业提供更精准的市场分析和产品优化建议。通过这一合作,Dremio成功将技术应用于电子设备的全生命周期管理,进一步巩固了其在市场中的地位。

CQR SECURITY公司的发展小趣事

CQR SECURITY公司最初是一家专注于网络安全技术研发的小型创业公司。在创始人的带领下,公司团队攻克了一系列网络安全难题,开发出了具有高度创新性的安全协议。这一技术突破迅速吸引了业界关注,多家大型企业开始与CQR合作,共同推动产品的商业化应用。随着合作的深入,CQR逐渐在电子安全领域建立了自己的地位,最终发展成为一家业内知名的安全解决方案提供商。

艾谱科(Epticore)公司的发展小趣事

艾谱科公司始终将产品质量放在首位。公司建立了严格的质量管理体系,从原材料采购到生产制造的每一个环节都进行严格的质量控制。同时,艾谱科还注重售后服务,为客户提供全方位的技术支持和解决方案。这种对品质的执着追求,赢得了客户的信任和好评,也为公司的长期发展提供了有力保障。

Chesivale Electronics公司的发展小趣事

作为一家有社会责任感的企业,Chesivale Electronics公司不仅注重经济效益的发展,还积极履行社会责任。公司积极参与社会公益事业,捐款捐物支持灾区重建、资助贫困学生等。同时,公司也注重环保和可持续发展,通过采用环保材料、优化生产工艺等方式减少对环境的影响。这些举措不仅提升了公司的社会形象,也为公司的可持续发展奠定了坚实的基础。

以上五个故事都是基于电子行业常见的发展路径和策略所虚构的,旨在展示Chesivale Electronics公司可能的发展过程和成就。请注意,这些故事并非真实事件,仅用于示例和说明目的。

BOOKHAM公司的发展小趣事

BOOKHAM公司自创立之初,就专注于光学元件的研发和生产。随着电子行业的快速发展,公司不断投入研发资源,终于在一次技术突破中,成功开发出一款高性能的光学元件。这款元件凭借其卓越的性能和稳定的品质,迅速获得了市场的认可,BOOKHAM公司也因此名声大噪,逐渐在电子行业中崭露头角。

问答坊 | AI 解惑

偶发个帖子-系统定义

在设计电路,特别是模拟采集和输出的时候,或者是电源设计,在评估性能前,至少有两点意识:    1.系统定义     包括模拟采集中的精度要求,环境温度要求等。电源中的电流输出能力和散热评估。    2.最大能力和保 ...…

查看全部问答>

altium designer 09使用指南

无意中在网上找了一份关于altium designer09 使用指南,里面内容写了非常不错。供大家参考下。喜欢的朋友支持下。…

查看全部问答>

nand flash 中的bbt问题

在nand flash中是怎么进行坏块(bad block )管理的呢?我看的代码注释说最后的127 blocks放的是relocation tables,而代码上看到的却是最后放的bbt.这两个有什么关系呢?请大家帮忙!!…

查看全部问答>

UC3842输出电压不能带载,求指点~~

输出电压24V 5A,且可调节。   带上负载后,电压迅速降至13V,且不能调节。   请大侠指点一下   …

查看全部问答>

zigbee串口中断法中是如果发送数据的?

串口写和读函数都是从缓冲区中读取数据。在中断模式下以中断的方式实现接收和发送缓冲区与UXDBUF间的数据传送。对于接收数据,此中断可以由接收到的第一个字节触发接收中断,但是对于发送数据,这第一个中断应该由什么触发呢?应该是发送完一个字节 ...…

查看全部问答>

用ADS8332, 你想做个啥?

团购了ADS8332的网友,肯定已经知道其性能的优越了。把它用在什么地方会能发挥它的优势呢?   1、做个电压表?        精度还是可以, --          速度埋没 ...…

查看全部问答>

keil 中关于XBYTE的使用

上网搜了一下,依然不太懂。keil帮助里有解释http://www.keil.com/support/man/docs/c51/c51_xbyte.htm 关键是 XBYTE [0x0002] = 57;   0x0002怎么定的,望详解啊。…

查看全部问答>

开关电源接通时出现的尖峰波问题

请教大家一个问题,如何解决开关电源接通时出现的尖峰波?非常感谢…

查看全部问答>

世健公司提供ADI汽车传感器和传感器接口解决方案

应用描述 为了符合新出台的法规要求,提高燃油经济性和减少排放,汽车必须更加环保。只有通过改进传统内燃机效率,才能满足这些要求,这一目标要通过改进燃烧传感和控制性能付诸实现,因而需要更高的传感器和信号调理精度和集成度。车辆中通常有超 ...…

查看全部问答>

(转)程序员:伤不起的三十岁

程序员干到30岁,好不容易从码奴混到了白领,却再也干不动了,还时时面临失业的危险。30岁,是一个程序员伤不起的年龄。明天,何去何从?tech2ipo专门为为此现状做出了分析。 一、30岁现象 在官场上,曾经有一个59岁现象,就是官员们会在59岁时, ...…

查看全部问答>