概述
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核编译指南
开发工具指南
变量和存储位置
嵌入式应用通常需要一定的工作区域存储状态信息、配置数据、中间数据、循环计数器以及计算结果。存储在该工作区域的数据通常作为变量,具有以下特征。
它们是临时数据。如果应用程序由于电源失效或复位产生中断,不需要保存这些数据。
它们可频繁访问和更新。它们必须存储在能够快速读写的位置;这些位置的写入次数必须没有限制。
它们具有定义的初始值。程序开始运行时,用户程序必须将它们设置为特定值。
用C语言或其它高级语言编写的程序编译成汇编代码,编译器通常自动为变量分配空间(同时将变量初始化为预先定义的起始值)。这种情况下,用户只需声明变量、变量类型及其初始值(可选),其余工作由编译器处理。
unsigned int c = 0x1234;
然而,用MAXQ汇编语言编写应用程序时,必须明确指定变量分配空间并设置变量初始值。这些细节能够严格控制MAXQ微控制器的资源,但增加了系统复杂性。
对于基于汇编的小型应用程序或不需要大量工作区域的应用程序,内部寄存器可用来存储所有应用程序变量。这种方法具有两个重要优势:
程序结构紧凑、运行速度快。根据寄存器位置,在一个指令周期内可实现寄存器变量的读取、写入或复制其它寄存器变量。对基于MAXQ20的微控制器,在最差工作条件下,通常最多占用不超过两个指令周期。
直接操作变量。可直接操作一些内部寄存器。例如,可以(使用AP寄存器)选择16个工作累加器A[0]至A[15]的任何一个作为有效累加器Acc。这就意味着如果需要对这些寄存器存储的一个变量进行操作时,可直接对寄存器进行操作,无需将数据复制出来进行操作后再将数据复制回去。同样,执行djnz指令时,存储在LC[0]和LC[1]寄存器的变量可直接作为循环计数器。
对于大规模应用程序或者当应用程序需要大量工作变量时,可将其中一些或所有变量存储在基于SRAM的数据存储器。这种方式允许创建更多的变量,具体受限于数据存储器的空间。按照这种方式存储的变量可以通过MAXQ20核的标准数据指针访问,该指针用于读取或写入字节宽度或字宽变量(注意:本应用笔记的例程假定DP[0]配置为工作在字模式)。
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
这种方案可以很好地工作,但是有几个问题需要注意。
必须事先定义每个变量的地址,这项工作比较耗时,特别是确定随后将所有变量移至不同的数据存储区域时。
必须注意一个以上的变量不要占用同一地址,如果发生这种错误将很难追踪这些故障。
变量的初始(开始)值必须通过应用程序装载,如上述程序的最后一行。如果有多个变量按照这种方式初始化将会占用大量的程序空间。
比较有效的方案是利用MAX-IDE机制分别声明程序段和数据段。这种方法允许编程人员指定汇编程序的哪一部分定义为程序空间,哪一部分定义为数据空间。
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函数实现。
应用程序在闪存中写一个标志,表明已经存储变量。
对于第二步,在后续的程序运行中,应用程序必须将变量从闪存重新装载到预先规定的数据RAM地址。
应用程序检测闪存的标志位置,以验证已经存储变量。
应用程序利用UROM_copyBuffer子程序将变量从闪存复制到数据RAM的正确位置。
以下程序清单展示了利用MAXQ2000评估板的保存-恢复方案,该程序中,变量值存储在闪存的7000h–71FFh地址内。
$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
结论
MAX-IDE提供的程序段和数据段工具能够在数据存储器中自动声明变量地址,以起始值初始化这些变量。可以利用应用程序在闪存中缓存这些变量,必要时恢复它们。这种方法允许基于汇编的应用程序使用MAX-IDE提供的数据段自动装载功能,无论微控制器是否连接JTAG调试器,都能保持工作。