zoukankan      html  css  js  c++  java
  • RT-Thread—STM32—在线升级(Ymodem_OTA、HTTP_OTA)

    概述

    本教程主要根据官方推荐的教程进行改编,详细信息请参考
    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)
    ", 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
    ");
            return -RT_EINVAL;
        }
    
        if (endAddr > STM32_FLASH_END_ADDRESS)
        {
            rt_kprintf("write outrange flash size! addr is (0x%p)
    ", 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)
    ", 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
    ", 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
    ",
                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
    ",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   欢迎大家一起来撩。

  • 相关阅读:
    容斥原理解决某个区间[1,n]闭区间与m互质数数量问题
    Educational Codeforces Round 28
    括号匹配问题(区间dp)
    小球装箱问题八连(组合数学)
    Educational Codeforces Round 29
    Codeforces Round #437 (Div. 2, based on MemSQL Start[c]UP 3.0
    Codeforces Round #434 (Div. 2, based on Technocup 2018 Elimination Round 1)
    Opencv保存读取float类型深度图
    OpenGL快速入门
    使用selenium判断标签的元素值是否存在
  • 原文地址:https://www.cnblogs.com/wt88/p/12779442.html
Copyright © 2011-2022 走看看