在MSP430的RAM中调试程序的研究

本文介绍了如何在MSP430F5529的RAM中进行程序调试,详细解析了MSP430F5529的启动过程,包括中断向量表的位置和程序入口点。通过调整IAR IDE的设置和链接器脚本,实现了在RAM中调试,避免了直接运行到main函数,确保了程序的完整执行流程。此外,还讨论了RAM与FLASH之间的切换问题,以及如何处理中断向量表,确保在RAM调试时中断功能的正常工作。
摘要由CSDN通过智能技术生成

       先说下我所使用的IDE及硬件,IDE为IAR集成开发环境,本人从学习单片机时就使用的IAR,CCS也用过,但觉得没IAR使用的顺手,如果是CCS爱好者请自己去研究下CCS中应该怎么设置才能在RAM中调试MSP430的程序,理论上也是很简单的。硬件为MSP-EXP430F5529LP,也就那块红色的MSP430F5529 LaunchPad 。至于为什么要在RAM中调试程序,自己去百度这样做的好处吧,我也懒得说了。

       首先简单分析下MSP430F5529的启动过程,根据其官方数据手册所述,中断向量表如下:

可以看到中断向量表在0xFF80-0xFFFF,其中复位向量在0xFFFE-0xFFFF,一共两个字节,16位,系统上电或复位后从这里取出16数据作为PC的值,然后MCU开始执行“main”函数,其实并不是最先执行的main函数,而是执行IAR编译器定义的程序入口点__program_start函数,其又被编译器定义为cstart_begin函数,后面紧跟着的是cstart_call_main函数,此函数才会去调用main函数。口说无凭,实验为证。

       使用IAR创建一个工程,在main.c中随便写几句代码,然后在工程选项的Debugger子选项卡中取消勾选Run to main,取消此勾选的原因是此选项会在调试中直接让单片机运行到main函数的第一行代码然后暂停。这样一来main函数之前的代码就无法看到了,我这里使用的是软件仿真,硬件仿真也会是一样的结果。

设置完后点击调试按钮出现反汇编窗口,如下图,可以看到,程序并没有停在左边的源代码窗口,而是停在了右边的反汇编窗口,并且当前程序的地址为0x4400,其具有两个标号,分别为cstart_begin和__program_start,此函数只有一句代码"MOV.W #0x4400,SP",不难看出这是在设置堆栈指针SP为0x4400(注意和当前的PC值不要搞混,虽然值是一样的),下面紧跟标号为cstart_call_main的函数,此函数第一句代码是调用的main函数,第二句调用exit函数,exit函数是编译器自动为我们定义的函数,这里不做说明。

然后再在Go to那里输入中断向量表的地址0xFFFE,结果如下图,红框处即为复位向量,其值为0x4400,后面是5529的参考手册中所说的仿真器指令中的一条,不做分析。于是验证了上面所说的MSP430F5529的启动过程。

既然已经知道了MSP430F5529的启动过程,那么接下来就是找链接器脚本了,不过IAR将其叫做XLINK configuration file(XLINK配置文件),其后缀名为.xcl,在工程选项的Linker子选项卡中有和链接器有关的选项,如下图

可以看到Linker configuration file选项一栏有Override default选项,将其勾选,然后就可以找到IAR默认使用的链接器脚本文件,其路径为X:\

Program Files (x86)\IAR Systems\Embedded Workbench 7.2\430\config\linker,这个文件夹里有许多xcl文件,找到对应单片机的即可。顺带

提一下在Linker configuration file选项下面有一个Override default program entr的选项,这里就有IAR自己定义的程序入口点的函数名称。

        在分析MSP430F5529的xcl文件之前先分析下MSP430F5529的的内存映像,其数据手册中有一个Memory Organization的表格,如下图

从这个表格中可以看出F5529的主FLASH所在的地址空间为0x4400-0x243FF,总大小为128KB,RAM所在地址空间为0x2400-0x43FF,总大小为8KB,其他地址空间就暂且不看了,反正这里用不到。

       接下里就是分析F5529的xcl文件了,F5529的xcl文件行数比较多,我就不贴出来了,只要关注其中几行即可。下面为RAM中所存的段(Segment),至于什么是段,我也不解释了,这个是嵌入式开发中关于链接器的知识,自己百度去吧。

