先说下我所使用的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
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
#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口中断,可以正常识别。
上一篇:程序跑飞原因分析
下一篇:STM8L和MSP430的低功耗对比
推荐阅读最新更新时间:2024-03-16 16:01
设计资源 培训 开发板 精华推荐
- 柔灵科技陈涵:将小型、柔性的脑机接口睡眠设备,做到千家万户
- 微灵医疗李骁健:脑机接口技术正在开启意识与AI融合的新纪元
- USB Type-C® 和 USB Power Delivery:专为扩展功率范围和电池供电型系统而设计
- 景昱医疗耿东:脑机接口DBS治疗技术已实现国产替代
- 首都医科大学王长明:针对癫痫的数字疗法已进入使用阶段
- 非常见问题解答第223期:如何在没有软启动方程的情况下测量和确定软启动时序?
- 兆易创新GD25/55全系列车规级SPI NOR Flash荣获ISO 26262 ASIL D功能安全认证证书
- 新型IsoVu™ 隔离电流探头:为电流测量带来全新维度
- 英飞凌推出简化电机控制开发的ModusToolbox™电机套件
- 意法半导体IO-Link执行器电路板为工业监控和设备厂商带来一站式参考设计