概述
本教程主要根据官方推荐的教程进行改编,详细信息请参考
OTA Downloader软件包
STM32 通用 Bootloader
本例程通过自己实际搭建环境,测试总结。
bootloader的制作
文末有我已经做好的Bootloader文件,可供参考
- 打开bootloader制作的网址Bootloader在线获取地址
- 其他步骤按照STM32 通用 Bootloader制作
- 这里我提供一下我制作的相关配置
烧录Bootloader
- 选择合适的工具烧录BootLoader
- 这里我选择的是J-Flash ARM V4.34(使用的是ST-Link/V2)
- 连接之后下载刚刚生成的Bootloader文件(xxxx.bin)
- 连接串口,测试打印信息
- 能看到我们之前制作Bootloader时,相关的参数以及logo,说明Bootloader烧录成功,如下图所示
- 博主使用的是Xshell软件(建议使用Xshell软件)
- Xhell官网
制作APP程序
使用RT-Thread Studio 添加这些软件包。
代码修改
- 打开fal_cfg.h文件(此过程一定要和Bootloader制作是保持地址对应,否者没法升级)
- 更改app的开始地址
#define RT_APP_PART_ADDR 0x08010000 // app区的开始地址
- 更改分区表
#include <rtconfig.h> #include <board.h> /* ===================== Flash device Configuration ========================= */ extern const struct fal_flash_dev onchip_flash_manager;// 片内 flash 分区管理对象 /* flash device table */ #define FAL_FLASH_DEV_TABLE \ { \ &onchip_flash_manager, \ } /* ====================== Partition Configuration ========================== */ #ifdef FAL_PART_HAS_TABLE_CFG #define FAL_PART_TABLE \ { \ {FAL_PART_MAGIC_WROD, "bl", "onchip_flash_manager", 0, 64 * 1024, 0}, \ {FAL_PART_MAGIC_WROD, "app", "onchip_flash_manager", 64*1024, 320 * 1024, 0}, \ {FAL_PART_MAGIC_WORD, "download", "onchip_flash_manager", 384*1024, 128 * 1024, 0}, \ }
#include <fal.h> /** * fal 读操作 * @param offset 基于分区首地址的偏移量 * @param buf 数据读取后的缓存区 * @param size 要读取的数据个数 * @return */ static int my_read(long offset, uint8_t *buf, size_t size) { uint32_t startAddr; // 起始地址 uint32_t endAddr; // 结束地址 // 首先,要读取数据的首地址的计算公式: // 起始地址 = flash device 起始地址 + flash 分区的偏移地址 + 相对分区偏移地址 // 然后此处传入的 offset,在 fal_partition_read() 中完成了 flash 分区的偏移地址 + 相对分区偏移地址的求和. // 所以此处的 offset = flash 分区的偏移地址 + 相对分区偏移地址 startAddr = onchip_flash_manager.addr + offset; // 结束地址 = startAddr + 要读取的字节长度 endAddr = startAddr + size; if (endAddr > STM32_FLASH_END_ADDRESS) { rt_kprintf("read outrange flash size! addr is (0x%p)\n", endAddr); return -RT_EINVAL; } for (uint32_t i = 0; i < size; i++, buf++, startAddr++) { *buf = *(rt_uint8_t *) startAddr; } return size; } /** * fal 写操作 * @param offset 基于分区首地址的偏移 * @param buf 要写入的数据的缓存 * @param size 要写入的数据长度 * @return */ static int my_write(long offset, const uint8_t *buf, size_t size) { rt_err_t result = RT_EOK; // 返回值 uint32_t startAddr; // 操作起始地址 uint32_t endAddr; // 操作结束地址 startAddr = onchip_flash_manager.addr + offset; endAddr = startAddr + size; // 因为写入时按字节存放,所以起始地址需要 4 的倍数 if (startAddr % 4 != 0) { rt_kprintf("write addr must be 4-byte alignment\n"); return -RT_EINVAL; } if (endAddr > STM32_FLASH_END_ADDRESS) { rt_kprintf("write outrange flash size! addr is (0x%p)\n", endAddr); return -RT_EINVAL; } HAL_FLASH_Unlock(); while (startAddr < endAddr) { if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, startAddr, *((rt_uint32_t *)buf)) == HAL_OK) { if (*(rt_uint32_t *)startAddr != *(rt_uint32_t *)buf) { result = -RT_ERROR; break; } startAddr += 4; buf += 4; } else { result = -RT_ERROR; break; } } HAL_FLASH_Lock(); if (result != RT_EOK) { return result; } return size; } /** * fal 擦操作 * @param offset 基于分区首地址的偏移 * @param size 要擦除的区域大小 * @return */ static int my_erase(long offset, size_t size) { rt_err_t result = RT_EOK; // 返回值 uint32_t startAddr; // 操作起始地址 uint32_t endAddr; // 操作结束地址 FLASH_EraseInitTypeDef EraseInitStruct; // flash 擦除结构体 uint32_t PAGEError = 0; // 错误页 startAddr = onchip_flash_manager.addr + offset; endAddr = startAddr + size; if ((endAddr) > STM32_FLASH_END_ADDRESS) { rt_kprintf("ERROR: erase outrange flash size! addr is (0x%p)\n", endAddr); return -RT_EINVAL; } HAL_FLASH_Unlock(); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.PageAddress = (uint32_t)RT_ALIGN_DOWN(startAddr, FLASH_PAGE_SIZE); EraseInitStruct.NbPages = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE; if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) { result = -RT_ERROR; goto __exit; } __exit: HAL_FLASH_Lock(); if (result != RT_EOK) { return result; } rt_kprintf("erase done: addr (0x%p), size %d\n", startAddr, size); return size; } /** * 片内 flash 分区管理对象 */ const struct fal_flash_dev onchip_flash_manager = { .name = "onchip_flash_manager", // 名称 .addr = 0x08000000, // 首地址 .len = 512 * 1024, // 管理 flash 片区大小 .blk_size = 1 * 1024, // 用于擦除最小粒度的闪存块大小 .ops = {RT_NULL, my_read, my_write, my_erase} }; static void init_fal(void) { fal_init(); } //INIT_APP_EXPORT(init_fal); static void fal_test(void) { // 查找分区 const struct fal_partition* fal_partition_data = fal_partition_find("data"); if(fal_partition_data == NULL) { rt_kprintf("未找到 data 分区"); return; } // 分区擦除 int erase_result = fal_partition_erase(fal_partition_data, 0, 1024); if(erase_result < 0) { rt_kprintf("data 分区擦除失败"); return; } // 分区写入 char data_in[] = {0x01, 0x02, 0x03, 0x04, 0x05}; int write_result = fal_partition_write(fal_partition_data, 0, data_in, 5); if(write_result < 0) { rt_kprintf("data 分区写入失败"); return; } // 分区读出 char data_out[5] = {0}; int read_result = fal_partition_read(fal_partition_data, 0, data_out, 5); if(read_result < 0) { rt_kprintf("data 分区读取失败"); return; } rt_kprintf("0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x\r\n", data_out[0], data_out[1], data_out[2], data_out[3], data_out[4]); } MSH_CMD_EXPORT(fal_test, fal_test);
#include "fal.h" #define APP_VERSION "V1.1.1" #define RT_APP_PART_ADDR 0x08010000 //程序启动运行地址 static int ota_app_vtor_reconfig(void) { #define NVIC_VTOR_MASK 0x3FFFFF80 /* Set the Vector Table base location by user application firmware definition */ SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK; return 0; } INIT_BOARD_EXPORT(ota_app_vtor_reconfig); /* PLEASE DEFINE the LED0 pin for your board, such as: PA5 */ #define LED0_PIN GET_PIN(A, 5) #define key GET_PIN(C, 13) int main(void) { int count = 1; /* set LED0 pin mode to output */ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_mode(key, PIN_MODE_OUTPUT); rt_pin_write(key, 0); rt_thread_mdelay(1000); rt_pin_write(key, 1); fal_init(); LOG_D("version:%s\r\n",APP_VERSION); while (count++) { /* set LED0 pin level to high or low */ rt_pin_write(LED0_PIN, count % 2); //LOG_D("Hello RT-Thread!"); rt_thread_mdelay(1000); } return RT_EOK; }
烧录APP程序的时候一定要注意下载起始地址:0x8010000
查看串口打印数据:
查看网络是MC20是否正常联网:
打包升级程序:
- 打开目录
packages
\ota_downloader-latest
\tools
\ota_packager
- 找到如下所示的生成软件包生成工具,并且打开
- 点击
选择固件
找到主目录下的rtthread.bin
文件 - 添加
固件区名
和固件版本
然后打包 - 成功后会在
rtthread.bin
文件的同一目录下生成rtthread.rbl
文件
- 打开串口输入
help
会打印帮助信息 - 输入
ymodem_ota
执行升级命令
- 在黑窗口点击
鼠标右键
–>传输
–>YMODEM(Y)
- 选择刚刚生成的
rtthread.rbl
文件,打开进行升级,如下图所示
- 成功之后,会看到版本变化了,说明升级成功,如下图所示
-
然后串口输入http_ota
需要NGINX搭建个web服务器 访问url地址可以自动下载打包好的文件
由于flash 太小没有通过http升级成功。
小结
在线升级很多地方都能够用到,能够对产品的缺陷及时进行修复,当然这需要更大的Flash硬件资源,需要测试demo的可以QQ联系我
我的QQ:319438908 欢迎大家一起来撩。