本应用笔记讨论了MAX-IDE为MAXQ®微控制器上的应用编程提供的代码和数据段工具。代码和数据段机制提供了一种在数据存储器中自动声明变量位置并使用起始值初始化这些变量的方法。然后,可以使用应用程序代码将这些变量值缓存在闪存中,并根据需要还原它们。这种方法允许基于汇编的应用利用MAX-IDE提供的数据段自动加载,同时无论微控制器是否连接到JTAG调试器,都能始终如一地运行。MAXQ2000微控制器评估板用于演示方法,文中提供了代码示例。
概述
MAXQ汇编应用中的变量可以存储在工作寄存器(如累加器A[0]至A[15])或数据存储器(SRAM)中。将变量存储在数据存储器中为应用程序变量提供了更大的工作区域,但确实需要额外的访问时间。
MaxQAsm 汇编器和 MAX-IDE 环境提供了一种声明单独代码和数据段的机制,并为每个段生成单独的十六进制输出文件。在运行时,MAX-IDE自动将数据段文件加载到程序存储器(通常为闪存)中,将数据段文件加载到数据存储器(通常为RAM)中。但是,由于数据存储器是易失性的,一旦微控制器断电,数据段内容将不会保持不变。
本应用笔记首先使用MAXQ2000评估(评估)板演示如何在初始应用运行期间将这些预加载的数据存储器值保存在闪存中,然后在微控制器随后再次上电时如何刷新闪存中的数据段值。这个两步过程允许使用相同的数据段机制来声明和初始化变量,无论应用程序是在开发中(连接到JTAG适配器和MAX-IDE)还是在现场运行。
本应用笔记的演示代码为MAXQ2000微控制器和MAXQ2000评估板编写,但所示代码和原理适用于任何具有可重写程序闪存的MAXQ20微控制器。
MAX-IDE环境的最新安装包和文档可免费下载。
最大集成开发台安装
MAXQ磁芯组装指南
开发工具指南
变量和存储位置
嵌入式应用程序通常需要一定的工作空间来存储状态信息、配置设置、中间工作值、循环计数器和计算结果。存储在此工作空间中的值通常称为变量,并具有以下特征。
它们是暂时的。如果应用程序因电源故障或重置而中断,则不需要保存它们。
它们经常被访问和更新。它们必须存储在可以快速读取或写入的位置;位置的写入次数不得有限制。
它们通常具有定义的初始值。用户的代码必须在应用程序开始时将它们设置为特定值。
在用 C 或其他高级语言编写并编译为汇编代码的应用程序中,编译器通常自动处理变量的空间分配(以及将变量初始化为预定义起始值的过程)。在这种情况下,用户只需要声明变量、其类型和(可选)其初始值。编译器处理其余部分。
unsigned int c = 0x1234;
但是,当直接用MAXQ汇编语言编写应用程序时,必须明确地为变量分配空间并将变量设置为初始值。这种详细的操作允许对MAXQ微控制器上的可用资源进行更严格的控制,但增加了系统的复杂性。
对于基于程序集的小型应用程序或不需要大量工作空间的应用程序,可以使用内部寄存器来存储所有应用程序变量。此方法具有两个重要优势:
紧凑、快速的代码。寄存器变量可以在短短一个指令周期内从另一个寄存器变量读取、写入或复制到另一个寄存器变量,具体取决于寄存器的位置。在基于MAXQ20的微控制器上,在最坏的情况下,通常不需要超过两个指令周期。
对变量的直接操作。一些内部寄存器位置可以直接操作。例如,可以选择 16 个工作累加器 A[0] 到 A[15] 中的任何一个(使用 AP 寄存器)作为活动累加器 Acc。这意味着,如果需要对存储在其中一个寄存器中的变量执行操作,则可以直接在该寄存器上执行该操作,而无需复制值、执行操作并重新复制值。类似地,存储在 LC[0] 和 LC[1] 寄存器中的变量可以通过执行 djnz 指令直接用作循环计数器。
较大的应用或需要大量工作变量的应用可以受益于将其部分或全部变量存储在基于SRAM的数据存储器中。此方法允许创建更多数量的变量,直至达到数据存储器大小的限制。以这种方式存储的变量可以使用MAXQ20内核的标准数据指针访问,该指针可用于读写字节大小或字大小的变量。(注:本应用笔记中的所有代码示例都假定DP[0]配置为在word模式下工作。
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 语句可用于初始化具有起始值的字大小和字节大小的变量。在这种情况下,假设在程序集文件中找不到以前的段数据指令,汇编程序将从地址 0000h 开始数据段。这意味着 VarA 将存储在字地址 0000h 处。与在代码空间中一样,org 语句可以强制变量位于指定地址的开头。
初始化数据段
在前面的代码清单中,变量 VarA 被定义为(使用 dw 语句)具有 0394h 的初始值。但此值永远不会加载到代码中的 VarA 中。那么,这个值是如何初始化的呢?答案是,数据段的初始化由MAX-IDE在项目编译和执行时自动执行。
MaxQAsm 汇编器通过生成辅助十六进制输出文件来响应段数据指令。通常,为包含代码数据的项目生成十六进制文件。例如,如果编译了项目“example.prj”,则将创建一个名为“example.hex”的十六进制文件,其中包含通过组装项目文件生成的代码数据。如果定义了数据段,则将创建一个名为“example_d.hex”的附加十六进制文件,其中包含在此段中组装的数据。
执行项目时,MAX-IDE 会检查在项目编译期间是否生成了数据段文件(以 _d.hex 结尾)。如果存在数据段文件,MAX-IDE使用标准JTAG加载器将来自该段的数据加载到器件的数据SRAM中。这是在将标准十六进制文件加载到程序内存后完成的。
这种方法在开发周期中效果很好,当器件连接到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调试器,都能始终如一地运行。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !