美文网首页
单片机分散加载与部分升级

单片机分散加载与部分升级

作者: 查无此人的查无此人 | 来源:发表于2020-09-27 10:29 被阅读0次

Keil编译后的Code,RO,RW,ZI分别表示什么

  • Code:代码的大小
  • RO:常量所占空间
  • RW:程序中已经初始化的变量所占空间
  • ZI:未初始化的static变量和全局变量以及堆栈所占的空间
    上述参数和芯片Flash以及SRAM的对应关系:
  • Flash占用大小=Code+RO+RW
  • SRAM占用大小=RW+ZI
    ZI变量是未初始化的变量,上电之后芯片会将这些变量初始化为0,因此这些变量在flash中无需占用空间。
    RW变量为代码中初始化的变量,因此在flash中需要记录相应的值,占用flash空间;同时RW变量在运行过程中会被修改,需要占用RAM空间。
uint8_t a;//ZI变量
uint8_t a = 0x55;//RW变量
const uint8_t a = 0x55;//RO变量

查看程序占用的ram、flash空间

程序编译成功后,生成map文件,详细记录了各个函数和变量的位置和大小。最后还会给出ram占用情况,flash占用情况。通过勾选listing选项卡下的linker listing选项,即可打开map生成功能。


配置map文件

其中Memory Map of the image部分,可以看到程序中不同的C文件生成的代码在ram和flash中是如何分布的。

==============================================================================

Memory Map of the image

Image Entry point : 0x00008001

Load Region BOOT_IROM (Base: 0x00000000, Size: 0x00007424, Max: 0x00008000, ABSOLUTE, COMPRESSED[0x0000738c])

Execution Region BOOT_IROM (Exec base: 0x00000000, Load base: 0x00000000, Size: 0x00007294, Max: 0x00008000, ABSOLUTE)

Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

0x00000000   0x00000000   0x000000e0   Data   RO         1272    RESET               startup_efm32lg.o
0x000000e0   0x000000e0   0x00000096   Code   RO         2223    .emb_text           port.o
0x00000176   0x00000176   0x00000002   PAD
0x00000178   0x00000178   0x00000a84   Code   RO          299    .text               em_cmu.o
0x00000bfc   0x00000bfc   0x00000260   Code   RO          338    .text               em_core.o
0x00000e5c   0x00000e5c   0x0000042e   Code   RO          493    .text               em_dma.o
0x0000128a   0x0000128a   0x00000002   PAD
0x0000128c   0x0000128c   0x00000308   Code   RO          549    .text               em_emu.o
0x00001594   0x00001594   0x00000148   Code   RO          616    .text               em_gpio.o
0x000016dc   0x000016dc   0x00000374   Code   RO          643    .text               em_i2c.o
0x00001a50   0x00001a50   0x000001c0   Code   RO          814    .text               em_leuart.o
0x00001c10   0x00001c10   0x000000ac   Code   RO          842    .text               em_msc.o
0x00001cbc   0x00001cbc   0x0000007c   Code   RO         1020    .text               em_rtc.o
0x00001d38   0x00001d38   0x00000064   Code   RO         1087    .text               em_system.o
0x00001d9c   0x00001d9c   0x000001fc   Code   RO         1111    .text               em_timer.o
0x00001f98   0x00001f98   0x00000330   Code   RO         1138    .text               em_usart.o
0x000022c8   0x000022c8   0x000000a2   Code   RO         1215    .text               em_wdog.o

Map文件的末尾,则给出了ram和flash的占用情况:

==============================================================================

Code (inc. data)   RO Data    RW Data    ZI Data      Debug   

58976       3888       3528       1672      30280     789511   Grand Totals
58976       3888       3528        832      30280     789511   ELF Image Totals (compressed)
58976       3888       3528        832          0          0   ROM Totals

==============================================================================

Total RO  Size (Code + RO Data)                62504 (  61.04kB)
Total RW  Size (RW Data + ZI Data)             31952 (  31.20kB)
Total ROM Size (Code + RO Data + RW Data)      63336 (  61.85kB)

==============================================================================

分割flash存储区

