Bootloader实现升级程序

使用CAN总线来更新程序,用Bootloader实现。其实就是通过上位机把.bin/hex文件以CAN通讯的方式发送给单片机并存储在规定的Flash中。这个过程与手机端更新APP类似。

图片

以STM8单片机为例,该如何实现Bootloader呢?今天和大家分享一下。

01、什么是Bootloader

Bootloader是一段用于更新自身应用软件并独立运行的代码,常被用于升级产品和修复产品bug。STM8单片机如果要下载hex文件的话需要通过STVP和STLINK来实现,单片机的引脚SWIM就是下载接口。如果产品在用户端被封装好了,无法通过SWIM来下载该怎么办呢?就使用Bootloader,STM支持CAN和UART实现Bootloader。就是通过CAN通信或者UART通信将数据存放在STM8的Flash上。

02、划分Flash区间

STM8的中断向量跳转地址是固定的,会跳转到0x8000对应中断的偏移地址,所以会把Bootloader存放在0x8000开始的空间内,比如给Bootloader代码所划分的空间为4k,那么空间起始地址为:0x8000-0x8FFF;那么应用代码的起始地址就可以从0x9000开始。

03、修改中断向量表

STM8的中断向量表通过一段特定的代码来实现,并需要根据前文提到的空间划分来修改。STM8的应用程序起始地址是0x8400,默认的向量表如下代码所示。

__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, };

前文已经将应用程序的起始地址修改为0x9000,所以,向量表的的修改方法为:除了第一个元素为,其余的元素将8改为9,修改完后代码如下:

__root const long reintvec[]@".intvec"={   0x82008080,0x82009404,0x82009408,0x8200940c, 0x82009010,0x82009014,0x82009018,0x8200901c,0x82009020,0x82009024,0x82009028,0x8200902c,0x82009030,0x82009034,0x82009038,0x8200903c,0x82009040,0x82009044,0x82009048,0x8200904c,0x82009050,0x82009054,0x82009058,0x8200905c,0x82009060,0x82009064,0x82009068,0x8200906c,0x82009070,0x82009074,0x82009078,0x8200907c,};       

04、修改ICF文件

ICF文件存放在编程环境的安装目录下,每个型号/系列的单片机都会对应一个ICF

文件,需要修改一下ICF文件,这里需要根据flash空间的划分来修改,前面将Bootloader的终止地址设置为0x9FFF,所以修改如下:

define region NearFuncCode = [from 0x8000 to 0x8FFF];define block INTVEC with size = 0x80 { ro section .intvec };place at start of NearFuncCode  { block INTVEC };

图片

05、跳转命令

所谓跳转,就是在应用程序中跳转到Boot中去升级;升级完成后需要跳转到应用程序中,所跳转的起始就是起始地址。Bootloader跳转到应用程序的代码如下,

asm("LDW X,  SP ");asm("LD  A,  $FF");asm("LD  XL, A  ");asm("LDW SP, X  ");asm("JPF $9000");

应用程序跳转到Bootloader中的代码如下

sm("LDW X,  SP ");asm("LD  A,  $FF");asm("LD  XL, A  ");asm("LDW SP, X  ");asm("JPF $8000");

06、编写交互代码

