完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
请来访的读者注意:如果你没读过本章笔记,请你忽略这一黑块里的总结。这一黑块是我多次阅读本笔记的总结。你可
以直接从本章“第01节 概念引入与处理流程”开始读。 每读一遍往往有得: 1、处理und、swi异常无非就是那几个步骤,两者相同 2、处理中断,中断是一种特殊的异常。在本笔记示例中,und、swi异常是你故意加一句错误语句让程序错误。 而中断不然,中断的处理函数比较复杂,你得分辨中断源,然后处理中段,还得清中断。 并且你得对中断控制器(也即mask寄存器)和中断源(比如按键配置成中断)进行初始化。 3、本章重要内容,我想两点吧,第一是异常的机制,即第4节(第5节其实和第4节一样);第二点是中断的机制,标准 的中断编程比直接看本章的最后代码即可,即把中断处理函数用一个注册函数指针数组保存起来,以后用的时候很方便。 对这种中断注册编程进行总结: 1、程序从start.S的 _start: b reset /* vector 0 : reset */ 然后执行 reset: 然后 ldr pc, =main /* 绝对跳转, 跳到SDRAM */ 2、然后看主函数: int main(void) { key_eint_init(); /* ①初始化按键,②初始化相应的中断控制器相应的EINTMASK去使能相应 的位,③把key_eint_init()注册到一个函数指针数组里*/ timer_init(); /* ①初始化时钟,②初始化相应的中断控制器相应的EINTMASK去使能相应 的位,③把timer_init()注册到一个函数指针数组里*/ /*注册函数: void register_irq(int irq, irq_func fp) { irq_array[irq] = fp; INTMSK &= ~(1< /*比如: register_irq(0, key_eint_irq); 就表示,一旦外部来0中断,那么就会跳到key_eint_irq()函数*/ 3、一旦发生中断信号,那么汇编start.S文件中就会跳到中断处理函数,handle_irq_c() void handle_irq_c(void) { /* 分辨中断源 */ int bit = INTOFFSET; /* 调用对应的处理函数执行 */ irq_array[bit](bit);/*要知道,你前面注册过了,来了哪个中断信号,就会处理相应的函数 (比如对于本章的按键中断示例来说,那就是key_eint_irq())*/ /* 清中断 : 从源头开始清 */ SRCPND = (1< 4、我在练习中断编程的时候遇到的坑: 我的友善之臂的四个按键为: k1 - GPG0 - EINT8; k2 - GPG3 - EINT11; k3 - GPG5 - EINT13; k4 - GPG6 - EINT14; 而在void key_eint_irq(int irq)函数中, /*先把这个值 读 出来,清的时候我再把他写进去。达到了清EINTPEND的目的*/ unsigned int val1 = EINTPEND; if (irq == 5) //注意:EINT8、EINT11、EINT13、EINT14都包含在INTOFFSET的bit5,即这四者的irp共为5!!! { 然后使用EINTPEND区分是四者中的哪一个。区分方法为: if(val1 & (1<<8)){//注意:不是if(val1 == 8)这种语法!!!! } if(val1 & (1<<11)){ } if(val1 & (1<<13)){ } if(val1 & (1<<14)){ } } 然后: EINTPEND = val1;//即清中断(恢复原本的EINTPEND值) 4、我在练习中断编程的时候遇到的坑二: 第四点中的 第01节 概念引入与处理流程 先来取个场景解释中断概念。 假设有个大房间里面有小房间,婴儿正在睡觉,他的妈妈在外面看书。问:这个母亲怎么才能知道这个小孩醒? 第一种方法:过一会打开一次房门,看婴儿是否睡醒,让后接着看书 第二种方法:一直等到婴儿发出声音以后再过去查看,期间都在读书 第一种 叫做查询方式: 优点:简单 缺点: 累 写程序表示这种方式: while(1) { 1 read book(读书) 2 open door(开门) if(睡) return(read book) else 照顾小孩 } 第二种叫中断方式: 优点:不累 缺点:复杂 写程序: while(1) { read book 中断服务程序()//如何被调用? { 处理照顾小孩 } } 我们看看母亲被小孩哭声打断如何照顾小孩? 母亲的处理过程: 1 平时看书 2 发生了各种声音,如何处理这些声音 ①有远处的猫叫(听而不闻,忽略) ②门铃声有快递(开门收快递) ③小孩哭声(打开房门,照顾小孩) 3 母亲的处理 故母亲只会处理门铃声和小孩哭声处理过程如下: a 现在书中放入书签,合上书(保存现场) b 去处理 (调用对应的中断服务程序) c 继续看书(恢复现场) 不同情况,不同处理: a 对于门铃:开门取快件 b 对于哭声:照顾小孩 我们将母亲的处理过程抽象化——母亲的头脑相当于CPU 耳朵听到声音会发送信号给脑袋,声音来源有很多种,有远处的猫叫,门铃声,小孩哭声。这些声音传入耳朵,再由耳朵传给大脑,除了这些可以中断母亲的看书,还有其他情况,比如身体不舒服,有只蜘蛛掉下来,对于特殊情况无法回避,必须立即处理。对比我们的arm系统: a 有CPU,有中断控制器。 b 中断控制器可以发信号给CPU告诉它发生了那些紧急情况 c 中断源有按键、定时器、有其它的(比如网络数据) 这些信号都可以发送信号给中断控制器,再由中断控制器发送信号给CPU表明有这些中断产生了,这些成为中断(属于一种异常)。 而异常就像是有一只蜘蛛掉下来,属于特殊情况无法回避,需要中断CPU,必须处理。比如指令不对,数据访问有问题。又reset信号,这些都可以中断CPU 这些成为异常中断。 母亲处理对比我们的arm系统如下图: 异常中断的重点在于保存现场以及恢复现场 异常中断处理过程 a 保存现场(各种寄存器) b 处理异常(中断属于一种异常) c 恢复现场 arm对异常(中断)处理过程 1 初始化: a 设置中断源,让它可以产生中断 b 设置中断控制器(可以屏蔽某个中断,优先级) c 设置CPU总开关,(使能中断) 2 执行其他程序:正常程序 3 产生中断:按下按键—>中断控制器—>CPU 4 cpu每执行完一条指令都会检查有无中断/异常产生 5 发现有中断/异常产生,开始处理。对于不同的异常,跳去不同的地址执行程序。这地址上,只是一条跳转指令,跳去执行某个函数(地址)(对于下面的程序,发生中断,就跳去0x18地址去执行某个函数(地址)_irq),这个就是异常向量。如下就是异常向量表,对于不同的异常都有一条跳转指令。 (注:3-5步都是硬件强制自行做的) .globl _start _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq //发生中断时,CPU跳到这个地址(假设地址为0x18),执行该指令 ldr pc, _fiq //我们先在0x18这里放 ldr pc ,__irq,于是cpu最终会跳去执行__irq代码 //保护现场,调用处理函数,恢复现场 6 这些函数(地址)(如_irq)做什么事情? 软件做的: a 保存现场(各种寄存器) b 处理异常(中断): b.a 分辨中断源 b.b 再调用不同的处理函数 c 恢复现场 (对比母亲的处理过程来比较arm中断的处理过程。) 中断处理程序怎么被调用? CPU—>0x18 --跳转到其他函数->做保护现场->调用函数(分辨中断源、调用对应函数)->恢复现场 cpu到0x18是由硬件决定的,跳去执行更加复杂函数(由软件决定)。 第02节 CPU模式(Mode)、状态(State)和寄存器 7种Mode: usr/sys undefined(und) Supervisor(svc) Abort(abt) IRQ(irq) FIQ(fiq) 2种State: ARM state Thumb state 寄存器: 通用寄存器 备份寄存器(banked register) 当前程序状态寄存器(Current Program Status Register);CPSR CPSR的备份寄存器:SPSR(Save Program Status Register) 我们仍然以这个母亲为例讲解这个CPU模式: 这个母亲无压力看书 -->(正常模式) 要考试,看书—>(兴奋模式) 生病---->(异常模式) 对于ARM CPU有7种模式: 1、 usr :用户模式,类比 正常模式 2、 sys :系统模式,类比兴奋模式 3、 五种异常模式:(2440用户手册72页) ① und :未定义模式(CPU执行时遇到某条指令不认识时为此异常) ② svc :管理模式 ③ abt :终止模式 a 指令预取终止(读写某条错误的指令导致终止运行,即CPU执行某条命令时,已在解析下一条指令,预取下下一条指令,而这个预取可能会出错,导致“指令预取终止”)。 b 数据访问终止 (读写某个地址,这个过程出错)。 这两者都会进入终止模式。 ④ IRQ: 中断模式 ⑤ FIQ: 快中断模式(快速处理中断) 我们可以称以下6种为特权模式(privileged mode) [tr].6种特权模式[/tr]
而上面的6种特权模式可以随便切换,可以编程去操作CPSR寄存器直接进入其他模式。这些异常模式是为了更好的处理相应的异常,每一种异常模式的差别在于CPU寄存器的差别,查看2440手册: 这个图是有关各个模式下所能够访问的寄存器,再讲这个图之前我们先引入 2种state。 CPU有两种state: 1 ARM state:使用ARM指令集,每个指令4byte 2 Thumb state:使用的是Thumb指令集,每个指令2byte 比如同样是: mov R0, R1 编译后 对于ARM指令集,上面的这条汇编指令编译成的机器码要占据4个字节; 对于Thumb指令集,上面的这条汇编指令编译成的机器码要占据2个字节。 因此,引入Thumb可以减少存储空间。本章第3节会演示使用Thumb指令集编译,看是否生成的bin文件会变小很多。 ARM指令集与Thumb指令集的区别: Thumb 指令可以看作是 ARM 指令压缩形式的子集,是针对代码密度的问题而提出的,它具有 16 位的代码密度,但是它不如ARM指令的效率高 。 Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集。 因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集,比如,所有异常自动进入 ARM 状态。在编写 Thumb 指令时,先要使用伪指令 CODE16 声明,而且在 ARM 指令中要使用 BX指令跳转到 Thumb 指令,以切换处理器状态,编写 ARM 指令时,则可使用伪指令 CODE32声明。 好,理解了CPU的state之后我们来继续看这个图:ARM State General Registers and Program Counter即ARM状态通用寄存器和程序计数器。 上图,CPU在每种模式下都有R0 ~ R15。 1、在这张图注意到有些寄存器画有灰色的三角形,表示访问该模式下访问的专属寄存器,比如FIQ模式的r8_fiq寄存器。FIQ模式的r8_fiq寄存器和 System 模式下的r8是两个在物理性质上不同的寄存器。 2、不带灰色的三角形的寄存器是各种模式下的通用寄存器,比如r0,是这7种模式共用的寄存器,是同一个物理上的寄存器。 比如: mov R0, R8 在System 模式下访问的是R0 ~ R8,在所有模式下访问R0都是同一个寄存器。 mov R0,R8_fiq 但是在FIQ模式下,访问R8是访问的FIQ模式专属的R8寄存器,不是同一个物理上的寄存器。 在这五种异常模式中每个模式都有自己专属的R13 R14寄存器,R13用作SP(栈), R14用作LR(返回地址) ,LR是用来保存发生异常时的指令地址。 为什么快中断(FIQ)有那么多专属寄存器(这些寄存器称为备份寄存器)。 解答这个问题,先回顾一下中断的处理过程 1 保存现场(保存被中断模式的寄存器)。 就比如说我们的程序正在系统模式/用户模式下运行,当你发生中断时,需要把R0 ~ R14这些寄存器全部保存下来,让后处理异常,最后恢复这些寄存器。 但如果是快中断,那么我就不需要保存 系统/用户模式下的R8 ~ R12这几个寄存器,在FIQ模式下有自己专属的R8 ~ R12寄存器,省略保存寄存器的时间,加快处理速度。但是注意,在Linux中并不会使用FIQ模式。 2 处理。 3 恢复现场。 上面这个图片还说到了CPSR、SPSR寄存器,CPSR是当前程序状态寄存器,这是一个特别重要的寄存器。SPSR保存的程序状态寄存器,他们二者的格式如下: 上图中的解释: 1、首先 M4 ~ M0 表示当前CPU处于哪一种模式(Mode);我们可以读取这5位来判断CPU处于哪一种模式,也可以修改这一种模式位,让其修改这种模式; 2、假如你当前处于用户模式下,是没有权限修改这些位的; 3、M4 ~ M0对应什么值,在2440手册中有说明: 4、要知道,CPU只有一个CPSR寄存器,所以从一个模式到另一个模式时,要对CPSR寄存器进行设置。 查看上上一副图中的其他位(即除了M4~M0位): 1、Bit5 State bits表示CPU工作用Thumb State还是ARM State指令集。 2、Bit6 FIQ disable当bit6等于1时,FIQ是不工作的。 3、Bit7 IRQ disable当bit5等于1时,禁止所有的IRQ中断,这个位是IRQ的总开关。 4、Bit8 ~ Bit27是保留位。 5、Bite28 ~ Bit31是状态位, 什么是状态位呢,比如说执行一条指令: cmp R0, R1 如果R0 等于 R1 那么zero位等于1,这条指令影响 Z 位,即如果R0 == R1,则Z = 1。 beq跳转到xxx这条指令会判断Bit30(即Z)是否为1,是1的话则跳转,不是1的话则不会跳转 使用 Z 位,如果 Z 位等于1 则跳转,这些指令是借助状态位实现的。 SPSR保存的程序状态寄存器: 表示发生异常时这个寄存器会用来保存被中断的模式下他的CPSR。 就比如我的程序在system系统模式下运行的 CPSR寄存器是某个值,当发生中断时会进入irq模式,这个SPSR_irq就保存系统模式下的CPSR的值。 现在,我们来看看发生异常时CPU是如何协同工作的: 进入异常的处理流程(硬件部分实现的),查看2440手册,如下图: 我们来翻译一下上面图片中的文字(硬件部分实现的): 发生异常时,我们的CPU会做什么事情: 1、把下一条指令的地址保存在LR寄存器里(某种异常模式的LR等于被中断模式的下一条指令的地址) 它有可能是PC + 4有可能是PC + 8,到底是那种取决于不同的情况。 2、 把CPSR(当前程序状态寄存器)保存在SPSR里面(某一种异常模式下SPSR里面的值等于CPSR) 3、 修改CPSR的模式为进入异常模式(即修改CPSR的M4 ~ M0进入异常模式)。 4 、跳到向量表。 退出异常怎么做?查看2440手册,如下图: 对上面图片文字进行翻译: 1、 让LR减去某个值,让后赋值给PC(即PC = 某个异常LR寄存器减去 offset)。 减去什么值呢(offset是什么呢)?也就是说,我们怎么返回去继续执行原来的程序(被中断模式),根据下面这个表来取值。 图表含义,比如: ①、如果发生的是SWI可以把 R14_svc复制给PC; ②、如果发生的是IRQ可以把R14_irq的值减去4赋值给PC。 2、 把CPSR的值恢复(CPSR 值等于 某一个异常模式下的SPSR)。 3、 清中断(如果是中断的话,对于其他异常不用设置)。 第03节 Thumb指令集程序示例 (注:本节并不重要,本节只是演示一下thumb指令集,在后续的学习中是用不到的。对于这一节,你不需要理解透彻,记住步骤即可。甚至不用会,以后用到的时候再来看就可以。 因为ARM的flash空间比较大,使用thumb节省内存的意义不大。一般flash比较小的单片机会比较重视这个)。 在上节视频里说ARMCPU有两种状态: 1、ARM State 每条指令会占据4byte; 2、Thumb State 每条指令占据2byte。 我们说过Thumb指令集并不重要,本节演示把一个程序使用Thumb指令集来编译它。 (本节代码见013_thumb_014_003文件夹) 使用上一章节的重定位代码,打开Makefile和Start.S。 以前的Makefile文件如下: all: arm-linux-gcc -c -o led.o led.c arm-linux-gcc -c -o uart.o uart.c arm-linux-gcc -c -o init.o init.c arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S #arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis clean: rm *.bin *.o *.elf *.dis 若使用Thumb指令集,对其进行更改: all: arm-linux-gcc -mthumb -c -o led.o led.c//只需要在arm-linux-gcc加上 mthumb命令即可 arm-linux-gcc -c -o uart.o uart.c arm-linux-gcc -c -o init.o init.c arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S #arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis clean: rm *.bin *.o *.elf *.dis 但是上面这种仅对led.c进行了使用thumb指令集,若全部使用还得一个一个改,麻烦。故进行如下改进: all: led.o uart.o init.o main.o start.o //all依赖led.o uart.o init.o main.o start.o #arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis clean: rm *.bin *.o *.elf *.dis %.o : %.c arm-linux-gcc -mthumb -c -o $@ $< //对于所有的.c文件使用规则就可以使用thumb指令集编译 $@表示目标 $<表示第一个依赖 %.o : %.S arm-linux-gcc -c -o $@ $< //.S文件不在这里改,而是在代码中改。 |
|
|
|
对start.S需要修改代码
原重定位章节Start.S文件: .text .global _start _start: /* 关闭看门狗 */ 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 main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr pc, =main /* 绝对跳转, 跳到SDRAM */ halt: b halt 对其更改,下面是使用thumb指令集的Start.S文件 .text .global _start /*更改处一------------------------------*/ .code 32 //表示后续的指令使用ARM指令集 _start: /* 关闭看门狗 */ 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] /* 恢复原来的值 */ /*更改处二------------------------------*/ /* 怎么从ARM State切换到Thumb State? */ adr r0, thumb_func //定义此标号的地址 add r0, r0, #1 /* bit0=1时, bx就会切换CPU State到thumb state */ bx r0 /*更改处三------------------------------*/ .code 16 //下面都使用thumb指令集 thumb_func: //需要得到这个标号的地址 /*下面就是使用thumb指令来执行程序*/ bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ /* 重定位text, rodata, data段整个程序 */ bl copy2sdram /* 清除BSS段 */ bl clean_bss //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr r0, =main /* 绝对跳转, 跳到SDRAM ,先把main的地址赋值给R0 */ mov pc, r0 /*让后再移动到PC*/ halt: b halt 上传代码编译测试,出现错误,如下: init.o(.text+0x6c):In function 'sdram_init2'; undefined reference to 'memcpy' 1 2 发现是init,o里sdram_init2使用的了memcpy函数的问题。查看init.c: #include "s3c2440_soc.h" void sdram_init(void) { BWSCON = 0x22000000; BANKCON6 = 0x18001; BANKCON7 = 0x18001; REFRESH = 0x8404f5; BANKSIZE = 0xb1; MRSRB6 = 0x20; MRSRB7 = 0x20; } #if 0 /************************************************************************** * 设置控制SDRAM的13个寄存器 * 使用位置无关代码 **************************************************************************/ void memsetup(void) { unsigned long *p = (unsigned long *)MEM_CTL_BASE; p[0] = 0x22111110; //BWSCON p[1] = 0x00000700; //BANKCON0 p[2] = 0x00000700; //BANKCON1 p[3] = 0x00000700; //BANKCON2 p[4] = 0x00000700; //BANKCON3 p[5] = 0x00000700; //BANKCON4 p[6] = 0x00000700; //BANKCON5 p[7] = 0x00018005; //BANKCON6 p[8] = 0x00018005; //BANKCON7 p[9] = 0x008e07a3; //REFRESH,HCLK=12MHz:0x008e07a3,HCLK=100MHz:0x008e04f4 p[10] = 0x000000b2; //BANKSIZE p[11] = 0x00000030; //MRSRB6 p[12] = 0x00000030; //MRSRB7 } #endif /*下面函数使用了memcpy函数,显然是编译器的操作,使用了memcpy把数组里的值从代码段拷贝到了arr局部变量里 是否可以禁用掉memcpy*/ void sdram_init2(void) { unsigned int arr[] = { 0x22000000, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x18001, //BANKCON6 0x18001, //BANKCON7 0x8404f5, //REFRESH,HCLK=12MHz:0x008e07a3,HCLK=100MHz:0x008e04f4 0xb1, //BANKSIZE 0x20, //MRSRB6 0x20, //MRSRB7 }; volatile unsigned int * p = (volatile unsigned int *)0x48000000; int i; for (i = 0; i < 13; i++) { *p = arr; p++; } } 查阅资料,资料文章中说没有什么方法禁用memecpy,但是可以修改这些变量。比如说将其修改为静态变量,这些数据就会放在数据段中,最终重定位时会把数据类拷贝到对应的arr地址里面去。即在unsigned int arr[]前加上const 和static。如下: void sdram_init2(void) { const static unsigned int arr[] = { //加上const 和static 0x22000000, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x18001, //BANKCON6 0x18001, //BANKCON7 0x8404f5, //REFRESH,HCLK=12MHz:0x008e07a3,HCLK=100MHz:0x008e04f4 0xb1, //BANKSIZE 0x20, //MRSRB6 0x20, //MRSRB7 }; volatile unsigned int * p = (volatile unsigned int *)0x48000000; int i; for (i = 0; i < 13; i++) { *p = arr; p++; } } 编译代码,得到的bin文件有1.4k左右。查看之前的文件使用ARM指令集是2K左右。从这里你可以看出thumb指令集的作用。我们再接着查看使用thumb指令集得到的反汇编代码,看看每条指令是否仅占用2个字节。看出地址30000074以后的指令都是占2个字节。 sdram.elf: file format elf32-littlearm Disassembly of section .text: /*前面这些ARM指令还是占用4个字节*/ 30000000 <_start>: 30000000: e3a00453 mov r0, #1392508928 ; 0x53000000 30000004: e3a01000 mov r1, #0 ; 0x0 30000008: e5801000 str r1, [r0] 3000000c: e3a00313 mov r0, #1275068416 ; 0x4c000000 30000010: e3e01000 mvn r1, #0 ; 0x0 30000014: e5801000 str r1, [r0] 30000018: e59f005c ldr r0, [pc, #92] ; 3000007c <.text+0x7c> 3000001c: e3a01005 mov r1, #5 ; 0x5 30000020: e5801000 str r1, [r0] 30000024: ee110f10 mrc 15, 0, r0, cr1, cr0, {0} 30000028: e3800103 orr r0, r0, #-1073741824 ; 0xc0000000 3000002c: ee010f10 mcr 15, 0, r0, cr1, cr0, {0} 30000030: e59f0048 ldr r0, [pc, #72] ; 30000080 <.text+0x80> 30000034: e59f1048 ldr r1, [pc, #72] ; 30000084 <.text+0x84> 30000038: e5801000 str r1, [r0] 3000003c: e3a01000 mov r1, #0 ; 0x0 30000040: e5910000 ldr r0, [r1] 30000044: e5811000 str r1, [r1] 30000048: e5912000 ldr r2, [r1] 3000004c: e1510002 cmp r1, r2 30000050: e59fd030 ldr sp, [pc, #48] ; 30000088 <.text+0x88> 30000054: 03a0da01 moveq sp, #4096 ; 0x1000 30000058: 05810000 streq r0, [r1] 3000005c: e28f0004 add r0, pc, #4 ; 0x4 30000060: e2800001 add r0, r0, #1 ; 0x1 30000064: e12fff10 bx r0 30000068 30000068: f94ef000 bl 30000308 3000006c: f9fef000 bl 3000046c 30000070: fa24f000 bl 300004bc /**下面的thumb指令占据2个字节**/ 30000074: 4805 ldr r0, [pc, #20] (3000008c <.text+0x8c>) 30000076: 4687 mov pc, r0 30000078 30000078: e7fe b 30000078 3000007a: 0000 lsl r0, r0, #0 3000007c: 0014 lsl r4, r2, #0 3000007e: 4c00 ldr r4, [pc, #0] (30000080 <.text+0x80>) 30000080: 0004 lsl r4, r0, #0 30000082: 4c00 ldr r4, [pc, #0] (30000084 <.text+0x84>) 30000084: c011 stmia r0!,{r0, r4} 30000086: 0005 lsl r5, r0, #0 30000088: 1000 asr r0, r0, #0 3000008a: 4000 and r0, r0 3000008c: 04fd lsl r5, r7, #19 3000008e: 3000 add r0, #0 综上所述,如果你的flash很小的话可以考虑使用Thumb指令集。Thumb指令集在后面的学习中没有任何作用,我们只是简单作为介绍。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1820 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1634 浏览 1 评论
1104 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
740 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1692 浏览 2 评论
1951浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
756浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
587浏览 3评论
605浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
570浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-31 03:34 , Processed in 0.809533 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号