// -----------------------------------------------
// RAM memory
//

-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,TLS16_I=2400-43FF
-Z(DATA)DATA16_HEAP+_DATA16_HEAP_SIZE
-Z(DATA)CODE_I
-Z(DATA)DATA20_I,DATA20_Z,DATA20_N,DATA20_HEAP+_DATA20_HEAP_SIZE
-Z(DATA)CSTACK+_STACK_SIZE#

可以看出这里定义的RAM地址空间为0x2400-0x43FF,和数据手册中的RAM所在地址空间一致。

下面为主FLASH中所存的段

// -----------------------------------------------
// Read-only memory
//

// -------------------------------------
// Low memory 0-0FFFF
//

// ---------------------------
// Constant data
//

-Z(CONST)DATA16_C,DATA16_ID,TLS16_ID,DIFUNCT,CHECKSUM=4400-FF7F

// ---------------------------
// Code
//

-Z(CODE)CSTART,ISR_CODE,CODE16=4400-FF7F


// -------------------------------------
// All memory 0-FFFFF
//

// ---------------------------
// Code
//

-P(CODE)CODE=4400-FF7F,10000-243FF

// ---------------------------
// Constant data
//

-Z(CONST)DATA20_C,DATA20_ID,CODE_ID=4400-FF7F,10040-243FF


// -------------------------------------
// Interrupt vectors
//

-Z(CODE)INTVEC=FF80-FFFF
-Z(CODE)RESET=FFFE-FFFF

可以看出这里定义的主FALSH地址空间为0x4400-0xFF7F、0xFF80-0xFFFF和0x10000-0x243FF。其中貌似部分段的存放地址是从0x10040开始的,不过这并无大碍,反正都在主FLASH空间。

        链接器脚本分析完了,接下来就是在RAM中调试程序了,在这里只需更改链接器脚本中的FLASH地址空间为RAM地址空间即可实现在RAM中调试程序,那么将RAM分为0x2400-0x33FF、0x3400-0x437F和0x4380-0x43FF三个部分,分别用作FLASH、RAM和中断向量表,至于为什么要将中断向量表放在0x4380-0x43FF处,后面在RAM中调试需要使用中断的程序时再做说明。xcl文件的改动部分如下

// -----------------------------------------------
// RAM memory
//

-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,TLS16_I=3400-437F
-Z(DATA)DATA16_HEAP+_DATA16_HEAP_SIZE
-Z(DATA)CODE_I
-Z(DATA)DATA20_I,DATA20_Z,DATA20_N,DATA20_HEAP+_DATA20_HEAP_SIZE
-Z(DATA)CSTACK+_STACK_SIZE#


// -----------------------------------------------
// Read-only memory
//

// -------------------------------------
// Low memory 0-0FFFF
//

// ---------------------------
// Constant data
//

-Z(CONST)DATA16_C,DATA16_ID,TLS16_ID,DIFUNCT,CHECKSUM=2400-33FF

// ---------------------------
// Code
//

-Z(CODE)CSTART,ISR_CODE,CODE16=2400-33FF


// -------------------------------------
// All memory 0-FFFFF
//

// ---------------------------
// Code
//

-P(CODE)CODE=2400-33FF

// ---------------------------
// Constant data
//

-Z(CONST)DATA20_C,DATA20_ID,CODE_ID=2400-33FF


// -------------------------------------
// Interrupt vectors
//

-Z(CODE)INTVEC=4380-43FF
-Z(CODE)RESET=43FE-43FF

将改动后的xcl文件放在工程的根目录,其它目录也行,反正IAR可以改变链接器脚本文件所在的路径。然后在工程选项的Linker子选项卡中的Linker configuration file选项中的Override default选项勾上,然后填入更改后的xcl文件的路径,我这里是填是$PROJ_DIR$\lnk430f5529_ram.xcl,因为我将更改后的文件改名成lnk430f5529_ram.xcl然后放在工程的根目录了,也可以用右边的选择按钮选择绝对路径。