利用__attribute__((section("name")))可以将变量或者函数定义到name区域,
__attribute__((section(".appmain.rom"))) int main(void),main函数将被分配到.appmain.rom区域。
利用__attribute__((at(addr)))可以将变量定义到绝对位置addr,addr可以是flash,也可以是ram,
uint8_t buf[5] __attribute__((at(0X20001000)));//起始地址为0X20001000
也可以用#pragma arm section code=" name "的形式将整个大段代码定义到name段。除了code,还可以用rodata、rwdata、zidata将变量定义到不同的段
#pragma arm section code,rodata,rwdata,zidata不带name的形式意味着后续代码和变量定义到默认的段中。
默认代码段.text:

0x00004308   0x00004308   0x00000364   Code   RO         2157    .text               segger_rtt_printf.o
0x0000466c   0x0000466c   0x00000230   Code   RO         2183    .text               event_groups.o
0x0000489c   0x0000489c   0x00000088   Code   RO         2211    .text               list.o
0x00004924   0x00004924   0x00000134   Code   RO         2224    .text               port.o
0x00004a58   0x00004a58   0x00000624   Code   RO         2254    .text               queue.o
0x0000507c   0x0000507c   0x00000ce4   Code   RO         2270    .text               tasks.o
0x00005d60   0x00005d60   0x00000364   Code   RO         2302    .text               timers.o
0x000060c4   0x000060c4   0x00000062   Code   RO         2320    .text               mc_w.l(uldiv.o)

自定义代码段.appmain.rom、.app.rom:

0x0000a768   0x0000a768   0x00000f04   Code   RO         1405    .app.rom            bsp.o
0x0000b66c   0x0000b66c   0x00000240   Code   RO         1493    .app.rom            common.o
0x0000b8ac   0x0000b8ac   0x000005e8   Code   RO         1521    .app.rom            cpu_card.o
0x0000be94   0x0000be94   0x000007d0   Code   RO         1565    .app.rom            fingertask.o
0x0000c664   0x0000c664   0x000022e8   Code   RO         1644    .app.rom            locktask.o
0x0000e94c   0x0000e94c   0x00000658   Code   RO         1762    .app.rom            mfrc522.o
0x0000efa4   0x0000efa4   0x000003fc   Code   RO         1797    .app.rom            os.o
0x0000f3a0   0x0000f3a0   0x00000258   Code   RO         1828    .app.rom            rfidtask.o
0x0000f5f8   0x0000f5f8   0x00000128   Code   RO         1858    .app.rom            sleeptask.o
0x0000f720   0x0000f720   0x00000698   Code   RO         1888    .app.rom            touchpadtask.o
0x0000fdb8   0x0000fdb8   0x00000578   Code   RO         1921    .app.rom            usermanage.o
0x00010330   0x00010330   0x000007c4   Code   RO         1951    .app.rom            videotask.o
0x00010af4   0x00010af4   0x000003cc   Code   RO         1984    .app.rom            voicetask.o
0x00010ec0   0x00010ec0   0x0000018c   Code   RO         2026    .app.rom            wt588h.o
0x0001104c   0x0001104c   0x00000085   Data   RO         1412    .app.rom            bsp.o
0x000110d1   0x000110d1   0x0000000e   Data   RO         1527    .app.rom            cpu_card.o

分段链接固件

C代码编译过程包含预处理、编译、汇编、链接四个过程,其中链接过程决定了代码在固件中的分布情况。


编译链接过程

Keil mdk根据链接脚本来控制代码的分布,默认的链接脚本如下所示:

LR_IROM1 0x00000000 0x0001E000  {    ; load region size_region
  ER_IROM1 0x00000000 0x0001E000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00007FFF  {  ; RW data
   .ANY (+RW +ZI)
  }
}

链接脚本由如下形式构成:

load_region_name base_address max_size
{
    execution_region_description
    {
        
    }
}

