IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【嵌入式】W801的OTA方案设计 -> 正文阅读

[嵌入式]【嵌入式】W801的OTA方案设计

系统分区表

W801是平头哥内核的WIFI芯片。给到的SDK里面没有找到中断向量表重定向的函数,类似于STM32的NVIC_SetVectorTable,所以中断向量表只能给主程序用,那么BootLoader就没法弄了。干脆不用BootLoader了,直接划一个分区用来存放Updater代码,用于解压或差分计算新固件并复制到主分区(存在变砖的可能性)。

国产芯片就是资料不全,规格书里面写了Flash的寻址空间为0x0800 0000 ~ 0x0FFF FFFF,但是在SDK里面,链接脚本是从0x080d0400开始的,0x080d0400是在tools\w800\wconfig文件里面W800_RUN_ADDRESS选项定义的默认值:

但是把W800_RUN_ADDRESS改成0x08000000之后无法运行,而且下载的时候发现芯片的MAC地址值还被冲刷了,把W800_RUN_ADDRESS改成大于0x080d0400的地址值是可以运行的,可能是前面的一些空间是用作其它用途的(后面发现擦写Flash的时候,从0x08000000开始计算2MB之后的地址是没法写入的,代码里面做了地址范围判断)。所以这里只好按照0x080d0400之后的地址值作为Flash的起始地址开始分区,由于芯片内部Flash的最小擦除单位是sector,一个sector为4KB,为了方便擦写,取一个最近的4KB对齐地址0x080D1000作为起始地址,同时每个分区的大小也设置为4KB的整数倍。2MB Flash的有效范围是0x08000000到0x08200000,那么定义用户代码的有效地址范围为0x080D1000~0x08200000,一共1212KB。

分区表设计如下图:

?最开始的800KB必须作为主程序Main APP,因为中断向量表固定在这个位置。紧跟64KB为Updater程序。Updater后面的32KB的Sub APP用于其它用途,然后是4KB存放OTA参数。最后312KB存放下载的OTA固件包(全量包、全量压缩包或差分包)。

OTA工作流程

Updater工程

Updater软件为单独的一个工程,直接拷贝原来的SDK软件,修改连接脚本:

......

MEMORY
{
	I-SRAM : ORIGIN = 0x08199000 , LENGTH = 0x10000     /* I-SRAM  100KB */ 
	D-SRAM : ORIGIN = 0x20000100 , LENGTH = 0x47f00     /* D-SRAM  288KB */
	V-SRAM : ORIGIN = 0x20000000 , LENGTH = 0x100       /* off-chip SRAM 8MB */
}


......

I-SRAM是指令存储器,把它的起始地址修改成Updater分区的起始地址0x08199000,LENGTH为64KB。把wm_main.c里面的main函数直接改成:

int main(void)
{
    u32 value = 0;
    /*32K switch to use RC circuit & calibration*/
    tls_pmu_clk_select(0);

    /*Switch to DBG*/
    value = tls_reg_read32(HR_PMU_BK_REG);
    value &= ~(BIT(19));
    tls_reg_write32(HR_PMU_BK_REG, value);
    value = tls_reg_read32(HR_PMU_PS_CR);
    value &= ~(BIT(5));
    tls_reg_write32(HR_PMU_PS_CR, value);

    /*Close those not initialized clk except uart0,sdadc,gpio,rfcfg*/
    value = tls_reg_read32(HR_CLK_BASE_ADDR);
    value &= ~0x3fffff;
    value |= 0x1a02;
    tls_reg_write32(HR_CLK_BASE_ADDR, value);

    void disp_version_info(void);
    disp_version_info();

    tls_sys_clk_set(CPU_CLK_80M);
    UserMain(); // for updater proj, OS is not required, directly jump to UserMain

    while(1);
    return 0;
}

因为Updater不需要运行操作系统,只需要做一些解压、差分运算以及读取、擦除和写入数据到flash。另外没有在main函数里面添加Updater的核心代码,而是调用的UserMain函数,是因为编译系统将wm_main.c和其它系统层代码编译成SDK库,最后和app文件夹里面的应用层代码链接成可执行文件的,并且编译库make lib速度较慢,而Usermain函数是属于应用层的代码,直接编译更方便。

Main APP工程

由于官方没有给很好的Flash烧写工具,每次make flash或者make down好像都只能从固定地址处开始下载,没法下载文件到指定的地址,所以把Updater软件的可执行bin文件作为常量数组先放到Main APP的工程中,并指定该数组的存放段为 .updater_bin,下载Main APP的时候连带Updater一起下载进去(也可以在Main APP里面添加一个烧写功能,使用X-modem之类的通信协议下载数据到指定位置)。