然后就是将调试器改为硬件仿真器然后下载调试了,进入调试状态后发现单片机没有停住,到反汇编窗口一看发现0xFFFE处的复位向量的值为0xFFFF,然后去看0x43FE处的值发现是正确的0x2400,如下图。

我在测试RAM调试之前先在FLASH中下载了一个P4.7每过1秒改变一次电平的程序,也即LaunchPad上绿灯闪烁的程序,当时调试时0xFFFE处的复位向量和上面软件仿真的一样,为0x4400,然而现在却不是了。理论上我将FLASH地址空间放到了RAM中是不会再去擦除FLASH的。但实际上仿真器却把FLASH给擦除了,于是我开始在工程选项的Debugger中寻找是否有与FLASH擦除的相关的选项,结果还真有

将这里的Flash erase选为Retain unchanged memory,这个选项是保留未改变内容的内存,其子选项中Compare with image on target和Compare with image cached on PC是差不多的选项,可以任选一个。这样的话就不会擦除原来的FLASH内容了。但是下载调试发现虽然FLASH中的程序没有被擦除,新的程序也被下载到RAM中,但是MCU还是运行的FLASH中的程序。

        再次回顾MSP430F5529的启动过程发现其实原因在于原来的0xFFFE处的复位向量的值并未改成正确的0x2400,还是使用FLASH时的0x4400,所以运行的还是FLASH中的程序,于是乎想了两种解决办法:

       1、将中断向量表仍然放在0xFF80-0xFFFF处,那样在下载程序时会更新0xFFFE处的复位向量,将其指向0x2400这个RAM地址,但却存在断电后再次上电,RAM中的程序消失后,MCU还是会去RAM中运行,而不是运行FLASH中的程序。

       2、在使用默认的xcl文件下载FLASH的程序时,在FLASH程序的main函数之前加一个函数。此函数判断0x43FE处“复位向量”是否为0x2400,如果是,则跳转到0x2400处执行RAM中的程序,否则继续运行FLASH中的main函数。

       显然,方法2更胜一筹,虽然需要在FLASH程序里多加一点内容。那么现在就是如何在main函数前加一个函数来判断了,通过在IAR的文档目录(X:\Program Files (x86)\IAR Systems\Embedded Workbench 7.2\430\doc)中寻找参考手册,发现了一个名为EW430_CompilerReference的pdf文件,在其书签的Part1.Using the compiler->The DLIB runtime environment->System startup and termination中找到了System Startup过程,如下图

我们所需关注的也就是被划红线的部分,这几部分主要说了系统在启动时会先去执行一个初始化序列,这个初始化序列在main函数之前执行,

初始化序列中有一个__low_level_init函数,这个函数如果用户定义了就会去执行,否则不会去执行这个函数,而处理启动和结束过程的代码在

IAR的目录X:\Program Files (x86)\IAR Systems\Embedded Workbench 7.2\430\src\lib这个目录里,而这个目录里貌似又有几个目录,通过筛

选,发现cstartup.s43和low_level_init.c在430\src\lib\430目录里,其中cstartup.s43为汇编文件,手册里说尽量不要修改这个文件,那么剩下的

就是low_level_init.c了,这个文件里只有一个__low_level_init函数,代码如下

#include <intrinsics.h>

int __low_level_init(void)
{
  /* Insert your low-level initializations here */

  /*
   * Return value:
   *
   *  1 - Perform data segment initialization.
   *  0 - Skip data segment initialization.
   */

  return 1;
}

那么将此文件拷贝到工程根目录,然后再工程中添加此文件,然后将此函数修改为如下代码

#include <intrinsics.h>

#define RUN_IN_RAM 1
#define NEW_RESET (*(volatile unsigned int*)0x43FE)

int __low_level_init(void)
{
    if(NEW_RESET == 0x2400)
    {
        if(!RUN_IN_RAM)
            asm("MOV #2400h, PC");
    }     
  return 1;
}


