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)\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   歡迎大家一起來撩。

 

Tags: