完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1.异常向量表
我们先看一下芯片手册里面的异常向量表, 2.代码流程 我们在重定位的第10个程序的基础上进行修改。 我们要在start.S的前面增加跳转到相应异常向量表的代码,并且要增加代码,当发生未定义指令异常时候跳转到该代码块进行保存现场、处理未定义异常以及恢复现场的工作,然后在下面故意写一条不能被识别的指令。 2.1增加异常向量表代码 首先在start.S的前面增加相应的跳转指令,当发生异常时会根据这里的跳转指令跳转到相应的地方,代码如下。 _start: b reset /* vector 0 : reset */ b do_und /* vector 4 : und */ 2.2设置栈 进入异常模式之后,使用的异常模式下的栈,所以我们要在处理异常之前设置一下异常模式的栈,我们随便找一块没有用到的内存就可以了,这里我们让他指向64MSDRAM的最高地址0x34000000。 /* sp_und未设置, 先设置它 */ ldr sp, =0x34000000 2.3 保存现场 由于r0-r12都有可能被修改,因为我们要把r0-r12都保存一下,另外当发生异常时,当前被中断的地址回报存在LR寄存器里面,所以我们也要把LR寄存器保存一下,这里stmdb只是stm decrease before,也就是先减后存。 /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ /* lr是异常处理完后的返回地址, 也要保存 */ stmdb sp!, {r0-r12, lr} 2.4 处理异常 这里处理未定义异常的的工作我们通过编写一个C函数实现,我们新建一个exception.c,在里面编写我们的处理函数,exception.c内容如下: #include "uart.h" void printException(unsigned int cpsr, char *str) { puts("Exception! cpsr = "); printHex(cpsr); puts(" "); puts(str); puts("nr"); } 这个函数由两个参数,所以我们在start.S中调用这个函数时要传给他两个参数,在汇编代码中定义字符串可以使用.ascii和.string,他们两个的区别在于,用.ascii定义的字符串变量不会再末尾自动加上结束符 . /* 处理und异常 */ mrs r0, cpsr ldr r1, =und_string bl printException /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ und_string: .string "undefined instruction exception" 2.5 恢复现场 把r0-r12的值从栈中读出来,并且并原来保存在LR寄存器中的值赋值给PC,ldmia是指ldm increase after,也就是 先读后加。 /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ 2.6 故意加入一条未定义指令 我们看一下2440的芯片手册,找到ARM指令集,只要是下表中的指令都是合法的指令。我们需要定义一条下表中不支持的非法指令,或者直接用0xdeadc0de表示一条未定义指令。 /* 故意加入一条未定义指令 */und_code: .word 0xdeadc0de /* 未定义指令 */ 3.代码 3.1start.S 下面我们看一下修改后的完整的start.S。 .text .global _start _start: b reset /* vector 0 : reset */ b do_und /* vector 4 : und */ do_und: /* 执行到这里之前: * 1. lr保存有被中断模式中的下一条即将执行的指令的地址 * 2. SPSR保存有被中断模式的CPSR * 3. CPSR中的M4-M0被设置为11011, 进入到und模式 * 4. 跳到0x4的地方执行程序 */ /* sp_und未设置, 先设置它 */ ldr sp, =0x34000000 /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ /* lr是异常处理完后的返回地址, 也要保存 */ stmdb sp!, {r0-r12, lr} /* 保存现场 */ /* 处理und异常 */ mrs r0, cpsr ldr r1, =und_string bl printException /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ und_string: .string "undefined instruction exception" reset: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, [r0] /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ ldr r0, =0x4C000000 ldr r1, =0xFFFFFFFF str r1, [r0] /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ ldr r0, =0x4C000014 ldr r1, =0x5 str r1, [r0] /* 设置CPU工作于异步模式 */ mrc p15,0,r0,c1,c0,0 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA mcr p15,0,r0,c1,c0,0 /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m = MDIV+8 = 92+8=100 * p = PDIV+2 = 1+2 = 3 * s = SDIV = 1 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M */ ldr r0, =0x4C000004 ldr r1, =(92<<12)|(1<<4)|(1<<0) str r1, [r0] /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址, 再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str r1, [r1] /* 0->[0] */ ldr r2, [r1] /* r2=[0] */ cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ moveq sp, #4096 /* nand启动 */ streq r0, [r1] /* 恢复原来的值 */ bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ /* 重定位text, rodata, data段整个程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss bl uart0_init bl print1 /* 故意加入一条未定义指令 */ und_code: .word 0xdeadc0de /* 未定义指令 */ bl print2 //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr pc, =main /* 绝对跳转, 跳到SDRAM */ halt: b halt 3.2 exception.c #include "uart.h" void printException(unsigned int cpsr, char *str) { puts("Exception! cpsr = "); printHex(cpsr); puts(" "); puts(str); puts("nr"); } 4.优化 我们把前面异常向量表的跳转指令改为绝对跳转,这样保证如果是nandflash,并且代码大于4K,那么我们后面的bl printException也不会出错, _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ 这样修改之后还有一个问题,我们看一下反汇编, 它去300000c0这个地方去取值, 然后c0这个地方存在的30000008,30000008就是下面这条指令的地址。 其实就是do_und指令的地址保存到内存中,然后ldr pc, und_addr会去内存中找到这个值,然后付给pc, 那么他去内存的那个地方找这个值呢,一般由编译器决定,通常会放在汇编文件的最后面,我们这个start.S非常小,如果这个start.S非常大,那么他有可能会去4K之外的地方取值,所以我们能不能指定让他去前面的内存去读这个值呢,因为我们担心这个汇编文件很大的话,有可能超过4K,如果你的板子是nand启动,那么在4K之外就没有办法读到值。修改如下: _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ und_addr: .word do_und 同样下面的代码在重定位之后,我们就应该跳转到SDRAM去执行代码,所以增加ldr pc, =sdram ldr pc, =sdram sdram: bl uart0_init bl print1 /* 故意加入一条未定义指令 */ und_code: .word 0xdeadc0de /* 未定义指令 */ bl print2 //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr pc, =main /* 绝对跳转, 跳到SDRAM */ halt: b halt 修改后的start.S如下: .text .global _start _start: b reset /* vector 0 : reset */ ldr pc, und_addr /* vector 4 : und */ und_addr: .word do_und do_und: /* 执行到这里之前: * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址 * 2. SPSR_und保存有被中断模式的CPSR * 3. CPSR中的M4-M0被设置为11011, 进入到und模式 * 4. 跳到0x4的地方执行程序 */ /* sp_und未设置, 先设置它 */ ldr sp, =0x34000000 /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */ /* lr是异常处理完后的返回地址, 也要保存 */ stmdb sp!, {r0-r12, lr} /* 保存现场 */ /* 处理und异常 */ mrs r0, cpsr ldr r1, =und_string bl printException /* 恢复现场 */ ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */ und_string: .string "undefined instruction exception" .align 4 reset: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, [r0] /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ ldr r0, =0x4C000000 ldr r1, =0xFFFFFFFF str r1, [r0] /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ ldr r0, =0x4C000014 ldr r1, =0x5 str r1, [r0] /* 设置CPU工作于异步模式 */ mrc p15,0,r0,c1,c0,0 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA mcr p15,0,r0,c1,c0,0 /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m = MDIV+8 = 92+8=100 * p = PDIV+2 = 1+2 = 3 * s = SDIV = 1 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M */ ldr r0, =0x4C000004 ldr r1, =(92<<12)|(1<<4)|(1<<0) str r1, [r0] /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址, 再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str r1, [r1] /* 0->[0] */ ldr r2, [r1] /* r2=[0] */ cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ moveq sp, #4096 /* nand启动 */ streq r0, [r1] /* 恢复原来的值 */ bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ /* 重定位text, rodata, data段整个程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss ldr pc, =sdram sdram: bl uart0_init bl print1 /* 故意加入一条未定义指令 */ und_code: .word 0xdeadc0de /* 未定义指令 */ bl print2 //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr pc, =main /* 绝对跳转, 跳到SDRAM */ halt: b halt 修改后的程序整体执行过程如下: 修改后的程序整体执行过程如下: |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1627 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1550 浏览 1 评论
984 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1601 浏览 2 评论
1867浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
650浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
518浏览 3评论
536浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
506浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 12:18 , Processed in 0.834884 second(s), Total 79, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号