这里定义RUN_IN_RAM这个宏的原因是,在编译即将在RAM中调试的代码时也会编译low_level_init.c这个文件,于是RAM中的代码也会含有__low_level_init函数,当FLASH中的__low_level_init函数检测到0x43FE处的值为0x2400时会跳到0x2400这个RAM地址处
继续执行代码,而接下来就是执行RAM中的__low_level_init代码了,如果不判断现在是不是在RAM中运行,程序又会跳转到0x2400处执行代码,如此一来就形成了死循环。所以当下载到FLASH中时RUN_IN_RAM为0,会跳转,而下载到RAM中时RUN_IN_RAM为1,不会跳转。这个函数跳转使用了汇编代码"MOV #2400h,  PC",也即将PC的值设置为0x2400,如果不懂汇编的话那就没法了,反正跳转基本得这么写因为C语言无法使用PC寄存器,只有汇编能使用。

       然后就是编译下载了,先用默认的xcl文件在FLASH里下载一次,然后用修改后的xcl文件下载到RAM里面运行,但结果还是失败了,下载到RAM的程序仍然没有运行,还是运行的FLASH的程序。

       经过调试,发现FLASH里的low_level_init函数在判断NEW_RESET(地址为0x43FE)是否为0x2400时的结果为否,然而用Go to跳转到0x43FE处却发现显示的值是正确的0x2400(PS:这里由于我看的是IAR的反汇编窗口,其内存刷新好像比较慢,所以还是显示的正确值,但其实已经被修改了),后来使用中间变量查看程序读取到的NEW_RESET值,发现是0。

      然后又调试了一会,突然一次调试的时候发现0x43FC处的值为0x4408,而0x4408正好是FLASH程序中调用low_level_init程序后的代码的地址,准确来说应该是0x004408,这样一来0x43FE处为0也就很好解释了,因为FLASH中的程序将堆栈指针SP设置在0x4400处,当调用low_level_init时MCU自动压栈,也即将下一句代码的地址0x004408存在0x43FC处,注意,这里存的是32位数据,所以如果按8位表示的话,0x43FC处存的是0x08,0x43FD处存的是0x44,0x43FE处存的0x00,0x43FF处存的是0x00,以小端模式存储。

      既然知道了是因为压栈而导致的复位向量被覆盖,那么在编译FLASH的程序时将RAM空间定义为0x2400-0x437F即可成功避开RAM中的中断向量表,考虑到中断向量表的0x80-0xD0处并没有中断,那么将RAM的结尾定位0x43CF也没问题(PS:虽然0xD0和0xD1没有放数据,但还是不能将RAM结尾定为0x43D2,因为堆栈要4字节对齐,0x43D2并不满足4字节对齐的条件)。

      修改默认的xcl文件中的RAM地址空间为0x2400-0x437F,然后将链接器配置文件选择为此文件,在FLASH中下载一遍,然后使用修改的适用于RAM的xcl文件再下载到RAM中,发现果然可以运行RAM中的程序了,不过要运行FLASH中的程序必须得断电几秒后再上电才能运行,按复位是没用的,还是会运行RAM中的程序,原因在于MSP430复位时并不会清空RAM,断电几秒的原因在于MSP430功耗太低,且工作电压最低可到1.8V,只有电压下降到1.8V以下RAM才会清空。

       至于带有中断的程序,只需要在你的main函数的关闭看门狗的代码后加一句SYSCTL |= SYSRIVECT,SYSCTL寄存器的SYSRIVECT位的功能如下图所示:

红线处说明了SYSRIVECT位如果为1则使用RAM的顶端所存放的中断向量表,RAM顶端也即0x43FF处,向下减去中断向量表的长度0x7F后为0x4380,所以我上面才会把中断向量表的地址空间定为0x4380-0x43FF。是为了在RAM中调试时可以正常调试带有中断的程序。我测试了下IO口中断,可以正常识别。

       下面放上工程文件,传送门:MSP430 RAM调试.zip_免费高速下载|百度网盘-分享无限制

       那么关于在MSP430的RAM中调试程序的研究就结束了。

       END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值