概述

本教程主要根据官方推荐的教程进行改编,详细信息请参考
OTA Downloader软件包
STM32 通用 Bootloader

本例程通过自己实际搭建环境,测试总结。

bootloader的制作

文末有我已经做好的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是否正常联网:

打包升级程序:

  • 打开目录packagesota_downloader-latesttoolsota_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)的更多相关文章

  1. STM32 IAP 在线升级详解(转)

    源:http://blog.csdn.net/yx_l128125/article/details/12992773 (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP   ...

  2. 【转载】STM32 IAP 在线升级详解

      (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP  再烧写APP应用程序要烧写2次增加工人劳动力基础上写了“STM32 IAP+APP ==>双剑合一”链接稍后 ...

  3. android 在线升级借助开源中国App源码

    android 在线升级借助开源中国App源码 http://www.cnblogs.com/luomingui/p/3949429.html android 在线升级借助开源中国App源码分析如下: ...

  4. STM32 IAP升级

    STM32 IAP在线升级,用Jlink设置读保护后前5K字节是默认加了写保护的,导致IAP升级时擦除和写入FLASH不成功,可以做两个boot,前5k为第一个boot程序,上电时负责跳转到APP还是 ...

  5. C#做的在线升级小程序

    转自原文C#做的在线升级小程序 日前收到一个小任务,要做一个通用的在线升级程序.更新的内容包括一些dll或exe或.配置文件.升级的大致流程是这样的,从服务器获取一个更新的配置文件,经过核对后如有新的 ...

  6. 【WCF】基于WCF的在线升级

    一.前言       前不久因公司产品需要完成了在线升级功能,因为编程技术不精,不敢冒然采用Socket方法实现在线升级,所以使用比较方便稳妥的WCF方式 如果考虑并发能力的话还是Socket> ...

  7. dsp 28377在线升级 实例总结

    使用dsp品台28377d来实现在线升级的功能. 方案 : 升级程序  +  应用程序 升级程序 : 主要的目的是将上位机发送过来的应用程序数据(ccs编译生成的.bin文件)烧写到指定位置,之后在跳 ...

  8. 关于DSP的boot mode / boot loader /上电顺序 /在线升级等问题的总结

    使用器件 ti dsp c2000 2837x 1.dsp的上电过程和boot mode以及boot loader 1)dsp的上电顺序, 对于双核系统而言 , 他的上电启动顺序如下所示: 系统复位或 ...

  9. Encrypting bootloader (程序BIN文件加密及在线升级)

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 在上一个博客随笔,我介 ...

随机推荐

  1. Jmeter4.0之插件安装(三)

    使用Jmeter的实际过程中,需要使用到很多插件,比如json的插件,还有就是做websocket接口测试的时候需要下载websocket的插件 到https://jmeter-plugins.org ...

  2. iOS 静态:动态 Pod

    一.静态和动态 在项目中使用 pod 实现模块化,对于子模块和第三类库的导入方式存在两种:静态库.动态库. 当在 podfile 中指定 use_frameworks! 时,子模块和第三方类库将被打包 ...

  3. Hystrix 使用手册 | 官方文档翻译

    由于时间关系可能还没有翻译全,但重要部分已基本包含 本人水平有限,如有翻译不当,请多多批评指出,我一定会修正,谢谢大家.有关 ObservableHystrixCommand 我有的部分选择性忽略了, ...

  4. Mac OS安装Go语言及配置VSCode开发环境:一个工具(gopls)解千愁

    前言 截止到目前为止,Go语言已经更新到1.14.1,网上的很多教程均已经过时,我在此汇总并整理一下相关的教程,提供一个适合当下的Mac OS教程. 教程中使用了Go在1.11之后推出的依赖包管理工具 ...

  5. yum-程序包管理器前端工具

    一.要想使用yum先要指定yum源 /etc/yum.com /etc/yum.repos.d/*repo 一.yum的使用 yum [option] command  包名 option -y: c ...

  6. 我遇到的一个ClassNotFoundException问题

    近期,使用socket进行进程间Object通信,但是总是报ClassNotFoundException错误. 查找了很多原因,均没有解决. 通过写入文件,查看Object发送的消息内容到底是何种格式 ...

  7. 学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?

    在编程语言中有两个很基础的概念,即方法(method)和函数(function).如果达到了编程初级/入门级水平,那么你肯定在心中已有了初步的答案. 也许在你心中已有答案了 除去入参.返回值.匿名函数 ...

  8. 在vue项目中封装echarts的正确姿势

    为什么需要封装echarts 每个开发者在制作图表时都需要从头到尾书写一遍完整的option配置,十分冗余 在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作 可能有一些开发者忘记 ...

  9. 用人话告诉小白:什么是项目管理(例如Maven),什么是调试工具(即debugger),什么是编译(即compile)

    项目管理 以java程序的项目管理软件Maven为例,java程序根据代码的不同需要不同的jar文件才能编译运行. 人物:两个程序员A和B 物品:一个java程序G,许多jar文件 场景:当A在自己电 ...

  10. echarts以地图形式显示中国疫情情况实现点击省份下钻

    首先要导入对应的包.下钻用到各个省份的json文件等内容导入之后进行相关的操作. 首先是从数据库中读取相应的数据文件.通过list方式.只有在ser出转化为json文件.在jsp页面通过ajax来进行 ...