返回列表 发新帖我要提问本帖赏金: 80.00元(功能说明)

[APM32E1] 用Xmodem协议实现APM32的固件升级

[复制链接]
3927|4
 楼主| shanyuxiang 发表于 2024-4-22 23:58 | 显示全部楼层 |阅读模式
本帖最后由 shanyuxiang 于 2024-4-27 18:27 编辑

#申请原创# @21小跑堂

用Xmodem协议实现APM32的固件升级


一、前言
1.1、关于Xmodem协议
Xmodem是一个经典的串口文件输协议,发送端发送大小为128字节或1024字节的包,每一包都带有CRC校验,接收端根据接收到的包返回一个字节的控制字。
由发送端发出的控制字:
1.png

由接收端发出的控制字:

2.png
简单地说说Xmodem的通信流程:
接收端(MCU)运行后间每隔大约1s发送一个字节的0x43('C'),发送端(PC上位机)收到0x43后发送第一包,
接收端收到这一帧并且CRC校验正确后返回ACK,然后发送端才会会发下一包;如果CRC不对返回的是NAK,则发送端重传该包。
当所有数据包传输完成后,发送端发送0x04(EOT)表示文件已经全部传完。
网上关于Xmodem资料很多,有兴趣的话可以找来看看,这里就不展开了。

Xmodem这种具有校验并重传的协议非常适合用来做固件的传输,有固定的包大小也便于flash的扇区写入。
这里就用一个Xmodem的开源协议栈代码来实现APM32的固件下载和升级。

1.2、准备工作
xmodem的协议栈代码:
Xmodem function source code
https://www.menie.org/georges/embedded/#xmodem
上位机软件:
支持xmodem协议的文件发送软件或串口助手皆可,例如SecureCRT

SDK工程:
APM32E10x_SDK_V1.1

二、Xmodem协议栈的移植
2.1 源码文件的添加
首先添加下载下来的开源代码 xmodem.c  、crc16.c,同时新建一个 xmodem_port.c 存放待补充的代码。
3.png

2.2 补充待实现的代码
xmodem.c中还需要实现这几个函数,usleep、_inbyte、_outbyte。
usleep是一个微秒级延时,时间不用太精准,能实现等待就可以,这里滴答定时做一个:
  1. void usleep(unsigned int us)
  2. {
  3.     unsigned int temp;
  4.     SysTick->LOAD = us * (SystemCoreClock / 1000000 / 8); //时间加载
  5.     SysTick->VAL = 0x00;
  6.     SysTick->CTRL = 0x01 ;
  7.     do
  8.     {
  9.         temp = SysTick->CTRL;
  10.     }
  11.     while (temp & 0x01 && !(temp & (1 << 16)));
  12.     SysTick->CTRL = 0x00;
  13.     SysTick->VAL = 0X00;
  14. }

_inbyte是字节的读取,有点类似文件的读取,用串口的轮询接收来实现:
  1. unsigned short uart_read()
  2. {
  3.     unsigned short data = 0;
  4.     if (USART_ReadStatusFlag(XMODEM_UART, USART_FLAG_RXBNE) != RESET)
  5.     {
  6.         data = USART_RxData(XMODEM_UART);
  7.         data |= 0x2000;
  8.     }

  9.     return data;
  10. }

  11. int _inbyte(unsigned short timeout) // msec timeout
  12. {
  13.     unsigned short c;
  14.     int delay = timeout << 4;

  15.     while (((c = uart_read()) & 0x2000) == 0)
  16.     {
  17.         usleep(5);//usleep(60); /* 60 us * 16 = 960 us (~ 1 ms) */
  18.         if (timeout)
  19.         {
  20.             if (--delay == 0) return -2;
  21.         }
  22.     }
  23.     return c & 0x0FF;
  24. }

_outbyte 是字节发送,这个好办,用串口的字节发送实现:
  1. void _outbyte(int  byte)
  2. {
  3.     while (USART_ReadStatusFlag(XMODEM_UART, USART_FLAG_TXBE) == RESET);
  4.     USART_TxData(XMODEM_UART, byte);
  5. }


2.3 Xmodem的调用
注释掉xmodem.c中宏定义 "TEST_XMODEM_RECEIVE",并给其中的main()函数改个名,改成xmodem_main()。
在真正的main函数循环中执行xmodem_main(),让协议栈一直运行,最后不要忘了初始化一下xmodem要用到的串口。
  1.     xmodem_init(); //初始化Xmodem所需要的串口

  2.     while (1)
  3.     {
  4.         xmodem_main();
  5.     }

至此,Xmodem的移植就完成了,接下来就来实现固件的烧写。

