【嵌入式】W801 OTA方案设计

发布者:清新家园最新更新时间:2022-08-08 来源: csdn关键字:嵌入式 手机看文章 扫描二维码
随时随地手机看文章

系统分区表

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


国产芯片就是资料不全,规格书里面写了Flash的寻址空间为0x0800 0000 ~ 0x0FFF FFFF,但是在SDK里面,链接脚本是从0x080d0400开始的,0x080d0400是在toolsw800wconfig文件里面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:%.8Xn", (uint32)updater_bin);

    LOGI("updater_bin:%.2X %.2X %.2X %.2Xn", updater_bin[0], updater_bin[1], updater_bin[2], updater_bin[3]);

    LOGI("updater_start_addr:%.8Xn", (uint32)updater_start_addr);

    LOGI("updater_reset_handler_addr:%.8Xn", (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服务器用于文件分享与下载 - 灰信网(软件开发博客聚合)

https://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,也很方便:

3、使用Node.js搭建一个简单的RESTful API服务器:


var express = require('express');

var app = express();

var fs = require("fs");

 

app.get('/ota/query', function (req, res) {

   fs.readFile( __dirname + "/resource/" + "info.json", 'utf8', function (err, data) {

       console.log( data );

       res.end( data );

   });

})

 

app.get('/ota/down/*', function (req, res) {

    console.log("req.params[0]:" + req.params[0]);

 

    fs.readFile( __dirname + "/resource/" + req.params[0], function (err, data) {

        res.end( data );

    });

})

 

var server = app.listen(80, function () {

//   var host = server.address().address

  var host = "127.0.0.1"

  var port = server.address().port

 

  console.log("Server address: http://%s:%s", host, port)

})


OTA固件压缩和解压

使用的miniz库:Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library......


PC端可以对OTA固件进行压缩,单片机端进行解压。由于单片机端没有足够的空间进行一次性数据解压,所以PC端进行一次性数据压缩,单片机端进行分批次数据解压(解压比较耗内存,单片机端代码尽量将大数组用全局变量或静态变量定义,不然heap空间不够解压算法使用会导致解压失败),代码示例:


#include

#include

#include

#include

#include

#include "string.h"

#include "debug.h"

#include "event.h"

 

#include

#include

#include

#include

#include "miniz.h"

 

typedef unsigned char uint8;

typedef unsigned short uint16;

typedef unsigned int uint;

 

#define my_max(a,b) (((a) > (b)) ? (a) : (b))

#define my_min(a,b) (((a) < (b)) ? (a) : (b))

 

#define BUF_SIZE (64 * 1024)

 

static uint8 s_inbuf[BUF_SIZE];

static uint8 s_outbuf[BUF_SIZE];

 

int my_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len)

{

    mz_stream stream;

    int status;

    memset(&stream, 0, sizeof(stream));

 

    /* In case mz_ulong is 64-bits (argh I hate longs). */

    if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU)

        return MZ_PARAM_ERROR;

 

    stream.next_in = s_inbuf;

    stream.avail_in = 0;

    stream.next_out = s_outbuf;

    stream.avail_out = BUF_SIZE;

    stream.total_out = 0;

 

    status = mz_inflateInit(&stream);

    if (status != MZ_OK)

        return status;

 

 

    uint remaining = *pSource_len;

    uint cursor = 0;

    uint total = 0;

    // LOGI("remaining: %d, cursor: %dn", remaining, cursor);

 

    while (1) {

 

        // If input buffer is empty, read more bytes from input file.

        if (!stream.avail_in) {

            uint n = my_min(BUF_SIZE, remaining);

 

            LOGI("Reading %d bytes data...n", n);

            memcpy(s_inbuf, pSource + cursor, n);

            // if (fread(s_inbuf, 1, n, pInfile) != n)

[1] [2]
关键字:嵌入式 引用地址:【嵌入式】W801 OTA方案设计

上一篇:W801上电自动重连wifi并通过蓝牙更新账号密码
下一篇:联盛德W801系列3-如何提高采集多路ADC效率

推荐阅读最新更新时间:2024-11-03 17:24

莫仕Twinax嵌入式解决方案赋予设计人员以极高自由度
当前,56Gbps PAM-4 和112 Gbps PAM-4信号成为一种主流。这对数据中心和电信基础设施行业提出了新的要求,他们需要在更紧凑的空间内提升数据速度而不得影响到信号完整性,因此他们需要考虑一个经济有效的方案,从而使效率、可靠性、能耗、热性能及速度提升到极致水平而又不影响经济效益。 从技术上讲,在相同的频率下,PAM-4技术提供的数据速率速度是NRZ技术的两倍,但PAM-4的实施具有难度。尽管Megtron 6和Megtron 8之类的印刷电路板材料可以降低插入损耗和信道噪声,但是成本高昂,而且在不使用retimers的情况下仍然可能无法提供充分的信道余量。 如此一来,业界对于低损耗高速twinax解决方案的兴
[嵌入式]
莫仕Twinax<font color='red'>嵌入式</font>解决方案赋予设计人员以极高自由度
基于LPC2478的嵌入式智能胰岛素泵的设计
概述 当前全球范围内,每10秒钟就有一个糖尿病患者因并发症死亡,在同一个10秒钟内,又会新增两例糖尿病患者;受糖尿病影响的人口总数约为2.46亿,预计在未来20年内糖尿病患者的数量会上升至3.8亿。糖尿病是一种终身代谢性疾病,若能得到有效控制,患者可以终生带病而不影响工作和生活;若控制不当,会导致心血管疾病、血脂异常、失明、肾功能衰竭及截肢等严重并发症。 现有的胰岛素泵往往只可以注入胰岛素,而缺乏对患者体内血糖的监测能力,是一个“半开环”系统。随着国民经济和人们生活水平的提高,患者对医疗仪器的需求正不断增长,对其性能需求也不断提高。因此,我们有必要设计一种基于嵌入式系统的智能胰岛素泵,具有注射和监测两种功能,实现真正的“闭环”
[单片机]
基于LPC2478的<font color='red'>嵌入式</font>智能胰岛素泵的设计
嵌入式系统应用设计应关注MPW
SoC是各种类型嵌入式应用系统的方向。长期以来,资金、批量因素一直制约着中小企业、研究机构、高等院校等部门直接采用微电子设计技术,运用ASIC模式进行嵌入式应用系统的开发;然而,嵌入式应用系统设计与微电子设计相融合已是一个技术发展趋势。为了解决这一瓶颈,国外从上世纪80年代初即开始实施了MPW服务计划与体系。MPW服务计划的实施大大加速了IC产业和嵌入式系统应用的发展。许多专家认为,我国IC产业、IC设计、ASIC应用长期落后,与我国长期以来忽视MPW服务计划与体系有关。可喜的是,近年来政府部门及相关单位己重视此问题,MPW服务体系建设研究已正式列入国家863计划,初步建立了几家MPW服务中心。MPW服务体系对我国IC产业的发展势头
[应用]
基于DSP+CPLD的嵌入式高速图像通信系统设计
   1 引言   随着现代的图形采集技术发展迅速,各种基于ISA,PCI,USB1.1等总线的图形采集卡速度已经不能满足用户的需求,而采用 USB2.0以后就可以解决这个传输速度上的瓶颈,USB2.0的速度是480Mbits/s,完全可以满足图像采集、传输以及后续处理的要求。系统中采用 DSP+CPLD的硬件设计方案,采用现场可编程芯片 CPLD及两片 SRAM构成的图像采集和存储系统,可以根据不同的需要进行现场编程,具有通用性好、价格相对便宜,易于系统调试,升级等特点。系统中 CPLD选择的型号是 ALTERA公司的MAX7000系列低功耗芯片EPM7128A。片外大容量 SRAM是DSP与 CPLD的联系桥梁,系统设计也
[嵌入式]
嵌入式编程需注意的Cache机制及其原理
  Cache的原理   Cache即高速缓存,它的出现基于两种因素:一、CPU的速度和性能提高很快,而主存速度较低且价格高;二、程序执行的局部性特点。将速度较快而容量有限的SRAM构成Cache,可以尽可能发挥CPU的高速度。CPU与外设交换数据时经常用到buffer(缓冲),这与缓存极其相似,只不过Cache是为了提高CPU和内存之间的数据交换速度而设计,而buffer是为了提高内存和硬盘(或其他I/O设备)之间的数据交换速度而设计的。   Baidu快照(cache.baidu.com)就是一个缓存的例子,其作用与计算机CPU缓存有类似之处。 Cache的原理如图1所示。   在读取内存数据的同时CPU将数据保存
[单片机]
<font color='red'>嵌入式</font>编程需注意的Cache机制及其原理
嵌入式机器视觉系统中ARM与DSP的数据通信方法
DSP对数字信号和数值算法具有强大的运算处理能力,因而在信号采集与处理中被广泛应用,但其在任务管理、实时控制、人机交互等方面不占优势。而ARM微控制器则控制功能强大,可以加载嵌入式操作系统,且能够提供良好的人机交互、任务管理、网络通信等方面功能。因此,发挥DSP和ARM处理器各自的优势,采用ARM+DSP结构的设计方案已成为嵌入式系统的研究热点,倍受关注。通过嵌入式机器视觉系统的设计实例,阐述ARM与DSP有机结合的设计思想,重点研究ARM与DSP之间的数据通信。 1 嵌入式机器视觉系统总体方案 采用ARM+DSP结构的机器视觉系统总体结构如图l所示。以三星公司高性能ARM处理器S3C2440作为主控制器,配置并移植Linu
[单片机]
<font color='red'>嵌入式</font>机器视觉系统中ARM与DSP的数据通信方法
Linux嵌入式 -- Bootloader , Uboo
1. Bootloader作用 PC机中的引导加载程序由BIOS(其本质是一段固件程序)和GRUB或LILO一起组成。BIOS在完成硬件检测和资源分配后,将硬盘中的引导程序读到系统内存中然后将控制权交给引导程序。引导程序的主要任务是将内核从硬盘上读到内存中,然后跳转到内核的入口点去运行,即启动操作系统。 简单地说,BootLoader就是在操作系统运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。 系统加电或复位后,所有的CPU通常都从CPU制造商预先安排地址开始执行。比如,S3C2410在复位后从地址0x00000000起开始执行。而嵌入式
[单片机]
Linux<font color='red'>嵌入式</font> -- Bootloader , Uboo
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved