3.找到关键性操作SFR(特殊功能寄存器)
WTCON(0xE2700000),其中bit5是看门狗开关:0代表关,1代表开
4.写代码关开门狗
#define WTCON 0xE2700000
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
//关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =(0<<5)
str r1, [r0]
原因:
@单片机中由硬件初始化时提供了一个默认可用的栈。
@在应用程序中我们编写的C程序其实并不是全部,编译器(gcc)在链接的时候会帮我们自动添加一个头,这个头就是一段引导我们的C程序能够执行的一段汇编实现的代码,
这个代码中就帮我们的C程序设置了栈及其他的运行时需要。@我们如何访问SVC模式下的sp呢?很简单,先把模式设置为SVC,再直接操作sp.
注:因为我们复位后就已经是SVC模式了,所以直接设置sp即可。
因此我们只能在SRAM中找一段内存来作为SVC的栈。
如图:
在ARM中,ATPCS(ARM关于程序应该怎么实现的一个规范)要求使用满减栈,所以不出意外都是用满减栈。
结合iROM_application_note中的memory map,可知SVC栈应该设置为0xD0037D80而不是0xD0037780
补充:
栈有4种:满减栈 满增栈 空减栈 空增栈
进栈时 | 出栈时 | |
满减栈 | 指针向下移动 -> 存数据 | 出数据 -> 指针向上移动 |
满增栈 | 指针向上移动 -> 存数据 | 出数据 -> 指针向下移动 |
空减栈 | 存数据 -> 指针向下移动 | 指针向上移动 -> 出数据 |
空增栈 | 存数据 -> 指针向上移动 | 指针向下移动 -> 出数据 |
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
//关看门狗(向WTCON的bit5写入0即可)
ldr r0,= WTCON
ldr r1,= (0<<5)
str r1,[r0]
//设置SVC栈,实现汇编与C的相互调用
ldr sp ,= SVC_STACK
//接下来就可以直接调用C程序了
bl Cfunction
用C语言来访问内存,就要用到指针;
格式:
unsigned int *p = (unsigned int *)0xE0200240;2.
代码如下:
start.S文件:
#define WTCON 0xE2700000
#define SVC_STACK 0xD0037D80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
//关看门狗(向WTCON的bit5写入0即可)
ldr r0,= WTCON
ldr r1,= (0<<5)
str r1,[r0]
//设置SVC栈,实现汇编与C的相互调用
ldr sp,= SVC_STACK
//接下来就可以直接调用C程序了
b czg_led
Makefile文件:
led.bin: start.o led.o
arm-linux-ld -Ttext 0x0 -o led.elf $^ #代码段运行地址为0x0,将所有依赖文件链接为led.elf
arm-linux-objcopy -O binary led.elf led.bin #将led.elf复制一份为led.bin文件
arm-linux-objdump -D led.elf > led_elf.dis #将led.elf文件转换为.dis反汇编文件
gcc mkv210_image.c -o mkx210
./mkx210 led.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c -nostdlib
%.o : %.c
arm-linux-gcc -o $@ $< -c -nostdlib
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
led.c文件:
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
void delay(void){
unsigned int i = 900000;
while(i--);
}
void czg_led(void){
rGPJ0CON = (0x111<<12); // 将三个引脚设置成out模式
while(1){
rGPJ0DAT = (0<<3)|(1<<4)|(1<<5);
delay();
rGPJ0DAT = (1<<3)|(0<<4)|(1<<5);
delay();
rGPJ0DAT = (1<<3)|(1<<4)|(0<<5);
delay();
}
}
3.关于volatile解释:
http://blog.csdn.net/czg13548930186/article/details/52454032
从速度来说:CPU > 寄存器 > cache > DDR
cache的存在,是因为寄存器和DDR之间速度差异太大,DDR的速度远不能满足寄存器的需要(不能满足CPU的需要,所以没有cache会拉低整个系统的整体速度)3、210内部有32KB icache和32KB dcache。
icache用来缓存指令的;
dcache是用来缓存数据的。
4、cache的意义:
指令平时是放在硬盘/flash中的,运行时读取到DDR中,再从DDR中读给寄存器,再由寄存器送给CPU。但是DDR的速度和寄存器(代表的就是CPU)相差太大,如果CPU运行完一句再去DDR读取下一句,那么CPU的速度完全就被DDR给拖慢了。
解决方案就是:icache.3.查阅ARM手册中CP15寄存器的相关部分
1、ARM处理器中CP15协处理器的寄存器,如下图所示:
2、由上图可知,CP15包括了16个寄存器,其中C1寄存器是控制寄存器,主要用于:
(1)禁止/使能MMU以及其它与存储系统有关的功能
(2)配置存储系统以及ARM处理器相关的工作
C1寄存器的位定义,如下图所示:
注:由上图可知,bit12用于开关iCache,其中0代表关闭iCache,1代表开启iCache。
3.代码
start.S文件:
#define WTCON 0xE2700000
#define SVC_STACK 0xD0037D80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
//关看门狗(向WTCON的bit5写入0即可)
ldr r0,= WTCON
ldr r1,= (0<<5)
str r1,[r0]
//设置SVC栈,实现汇编与C的相互调用
ldr sp,= SVC_STACK
//开关icache
mrc p15,0,r0,c1,c0,0 //读出cp15的c1到r0中
bic r0, r0, #(1<<12) //bit12 清0 关icache
orr r0, r0, #(1<<12) //bit12 置1 开icache
mcr p15,0,r0,c1,c0,0 //将r0写入cp15中的c1中
//接下来就可以直接调用C程序了
b czg_led
直接使用BL0中对icache的操作 | 手动关icache | 手动开icache | |
现象(结论) | 速度不变,且和手动开icache相同,证明BL0初始化了icache | 速度明显变慢 | 速度不变 |