以上设置好之后,就可以编写交互代码了,所以交互代码其实就是一套协议,规定了应用程序如何跳转到Boot,在Boot中如何处理和存储数据,这需要上位机的支持。如果只是用于自家的产品,就可以自己定义协议。也可以使用标准的协议,比如汽车行业中的UDS(统一诊断服务)

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是一个简单的基于STM32F103的CAN Bootloader升级程序代码,仅供参考: ``` // 定义 CAN Bootloader 升级命令 #define CAN_CMD_BOOTLOADER_START 0x01 #define CAN_CMD_BOOTLOADER_DATA 0x02 #define CAN_CMD_BOOTLOADER_END 0x03 // 定义 CAN Bootloader 升级状态 #define CAN_BOOTLOADER_STATE_IDLE 0 #define CAN_BOOTLOADER_STATE_READY 1 #define CAN_BOOTLOADER_STATE_TRANSFER 2 #define CAN_BOOTLOADER_STATE_COMPLETE 3 // 定义 CAN Bootloader 升级数据包大小 #define CAN_BOOTLOADER_DATA_SIZE 8 // 定义 CAN Bootloader 升级数据结构 typedef struct { uint8_t cmd; uint32_t address; uint32_t length; uint8_t data[CAN_BOOTLOADER_DATA_SIZE]; } can_bootloader_packet_t; // 定义 CAN Bootloader 升级状态变量 volatile uint8_t can_bootloader_state = CAN_BOOTLOADER_STATE_IDLE; // 定义 CAN Bootloader 升级数据缓冲区 volatile can_bootloader_packet_t can_bootloader_packet; // 处理 CAN Bootloader 升级命令 void can_bootloader_handle_command(uint8_t cmd, uint32_t address, uint32_t length) { switch(cmd) { case CAN_CMD_BOOTLOADER_START: // 处理 Bootloader 启动命令 // 初始化升级状态变量和数据缓冲区 can_bootloader_state = CAN_BOOTLOADER_STATE_READY; can_bootloader_packet.address = address; can_bootloader_packet.length = length; break; case CAN_CMD_BOOTLOADER_DATA: // 处理 Bootloader 数据传输命令 if(can_bootloader_state == CAN_BOOTLOADER_STATE_TRANSFER) { // 将数据写入指定地址 uint8_t* address_ptr = (uint8_t*) address; for(int i = 0; i < CAN_BOOTLOADER_DATA_SIZE && length > 0; i++, address_ptr++, length--) { *address_ptr = can_bootloader_packet.data[i]; } // 如果数据传输完成,则修改升级状态 if(length == 0) { can_bootloader_state = CAN_BOOTLOADER_STATE_COMPLETE; } } break; case CAN_CMD_BOOTLOADER_END: // 处理 Bootloader 结束命令 // 重置升级状态变量和数据缓冲区 can_bootloader_state = CAN_BOOTLOADER_STATE_IDLE; memset(&can_bootloader_packet, 0, sizeof(can_bootloader_packet_t)); break; default: // 未知命令 break; } } // 处理 CAN Bootloader 升级数据 void can_bootloader_handle_data(uint8_t* data, uint8_t length) { if(can_bootloader_state == CAN_BOOTLOADER_STATE_READY) { // 处理 Bootloader 启动命令 memcpy(can_bootloader_packet.data, data, length); can_bootloader_state = CAN_BOOTLOADER_STATE_TRANSFER; } else if(can_bootloader_state == CAN_BOOTLOADER_STATE_TRANSFER) { // 处理 Bootloader 数据传输命令 if(length <= CAN_BOOTLOADER_DATA_SIZE) { memcpy(can_bootloader_packet.data, data, length); } } } // 处理接收到的 CAN 消息 void can_receive_handler(can_message_t* msg) { if(msg->id == CAN_BOOTLOADER_ID) { // 处理 CAN Bootloader 升级命令 can_bootloader_handle_command(msg->data[0], *(uint32_t*)(msg->data + 1), *(uint32_t*)(msg->data + 5)); } else if(msg->id == CAN_BOOTLOADER_DATA_ID) { // 处理 CAN Bootloader 升级数据 can_bootloader_handle_data(msg->data, msg->length); } } int main() { // 初始化 CAN 总线和中断 can_init(); can_set_receive_handler(can_receive_handler); while(1) { // 主循环 } } ``` 注意:该代码仅供参考,具体实现可能会因为硬件和软件环境等因素而有所不同。此外,正确的CAN Bootloader升级操作需要谨慎处理,避免出现意外情况导致设备无法正常工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值