概述

本教程主要根据官方推荐的教程进行改编,详细信息请参考
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. JavaScript之onclick事件

    对于给同一个元素添加两个点击事件时,其中一个是通过js获取元素添加点击事件另一个是通过内联的方法为元素添加事件. 执行之后只会执行通过元素获取的点击事件.而内联式的添加点击事件是不会执行的 还有一个就 ...

  2. Python python对象 deque

    # deque对象 ''' class collections.deque([ iterable [,maxlen ] ] ) 返回一个从左到右(使用append())初始化的新deque对象,其中包 ...

  3. Unix 网络编程卷一源码编译踩坑记录 ubtutu 19.10

    在阅读unpv1时运行源代码的环境配置,这里简单记录一下 源代码里的README 写得挺详细的,但是在Linux 系统的下还没没办法直接编译通过的, 这里我使用的是ubuntu 19.10(在腾讯云1 ...

  4. JSP学习笔记(二)

    JSP内置对象 request对象 response对象 session对象 application对象 out对象 有些对象不用声明就可以在JSP页面的Java程序片和表达式部分使用,这就是JSP的 ...

  5. SpringMVC里 form提交到controller404 解决方法

    把 <form action="/logon"> 改为 <form action="${pageContext.request.contextPath} ...

  6. 2019ICPC(银川) - Delivery Route(强连通分量 + 拓扑排序 + dijkstra)

    Delivery Route 题目:有n个派送点,x条双向边,y条单向边,出发点是s,双向边的权值均为正,单向边的权值可以为负数,对于单向边给出了一个限制:如果u->v成立,则v->u一定 ...

  7. [教程]KALI LINUX 2.0 2019 更新国内源

    2019年最新版本KALI 为 KALI 2019.1 下载地址:https://www.kali.org/downloads/ 有的新入门的朋友可能会问,为什么每次都无法手动更新 例如:Update ...

  8. 使用vant的时候,报错:component has been registered but not used以及vant的使用方法总结

    使用vant的时候,报错:component has been registered but not used以及vant的使用方法总结 在使用vant的时候. 想按需引入,于是安装了babel-pl ...

  9. PTA数据结构与算法题目集(中文) 7-20

    PTA数据结构与算法题目集(中文)  7-20 7-20 表达式转换 (25 分)   算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个 ...

  10. Golang入门(3):一天学完GO的进阶语法

    摘要 在上一篇文章中,我们聊了聊Golang中的一些基础的语法,如变量的定义.条件语句.循环语句等等.他们和其他语言很相似,我们只需要看一看它们之间的区别,就差不多可以掌握了,所以作者称它们为&quo ...