概述

本教程主要根据官方推荐的教程进行改编,详细信息请参考
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. 关于getchar的疑惑

    最近做了一道题,我的代码有片段是这样的 while(scanf("%d",&n)) { if(n==0&&getchar()=='\n') break; . ...

  2. 树莓派 Raspberry PI之GPIO

    树莓派 Raspberry PI之GPIO 树莓派各版本硬件原理图:https://www.raspberrypi.org/documentation/hardware/raspberrypi/REA ...

  3. 个人项目:WordCount (Java)

    一.Github项目地址 https://github.com/misterchaos/WordCount 二.解题思路 2.1 基本需求分析 经过仔细阅读题目,分析得出项目的基本需求如下: wc.e ...

  4. 性能优化之三:将Dottrace过程加入持续集成

    之前分享过一篇如何做接口性能分析的文章,但是整个分析过程有点繁琐,需要写一个控制台程序调用被测接口,再预热.启动dottrace追踪,最后才能得到我们想要的性能分析报告.如果有办法一键生成性能分析报告 ...

  5. docker 搭建keepalived+nginx高可用

    前言 最近工作 中 有用到keepalived,就想着 在 本地 搭建一套环境验证一下相关的功能.因为创建虚拟机比较麻烦,就借助  docker来搭建这样 一套 环境 ,顺带学习 巩固下docker的 ...

  6. stm32:#ifndef/#define/#endif使用

    参考:https://blog.csdn.net/abc5382334/article/details/18052757/ 比如:存在a.h文件#include "c.h"而此时b ...

  7. STM32F103ZET6的基本定时器

    1.定时器的分类 STM32F103ZET6总共有8个定时器,它们是:TIM1~TIM8.STM32的定时器分为基本定时器.通用定时器和高等定时器. TIM6.TIM7是基本定时器.基本定时器是只能向 ...

  8. markdown 插入图片太大?怎么设定图片大小?

    你一定在插入图片的时候,遇到图片太大,影响观感的问题. Markdown中,图片大小的设定方式有两种 第一种: ![](https://img2018.cnblogs.com/blog/1735896 ...

  9. Redis 练习(一)

    来自<Redis 实战>第一章 对于 Redis 数据结构类型及使用不太清楚的可以参考 此链接 需求: 网站需要根据文章的发布时间和文章获得的投票数量计算出一个评分,然后按照这个评分来决定 ...

  10. Loop Unrolling 循环展开

    在csapp第五章5.2中提到了循环展开(loop unrolling).这里展开一下为什么循环展开可以提升程序的效率. 以书中计算数组和的两段代码为例: 1.未展开: void psum1(floa ...