load_region_name为加载时域的名字,长度不超过31个字节;
base_address为加载时域的起始地址,即从该地址开始加载相关代码;
max_size为加载时域的最大范围,若实际大小超过该大小,链接器将会报错;
execution_region_description是对执行时域的描述.
由于单片机代码在flash上运行,因此加载域和运行域在同一个地址,而变量涉及到平方修改,因此变量的运行域在ram上。
*.o (RESET, +First)表示将reset段放在flash最开始的地方,因为单边机上电后就从这里开始执行代码。Reset段代码定义在start文件中,一般用汇编编写:

; Vector Table Mapped to Address 0 at Reset

                AREA    RESET, DATA, READONLY, ALIGN=8
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp              ; Top of Stack
                DCD     Reset_Handler             ; Reset Handler
                DCD     NMI_Handler               ; NMI Handler
                DCD     HardFault_Handler         ; Hard Fault Handler

*(InRoot$$Sections)段用于加载代码,将变量从flash复制到ram。
.ANY (+RO)、.ANY (+XO)表示剩余的代码,.ANY (+RW +ZI)表示剩余的变量.

自定义链接脚本

为了减少升级时固件大小,可以将代码分成固定部分和可升级部分。启动代码、外设库、加解密代码等程序一般是不会改变的,而应用代码则可能根据需求进行更改,因此两部分分开存放,升级时仅需覆盖应用代码即可。由于程序更改时会导致分区大小的变化,因此在更新应用的同时,需要更新分区表。将分区表分配在单独的区域,置于boot之后,单独更新。
去掉use memory layout from target dialog的选项,即可选择自定义的链接脚本。


选择自定义链接文件

自定义链接文件如下:

#! armcc -E
#define RomBase 0x00000000
#define RomTotal 0x20000
#define RamBase 0x20000100
#define RamTotal 0x7F00

#define BootRomSize 0x8000
#define TableRomSize 0x1000
#define CodeRomSize (RomTotal - BootRomSize - TableRomSize)
#define BootRomAddr RomBase
#define TableRomAddr (RomBase + BootRomSize)
#define CodeRomAddr (TableRomAddr + TableRomSize)

#define BootRamSize 0x2c00
#define CodeRamSize (RamTotal - BootRamSize)
#define BootRamAddr RamBase
#define CodeRamAddr (RamBase + BootRamSize)

BOOT_IROM BootRomAddr BootRomSize {
  BOOT_IROM BootRomAddr BootRomSize {
   *.o (RESET, +First)   
   .ANY (+RO)
  }
  BOOT_IRAM BootRamAddr BootRamSize {
   .ANY (+RW +ZI)
  }
}

TABLE_IROM TableRomAddr TableRomSize {
  TABLE_IROM TableRomAddr TableRomSize {
    *(InRoot$$Sections)
  }
}

CODE_IROM CodeRomAddr CodeRomSize {
  CODE_IROM CodeRomAddr CodeRomSize {
    *(.appmain.rom, +First)
    *(.app.rom)
  }
  CODE_IRAM CodeRamAddr CodeRamSize {
    *(.app.ram)
  }
}

#! armcc -E用来处理后续的#define,可以定义段地址、段长度等变量。
CODE_IROM用来存储可升级代码和变量,同样ram也分成两段。
TABLE_IROM用来存储分区表,升级后单片机重新读取分区表,才能够正确地加载代码。
可升级部分分成了.appmain.rom段和.app.rom,目的是为了从固定段运行到可升级段时,能从固定的地址开始。在上述例子中,可升级部分的第一个段.appmain.rom肯是在地址0xa200,而且.appmain.rom段中只有一个函数,因此这个函数一定是在0xa200。固件升级后,不管内容如何修改,固定段仍然可以通过跳转执行0xa200的函数,从而执行可升级段的程序。
考虑到flash一般扇区为4k,因此每段均4k对齐,分区表那段直接设为4k。

重映射中断向量表