三、固件的烧录
3.1 内部flash的擦写
具体操作可以参考FMC例程,简单的说就是解锁、擦除、编程...擦除、编程、上锁。
因为这个boot要占用4KB多的空间,所以APP就从8KB的位置开始,这也就为什么烧写的起始地址是0x08002000;
该芯片的最小擦除单位是2KB,所以每次就按2KB来写入。
  1. #define FLASH_PAGE_SIZE    2048
  2. #define FLASH_BOOT_ADDR    0x08002000
  3. void xmodem_write_flash(unsigned char buf[], unsigned int len)
  4. {
  5.     unsigned int sectors, i, j;
  6.     unsigned int word;
  7.     sectors = (len + 2047) / 2048;

  8.     FMC_Unlock();

  9.     for (i = 0; i < sectors; i++)
  10.     {
  11.         FMC_ErasePage(FLASH_BOOT_ADDR + (FLASH_PAGE_SIZE * i));

  12.         for (j = 0; j < FLASH_PAGE_SIZE / 4; j++)
  13.         {
  14.             word = buf[FLASH_PAGE_SIZE * i + j * 4 + 0];
  15.             word |= buf[FLASH_PAGE_SIZE * i + j * 4 + 1] << 8;
  16.             word |= buf[FLASH_PAGE_SIZE * i + j * 4 + 2] << 16;
  17.             word |= buf[FLASH_PAGE_SIZE * i + j * 4 + 3] << 24;

  18.             FMC_ProgramWord(FLASH_BOOT_ADDR + FLASH_PAGE_SIZE * i + j * 4, word);
  19.         }
  20.     }

  21.     FMC_Lock();
  22. }


3.2 APP的接收与写入
先建一个缓存用来存放接收固件文件,数组地址传给 xmodemReceive 函数,当接收完成后,调用刚才写好 xmodem_write_flash 函数对固件进行烧写。
  1. int xmodem_main(void)
  2. {
  3.     int st;

  4.     printf("Send data using the xmodem protocol from your terminal emulator now...\r\n");
  5.     /* the following should be changed for your environment:
  6.        0x30000 is the download address,
  7.        65536 is the maximum size to be written at this address
  8.      */
  9.     //st = xmodemReceive((char *)0x30000, 65536);
  10.     st = xmodemReceive((char *)FirmwareBuffer, 65536);
  11.     if (st < 0)
  12.     {
  13.         printf("Xmodem receive error: status: %d\r\n", st);
  14.     }
  15.     else
  16.     {
  17.         printf("Xmodem successfully received %d bytes\r\n", st);

  18.         xmodem_write_flash(FirmwareBuffer, st);


  19.     }

  20.     return 0;
  21. }


3.3 跳转到APP执行
固件烧写完后就可以跳转了,常规流程,先判断一下APP的头,如果正确就跳转到APP。
  1. typedef void (*pFunction)(void);
  2. pFunction JumpToApplication;
  3. unsigned int  JumpAddresss;

  4. void xmodem_jump(void)
  5. {
  6.     if (((*(__IO uint32_t *)FLASH_BOOT_ADDR) & 0x2FFE0000) == 0x20000000)
  7.     {
  8.         printf("[boot]APM32 xmodem jump\r\n\r\n");

  9.         JumpAddresss = *(__IO uint32_t *)(FLASH_BOOT_ADDR + 4);
  10.         JumpToApplication = (pFunction)JumpAddresss;
  11.         __set_MSP(*(__IO uint32_t *)FLASH_BOOT_ADDR);
  12.         JumpToApplication();

  13.     }
  14.     else
  15.     {
  16.         printf("[boot]APM32 invalid app!\r\n\r\n");
  17.     }
  18.     printf("[boot]APM32 xmodem jump fail!\r\n");
  19. }


四、固件升级的测试验证
4.1 制作测试用的APP
bootloader代码已经完成,为了测试固件升级功能,还需要做一个app固件。相对于bootloader来说,app部分就比较简单了。
因为APP是打算放在 0x08002000 的位置,所以需要让编译出的APP代码的在 0x08002000 基础上进行偏移,在工程设置中改一下编译的地址:
4.png

另外 system_apm32e10x.c 中的向量偏移也改一下:
  1. #define VECT_TAB_OFFSET     0x2000//0x00


APP中加点串口打印代码便于观察执行情况:
  1.     printf("\r\n\r\n[app]APM32 Xmodem appliaction star...\r\n");
  2.     while (1)
  3.     {
  4.         Delay();

  5.         printf("[app]%03d\r\n", LoopCount++);
  6.     }
为了得到APP的bin文件,在工程设置选项中添加这条命令并编译:
fromelf.exe --bin -o "$L@L.bin" "#L
5.png

4.2 用 SecureCRT 发送固件文件
下面是见证奇迹的时刻,先运行bootloader程序,打开 SecureCRT 软件,协议选择"Serial",设置正确的串口号和波特率,点下面的"Connect"按钮:
6.png

串口打开后会看到MCU一直在发送字符'C':

8.png

这时拖动刚才编译好的bin文件,选择"Send Xmodem...":
7.png

等待进度完成,程序跳转,看到App程序执行并打印出串口信息,至此固件成功升级。
9.png


不足与改进:
这里的xmodem是接收完整个固件才进行烧录操作,当app固件非常大时需要占用很大的sram空间;
理论上来说xmodem是支持边收边烧写的,比如收够2KB字节就写入到flash中,这样适用面会更广。








      

APM32E10x_SDK_V1.1_xmode_boot.rar

275.18 KB, 阅读权限: 10, 下载次数: 35

源码工程

打赏榜单

21小跑堂 打赏了 80.00 元 2024-04-28
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

在APM32上使用Xmodem协议通过串口升级固件,实现步骤详细,代码简介清晰,实现效果较好,不足之处也有举出,期待后续完善实验。  发表于 2024-4-28 16:15
xiaoqi976633690 发表于 2024-4-28 17:57 | 显示全部楼层
学习了
WoodData 发表于 2024-4-28 23:17 | 显示全部楼层
学习了
WoodData 发表于 2024-4-28 23:17 | 显示全部楼层
学习了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

50

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部