__attribute__((section(".updater_bin"))) const uint8_t updater_bin[1024*64] = 
 {
	0x00, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 
	0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 
	0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 
	0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 0xD4, 0x85, 0x19, 0x08, 

......

修改Main APP的链接脚本:

......

MEMORY
{
	I-SRAM : ORIGIN = 0x080D1000 , LENGTH = 0x120000 /* I-SRAM  1M+128KB */ 
	D-SRAM : ORIGIN = 0x20000100 , LENGTH = 0x47f00   /* D-SRAM  288KB */
	V-SRAM : ORIGIN = 0x20000000 , LENGTH = 0x100   /* off-chip SRAM 8MB */
	UPDATER : ORIGIN = 0x08199000 , LENGTH = 0x10000   /* updater */
}

......

 .updater_bin :
 {
    KEEP(*main.o(.updater_bin))
 } > UPDATER

......

链接脚本里面添加一个存储器UPDATER,起始地址为Updater分区的起始地址,大小64KB。再添加一个updater_bin段,Updater的bin文件数组就放在这个段里面。这样Main APP生成的bin文件大小就变成864KB了(800KB + 64KB),下载较慢。

这样在Main APP中就可以正常跳转到Updater中去了(注意,可执行文件的前256个字节为中断向量表,第一个中断向量为复位中断,所以向量表的第一个字为reset handler的地址):

#define OTA_PARAM_START_ADDRESS         0x081B1000
#define OTA_PARAM_LENGTH                (4 * 1024)

#define OTA_PACKAGE_START_ADDRESS       0x081B2000
#define OTA_PACKAGE_LENGTH              (312 * 1024)

#define OTA_UPDATER_START_ADDRESS       0x08199000
#define OTA_UPDATER_LENGTH              (64 * 1024)

#define OTA_SUBAPP_START_ADDRESS       	0x081B1400
#define OTA_SUBAPP_LENGTH              	(32 * 1024)

#define OTA_MAINAPP_START_ADDRESS      	0x080D1000
#define OTA_MAINAPP_LENGTH             	(800 * 1024)

__attribute__((section(".updater_bin"))) const uint8_t updater_bin[1024*64] = 
 {
    ......
};

void UserMain(void)
{
    uint32 *updater_start_addr = (uint32 *)OTA_UPDATER_START_ADDRESS;
    uint32 updater_reset_handler_addr = *updater_start_addr;
    LOGI("updater_bin addr:%.8X\n", (uint32)updater_bin);
    LOGI("updater_bin:%.2X %.2X %.2X %.2X\n", updater_bin[0], updater_bin[1], updater_bin[2], updater_bin[3]);
    LOGI("updater_start_addr:%.8X\n", (uint32)updater_start_addr);
    LOGI("updater_reset_handler_addr:%.8X\n", (uint32)updater_reset_handler_addr);
    void (*reset_handler)(void) = (void (*)(void))updater_reset_handler_addr;
    reset_handler();
    while (1) {
        tls_os_time_delay(5 * HZ);
    }
}

附:后面发现,Updater工程中设置了W800_RUN_ADDRESS为0x08199000之后,下载是从0x08199000处开始下载,这样Main APP下载就不会影响Updater代码了,两个工程可以独立下载不干扰,所以不需要再把Updater文件作为常量数组放在Main APP里面了。但是要注意,代码的执行地址也会变成W800_RUN_ADDRESS(可能0x08000000前面的一段空间是一个BootLoader,下载的时候把W800_RUN_ADDRESS传给它了,所以BootLoader启动应用程序的时候会跳转到W800_RUN_ADDRESS处运行并且设置了中断向量表地址也为W800_RUN_ADDRESS),所以单片机复位之后是会运行最后下载的代码的,那么需要先下载Updater再下载Main APP才行。

简单的HTTP服务器

创建本地HTTP服务器用于下载OTA固件包。参考链接:快速搭建一个简易的HTTP服务器用于文件分享与下载 - 灰信网(软件开发博客聚合)icon-default.png?t=M4ADhttps://www.freesion.com/article/2710660021/

1、使用Python脚本创建http server:

import http.server
import socketserver
 
PORT = 80
 
Handler = http.server.SimpleHTTPRequestHandler
 
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

2、使用小工具软件MyWebServer,也很方便:

?

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-12 16:35:35  更:2022-05-12 16:35:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 11:42:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码