单片机的中断跳转是由硬件决定的,代码分段存储后,中断依然会跳刀地址0去读取中断服务函数。由于应用更新后,中断服务函数的地址可能变化,因此需要将中断向量表映射到ram中,并且在可升级段代码的开头重映射,确保中断函数地址正确。
一般单片机都有重映射中断向量表的功能,如efm32系列单片机,可通过SCB->VTOR寄存器定义向量表的地址,因此在ram中定义中断服务函数数组,并将数组地址赋值到SCB->VTOR即可。
针对stm32f0系列单片机,由于没有提供SCB->VTOR寄存器,不能通过上述方式重映射。而是在ram开头0x20000000地址处定义中断服务函数数组,然后将地址0x00000000映射到0x20000000处实现中断向量表重映射。另外需要将加载脚本中ram地址空出一部分用于存放数组,防止其他变量占用这段地址空间。

批处理合并升级文件

keil在编译固件时,会根据链接文件的每一个加载地址生成多个固件文件。为了方便升级,编译完成后调用批处理文件,对固件进行合并。

set varpath="%~dp0obj\bin\TABLE_IROM.bin"
set codepath="%~dp0obj\bin\CODE_IROM.bin"

del /q temp.bin
del /q ..\update.bin
del /q ..\complete.hex
del /q .\obj\bin

fromelf.exe --bin --output .\obj\bin .\obj\EFM32LG.axf
copy .\obj\EFM32LG.hex ..\complete.hex

cd obj\bin
ren TABLE_IROM TABLE_IROM.bin
ren CODE_IROM CODE_IROM.bin
cd ..\..

for %%a in (%varpath%) do set /a size=4*1024-%%~za
fsutil file createnew temp.bin %size%
copy /b %varpath% + temp.bin + %codepath% ..\update.bin

del /q temp.bin

部分升级测试

利用jlink可以实现部分程序的更新,用以测试分散加载效果。选择芯片型号、升级文件名、烧写地址,就可实现用jlink下载固件。

@echo off

set chip=EFM32LG230F128
set filename=update.bin
set addr=0x8000
set software="F:/SEGGER/JLink/JLink.exe"

echo device %chip%> __download__script.jlink
echo if swd>> __download__script.jlink
echo speed 4000>> __download__script.jlink
echo r>> __download__script.jlink
echo loadfile %filename%,%addr%>> __download__script.jlink
echo r>> __download__script.jlink
echo exit>> __download__script.jlink
%software% __download__script.jlink
del __download__script.jlink
exit

相关文章

  • 单片机分散加载与部分升级

    Keil编译后的Code,RO,RW,ZI分别表示什么 Code:代码的大小 RO:常量所占空间 RW:程序中已经...

  • STM32BootLoad深入理解

    BoootLoad在线升级分为两个部分:Boot、APP Boot Boot为单片机复位启动以后运行的部分,这里面...

  • 2019-01-05 人机接口 D/A、A/D转换器接口

    存储器扩展 部分译码法所谓部分译码就是存储器芯片的地址线与单片机的地址线顺次相接后,单片机剩余的高位地址线仅用一部...

  • 单片机串行口通信原理

    单片机通信是指单片机与单片机之间、单片机与计算机之间的通信。 通信可以分成两种方式:并行通信方式和串行通信方式。 ...

  • 实时操作系统在单片机开发中的运用

    1、背景概述 单片机由于资源受限,不能加载linux等大型操作系统,一般均采用裸机编程的形式。随着单片机资源的增加...

  • M.M

    单片机数组与指针

  • Android9.0部分机型WebView加载图片失败

    由于项目sdk升级到28,也就是Android9.0。在加载webview的时候,部分机型webview网络图片加...

  • 单片机系统能否正常工作

    单片机系统能否正常工作 最小系统概念:单片机的最小系统就是让单片机能正常工作并发挥其功能时所必须的最少硬件组成部分

  • 漫谈可编程芯片(转载)

    漫谈可编程芯片 - 单片机DIY制作 单片机论坛 写在前面:很多朋友都问我做的是什么东西,我说单片机,但是大部分人...

  • PDF预览:vue-pdf

    1、下载依赖 2、基础示例(加载一页) 2、升级示例(加载多页) 3、升级示例(多页时,添加各种操作按钮)

网友评论

      本文标题:单片机分散加载与部分升级

      本文链接:https://www.haomeiwen.com/subject/brmkuktx.html