在MAX-IDE中自动初始化数据段
摘要:该应用笔记讨论了MAX-IDE提供的程序和数据段的灵活工具,用于MAXQ®微控制器的应用编程。程序和数据段机制能够在数据存储器自动声明变量位置,并以起始值对这些变量进行初始化。应用程序可以将这些变量值缓存在闪存内,并在需要时恢复这些数据。该方法允许基于汇编语言的应用程序充分利用MAX-IDE提供的数据段自动装载功能,无论微控制器与JTAG调试器是否连接都能保持工作。MAXQ2000微控制器评估板能够演示这一方案,本文给出了相应的例程。
概述
MAXQ汇编程序的变量可以存储在工作寄存器(例如累加器A[0]至A[15])或数据存储器(SRAM)。在数据存储器中存储变量能够为应用程序变量提供较大的工作区域,但需要额外的访问时间。MaxQAsm编译器和MAX-IDE环境提供单独声明程序段和数据段的机制,对各段产生各自的hex输出文件。运行时,MAX-IDE自动将程序段文件装载到程序存储器(通常为闪存),将数据段文件装载到数据存储器(通常为RAM)。然而,由于数据存储器为易失存储器,一旦微控制器掉电,将丢失数据段内容。
本文利用MAXQ2000 EV (评估)板首先演示应用程序开始运行时如何将预装载数据存储器值保存到闪存内,随后演示微控制器重新上电时如何从闪存刷新数据段。无论应用程序处于开发阶段(连接到JTAG适配器和MAX-IDE)还是在现场运行,这两个步骤都允许使用相同的数据段机制声明和初始化变量。
该应用笔记的例程为MAXQ2000微控制器以及MAXQ2000评估板编写,但相应程序和原理适用于任何基于MAXQ20、能够重新编程闪存的微控制器。
提供最新版本的安装软件和关于MAX-IDE环境的文档,可免费下载。
- MAX-IDE安装程序
- MAXQ核编译指南
- 开发工具指南
变量和存储位置
嵌入式应用通常需要一定的工作区域存储状态信息、配置数据、中间数据、循环计数器以及计算结果。存储在该工作区域的数据通常作为变量,具有以下特征。- 它们是临时数据。如果应用程序由于电源失效或复位产生中断,不需要保存这些数据。
- 它们可频繁访问和更新。它们必须存储在能够快速读写的位置;这些位置的写入次数必须没有限制。
- 它们具有定义的初始值。程序开始运行时,用户程序必须将它们设置为特定值。
unsigned int c = 0x1234;然而,用MAXQ汇编语言编写应用程序时,必须明确指定变量分配空间并设置变量初始值。这些细节能够严格控制MAXQ微控制器的资源,但增加了系统复杂性。
对于基于汇编的小型应用程序或不需要大量工作区域的应用程序,内部寄存器可用来存储所有应用程序变量。这种方法具有两个重要优势:
- 程序结构紧凑、运行速度快。根据寄存器位置,在一个指令周期内可实现寄存器变量的读取、写入或复制其它寄存器变量。对基于MAXQ20的微控制器,在最差工作条件下,通常最多占用不超过两个指令周期。
- 直接操作变量。可直接操作一些内部寄存器。例如,可以(使用AP寄存器)选择16个工作累加器A[0]至A[15]的任何一个作为有效累加器Acc。这就意味着如果需要对这些寄存器存储的一个变量进行操作时,可直接对寄存器进行操作,无需将数据复制出来进行操作后再将数据复制回去。同样,执行djnz指令时,存储在LC[0]和LC[1]寄存器的变量可直接作为循环计数器。
move DP[0], #0010h ; Location of variable in data memory move Acc, @DP[0] ; Read variable add #1 ; Increment variable value by 1 move @DP[0], Acc ; Store variable back in data memory当对一个变量进行一系列的较长计算操作时,可以将变量值首先复制到工作寄存器,如上述例程所示。所有中间操作可利用该工作寄存器执行,完成计算后将变量值复制回来。
MAX-IDE的段声明
决定在基于SRAM的数据存储器存储应用程序变量时,如何确定变量的存储地址?通常,除了调试器使用的最高32个字节的存储空间外,应用程序可以使用其它所有数据存储器。这意味着声明一个变量即可定义其在数据存储器中的位置。程序可通过该地址对变量进行读写,用#define宏命令将变量地址和符号名称关联起来。
#define VarA #0020h #define VarB #0021h #define VarC #0022h move DP[0], VarA ; Point to VarA variable move Acc, @DP[0] ; Read value of variable move DP[0], VarB ; Point to VarB variable move @DP[0], Acc ; Copy VarA to VarB move DP[0], VarC ; Point to VarC variable move @DP[0], #1234h ; Set VarC = 1234h这种方案可以很好地工作,但是有几个问题需要注意。
- 必须事先定义每个变量的地址,这项工作比较耗时,特别是确定随后将所有变量移至不同的数据存储区域时。
- 必须注意一个以上的变量不要占用同一地址,如果发生这种错误将很难追踪这些故障。
- 变量的初始(开始)值必须通过应用程序装载,如上述程序的最后一行。如果有多个变量按照这种方式初始化将会占用大量的程序空间。
segment code move DP[0], #VarA ; Point to VarA move Acc, @DP[0] ; Get current value of VarA add #1 ; Increment it move @DP[0], Acc ; Store value back in VarA segment data VarA: dw 0394h ; Initial value for VarA利用上述方案,在数据段声明的变量地址由编译器解析文件时自动指定,用同样方法为程序空间分配地址标签。标签用于对变量地址指定符号名称,dw和db声明可以在初始化变量时用于设置字宽或字节宽度初始值。这种情况下,假定汇编文件中事先没有segment data指令,编译器将从0000h地址起始数据段。这意味着VarA将存储在字地址0000h。对于程序空间,org声明将强制变量从指定的起始地址开始存储。
数据段初始化
在先前的程序清单中,变量VarA定义(用dw声明)的初始值为0394h。但是,该值在程序中并不装载到VarA。那么,如何初始化这一数值? 答案是在编译和运行工程时,MAX-IDE将自动执行数据段初始化。MaxQAsm编译器通过产生一个二级hex输出文件响应segment data指令。通常,为工程产生的hex文件包含程序数据。例如,如果编译工程"example.prj",将产生一个名称为"example.hex"的hex文件,并包含通过编译工程文件产生的程序数据。如果定义了数据段,则将产生一个名称为"example_d.hex"的附加hex文件,该文件包含该段编译数据。
执行工程时,MAX-IDE检查是否在工程编译中产生了数据段文件(以_d.hex结尾)。如果存在数据段文件,MAX-IDE通过标准的JTAG装载器将该段数据装载到器件的数据SRAM。该过程在标准的hex文件装载到程序存储器之后执行。
这种方案能够很好地工作在开发阶段,当器件连接到JTAG适配器,在应用程序运行之前,MAX-IDE重新装载程序数据和段数据。但是,一旦器件掉电并重新上电,而且允许独立运行(没有连接调试器),在每次运行前MAX-IDE将无法正确装载数据段。变量也无法设置在所要求的数值,导致应用程序不能正确执行。这种故障很难分析,因为一旦器件重新连接到调试器,MAX-IDE将在每次运行前重新开始装载数据段,问题也就消失了。
保存和恢复数据段
一个遗留问题是:如何使应用程序在连接调试器(每次运行前MAX-IDE重新装载程序和数据)和独立运行(上电后RAM内容不确定)时都能保持工作。显然,解决方法需要两个步骤:应用程序将变量值(一旦经过初始化)保存到闪存,每次复位或上电后重新装载这些数值。对于第一步,应用程序必须将数值保存到闪存。每次主机擦除或装载程序后第一次运行应用程序时执行该操作。
- 应用程序检测“标志”位置以验证变量之前是否复制到闪存内。该标志可以存储在特殊功能、非易失存储器,或与变量共用存储器,只要变量具有非零初始值(与空RAM地址区分开)。
- 应用程序将每个变量值从数据RAM复制到闪存,绝大多数带有可重复写操作闪存的MAXQ微控制器(如MAXQ2000)利用UROM_flashWrite函数实现。
- 应用程序在闪存中写一个标志,表明已经存储变量。
- 应用程序检测闪存的标志位置,以验证已经存储变量。
- 应用程序利用UROM_copyBuffer子程序将变量从闪存复制到数据RAM的正确位置。
$include(maxQ2000.inc) ;; Code memory (flash) : 0000h-7FFFh (word addr) ;; Data memory (RAM) : 0000h-03FFh (word addr) org 0000h ljump start ; Skip over password area org 0020h start: move DPC, #1Ch ; Set all pointers to word mode move DP[0], #0F000h ; Check first variable value (flag) lcall UROM_moveDP0 ; 'move GR, @DP[0]' executed by Utility ROM move Acc, GR cmp #1234h jump NE, copyToFlash ;; This is the "free-running" code, executed on subsequent power-ups, that copies ;; values from the flash back into their proper data segment locations. move DP[0], #0F000h ; Source: Flash location 7000h move BP, #0 ; Dest: Start of RAM move Offs, #0 move LC[0], #100h ; Copy 256 words lcall UROM_copyBuffer jump main ;; This is the first-pass code. A bit of a trick here; because MAX-IDE enters ;; and exits the loader separately when loading the code and data segment files, ;; the application is allowed to execute briefly before the data segment file ;; has been loaded. The first four lines under copyFlash ensure that the ;; application waits for MAX-IDE to load the data segment file before continuing. copyToFlash: move DP[0], #0h ; Wait for flag variable to be loaded by MAX-IDE. move Acc, @DP[0] ; Note that this will reset the application; the cmp #1234h ; data segment is not loaded while the application jump NE, copyToFlash ; is still running. move DP[0], #0 ; Start of RAM variable area move A[4], #7000h ; Location in flash to write to move LC[0], #100h ; Store 256 words in flash 7000h-70FFh copyToFlash_loop: move DP[0], DP[0] ; Refresh the data pointer to read values correctly, ; because calling UROM_flashWrite changes memory ; contexts and affects the cached @DP[0] value move A[0], A[4] ; Location to write move A[1], @DP[0]++ ; Value to write (taken from RAM) lcall UROM_flashWrite move Acc, A[4] add #1 move A[4], Acc djnz LC[0], copyToFlash_loop main: move PD0, #0FFh ; Set all port 0 pins to output move PO0, #000h ; Drive all port 0 pins low (LEDs off) move DPC, #1Ch ; Set pointers to word mode move DP[0], #varA move Acc, @DP[0] cmp #1234h ; Verify that the variable is set correctly jump NE, fail pass: move PO0, #55h sjump $ fail: sjump $ segment data org 0000h varA: dw 1234h org 00FFh varB: dw 5678h end
评论
查看更多