简单解析"/>
Keil MDK 4.23 仿真 STM32F4 配置总结与简单解析
这里的仿真是指电脑仿真,而不是在线仿真。
最近刚刚入门STM32F4系列,在MDK仿真问题上卡住了一段时间。查帖子,有的人说4.23不可以但高版本可以,有的人说高版本也不行,也有的人说4.23就能仿真。
我这里用的是MDK4.23实现STM32F407VET6的电脑仿真,不过其他型号STM32F4芯片、更高版本MDK应该也可以。
下面就从一个简单工程开始说明配置过程,图多也略繁琐,建议高手跳着看。
另外只是寻找仿真配置方式的读者可以直接跳到末尾看结论。
下文包含的内容:
1)建立简单的示例工程
2)解决第一个问题:仿真起始地址不对同时读权限错误
3)解决第二个问题:仿真出现HardFault
4)解决第三个问题:运行过程中地址读写权限错误
5)总结
- - - - - - - - - - - - - - - - 华丽丽的分割线 - - - - - - - - - - - - - - - -
建立工程的过程很基本,就不多说了。
我们只是建立一个最简单的工程。需要的文件,除了MDK会自动添加Startup汇编代码之外,还有
stm32f4xx.h
system_stm32f4xx.c
system_stm32f4xx.h
main.c
main.c用户程序也写得很简单,基本的程序框架。
这时候看看默认的项目配置,Alt + F7,或者Project->Options for target "xxx"
在Target这一页,下方定义了各个段的地址和大小。IROM就是程序指令存放的位置了,IRAM是片上内存。
地址和大小应该是根据具体芯片样本的,如下图
这里样本中Flash就对应IROM,存放程序代码;样本中SRAM对应IRAM。
另外能看到,MDK配置中另一个IRAM是对应了CCM data RAM。
Linker页。这里会配置如何链接各obj文件来生成目标文件。这里也有区段的配置,不过默认是与前面Target页的配置保持一致。
Debug页,电脑仿真要选择左边Use Simulator,看情况如果从Startup开始调试,就可以取消复选框Run to main.
编译没有问题。
按Ctrl + F5开始仿真,这时候就遇到问题了,提示没有读权限。
- - - - - - - - - - - - - - - - 华丽丽的分割线 - - - - - - - - - - - - - - - -
这时在工程目录里创建一个debug.ini文件(实际上文件名任意),输入内容
map 0x00000000,0x00010000 read
这句话是说,把芯片的地址0x00000000 ~ 0x00010000这段区域设置成可读的。
然后在Debug配置中引用这个初始化文件。
这时候再启动调试,应该不会报错了,但还是有问题:
从反编译窗口可以看出两个问题:
1)起始地址是0x0000 0000,这不是我们想要的地址。
2)在这个地址上,很大部分是0,即内容是空的。
如果这时候看看应该存放代码的区段,从0x0800 0000开始的内容:
这里是有内容的。
所以现在要做的,就是改变仿真CPU开始执行的地址。
方法还是通过初始化文件。修改前面那个debug.ini
map 0x00000000,0x00010000 read
FUNC void Setup (void) {
SP = _RDWORD(0x08000000);
PC = _RDWORD(0x08000004);
_WDWORD(0xE000ED08, 0x08000000);
}
Setup();
这段可以从MDK自带的例子中找到,地址是<Keil>\ARM\Boards\ST\STM32F4-Discovery\Blinky\Dbg_RAM.ini
(顺便说一句,遇到问题多看demo还是很有用的)
不过这个Dbg_RAM.ini文件没法直接用,我修改了一下。
说明:
在代码段一开始就是中断向量表。从startup就能看出来:
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
......
后面还有很多。第一个DWORD是栈顶指针,就是说CPU开始运行后栈寄存器SP应该指向的地方;第二个DWORD是Reset Handler地址,就是程序开始执行的地址。
所以初始化文件debug.ini中在仿真开始时执行Setup()过程,把代码段0x0800 0000开始的第一个DWROD赋值给SP,把第二个DWORD赋值给PC,这样程序就可以从头开始执行了。
第三行_WDWORD(0xE000ED08, 0x08000000);
是对0xE000ED08这个寄存器赋值0x08000000。这个寄存器是SCB_VTOR,存放的是中断向量表的表头地址。
修改过debug.ini,按Ctrl + F5仿真
这样就可以了吗?按F11步进,发现又出错了。
这次不是MDK出错,而是仿真的CPU直接跳到了HardFault_Handler,就是说CPU认为出现了HardFault。
这个地方我好久才发现问题。原来是xPSR寄存器的初始值有问题。
PSR在ST官网的Cortex-M3 programming manual文档中有描述。
图3的APSR、IPSR和EPSR合并起来就是PSR寄存器。问题出在EPSR的24位T位上。这个位应该总是1,不是1就会出错。
所以要在初始化文件debug.ini中加入相应的初始赋值。同时,由于地址段0x00000000开始的那部分不再访问了,所以可以丢掉最初的map指令。
现在这个初始化文件debug.ini变成了
FUNC void Setup (void) {
xPSR = 1<<24;
SP = _RDWORD(0x08000000);
PC = _RDWORD(0x08000004);
_WDWORD(0xE000ED08, 0x08000000);
}
Setup();
多了xPSR = 1<<24;这一行对xPSR赋值。这下可以开始仿真了,按F11一步一步都能跑。
后面又遇到了问题,跑到SystemInit()函数里面:
提示0x4000 0000开始的一个地址段内访问错误。看样本地址空间说明,能看到这一段是外设(Peripherals)的地址空间。对UART、SPI、Timer等访问时就会访问到这个地址段。
同样会访问到的还有Cortex-M4's internal peripherals段、FSMC registers段等等,根据实际情况可以加入相应的map地址空间权限映射。
比如只增加Peripheral和Cortex-M4's internal peripherals,可以加入下面的指令:
map 0x40000000, 0x40007FFF // APB1
map 0x40010000, 0x400157FF // APB2
map 0x40020000, 0x4007FFFF // AHB1
map 0x50000000, 0x50060BFF // AHB2
map 0x60000000, 0xA0000FFF // AHB3
map 0xE0000000, 0xE00FFFFF // CORTEX-M4 internal peripherals
这样完整的初始化文件debug.ini是:
map 0x40000000, 0x40007FFF read write // APB1
map 0x40010000, 0x400157FF read write // APB2
map 0x40020000, 0x4007FFFF read write // AHB1
map 0x50000000, 0x50060BFF read write // AHB2
map 0x60000000, 0xA0000FFF read write // AHB3
map 0xE0000000, 0xE00FFFFF read write // CORTEX-M4 internal peripherals
FUNC void Setup (void) {
xPSR = 1<<24;
SP = _RDWORD(0x08000000);
PC = _RDWORD(0x08000004);
_WDWORD(0xE000ED08, 0x08000000);
}
Setup();
现在可以顺利执行到用户代码init()了。
- - - - - - - - - - - - - - - - 华丽丽的分割线 - - - - - - - - - - - - - - - -
总结:
仿真STM32F4时,系统只是简单加载了程序文件,而没有做一下关键的几个初始化:
1)根据仿真文件设置PC和SP初始值
2)设置xPSR初始值
3)设置地址空间的权限。
所以我们需要建立初始化文件来完成上述三个关键初始化。初始化文件代码我在这里重复贴一遍:
map 0x40000000, 0x40007FFF read write // APB1
map 0x40010000, 0x400157FF read write // APB2
map 0x40020000, 0x4007FFFF read write // AHB1
map 0x50000000, 0x50060BFF read write // AHB2
map 0x60000000, 0xA0000FFF read write // AHB3
map 0xE0000000, 0xE00FFFFF read write // CORTEX-M4 internal peripherals
FUNC void Setup (void) {
xPSR = 1<<24;
SP = _RDWORD(0x08000000);
PC = _RDWORD(0x08000004);
_WDWORD(0xE000ED08, 0x08000000);
}
Setup();
代码中map命令用来设置地址空间权限,Setup过程用来对CPU寄存器赋初始值。
更多推荐
Keil MDK 4.23 仿真 STM32F4 配置总结与简单解析
发布评论