本教程主要根据官方推荐的教程进行改编,详细信息请参考
OTA Downloader软件包
STM32 通用 Bootloader
本例程通过自己实际搭建环境,测试总结。




使用RT-Thread Studio 添加这些软件包。

#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 欢迎大家一起来撩。
RT-Thread—STM32—在线升级(Ymodem_OTA、HTTP_OTA)
原文:https://www.cnblogs.com/wt88/p/12779442.html