- 原理分析
- 主要流程
- 源码
硬件平台:jz2440
软件平台:Ubuntu16.04 arm-linux-gcc-3.4.5
源码位置: https://github.com/lian494362816/C/tree/master/2440/005_leds/002
1.原理分析
1.1问题的由来
前面篇文章 https://blog.youkuaiyun.com/lian494362816/article/details/84642425 提到了SP的设置,但是当使用nand flash 和 nor flash启动时,2440内部的sram位置是不同的, SP的设置也不同。使用nor flash时,内部sram起始位置为0x40000000, 而使用nand flash时,内部sram起始位置为0x00000000。为了更好的使用,需要让代码区分nand 还是nor启动。总不能准备2份代码, 一份给nor使用,一份给nand使用吧。
1.2 解决方法
编程时,可以根据nor flash的特性和2440在使用nand flash启动时的特性来区分是nor 启动还是nand启动。
- 如果是nor 启动, 2440会直接从nor flash 去读指令,如果是nand 启动,2440会把nand 前4K的内容拷贝到sram里面,从内部的sram去读取指令。
- nor flash是可以随意读取数据的,但是如果需要往nor flash里面写数据,则需要先发送一些特殊的命令才可以,所以说nor flash是可以随意读但是不可以随意写。但是内部的sram是可以随意读写的。
2主要流程
根据前面提到的2点,可以在程序一开始往0地址写一个数据(特殊一点即可,这里写0),再读取0地址的数据。
如果是nor 启动,应该会写失败(写必须先发特定命令),所以再次读取0地址的数据不等于之前所写的数据。
如果是nand启动,因为程序是在内部的sram运行(2440自动把nand 数据拷贝到sram),所以写是会成功的,再次读取0地址的数据就等于之前所写的数据。
3源码
start.s
.global _start
_start:
/* aoto switch nand or nor flash */
/* write 0 to address[0] and read it
* if address[0] is equal 0, it is nand
* because nand can wirte easy, but nor need send
* write-commond before wirte data, so write 0 to
* nor address[0] will fail, and get nor address[0]
* data is not equal 0
*/
mov r1, #0
ldr r0, [r1] /* r0=[0] store address[0] data*/
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2
ldr sp, =0x40000000 + 4096/* for nor */
ldreq sp, =4096 /* for nand */
streq r0, [r1] /* recover address[0] data */
/* stop watch dog */
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
bl main
loop:
b loop
汇编代码流程很简单
1)把R1赋值为0
2)将0地址数据保存到R0
3)往0地址写0
4)把0地址数据读到R2
5)SP先设置为0x40000000+ 4096
6)如果R2等于R1, 即R2=0,那么说明是nand启动, SP设置成4096
7)把R0的值写回0地址
这里不需要担心,万一程序一开始0地址的数据就是0,那么在往0地址写0再取出来,就会误被判定为成功。 程序最终会编译成机器码, 而机器码组成的指令有特定的格式,不会出现一条指令全是0组成。 通过反汇编可以知道,程序的第1条指令就是“ mov r1, #0”, 这条指令的机器码是e3a01000, 所以0地址的值,一开始不会等于0。
led_on.c
int main(void)
{
int i = 0;
/* clear mode */
GPFCON &= ~((3 << 8) | (3 << 10) | (3 << 12));
/* set to output mode */
GPFCON |= ((1 << 8) | (1 << 10) | (1 << 12));
/* all led off */
GPFDAT |= (7 << 4);
while (1)
{
GPFDAT |= (7 << 4);
GPFDAT &= ~(i << 4);
i++;
if (8 == i)
{
i = 0;
}
delay(100000);
}
return 0;
}
main函数是写做一个流水灯的功能, 具体的细节不说,因为今天主要讲的是区分nand flash 和nor flash。把这份代码分别烧到nand 和 nor 都可以正常启动,并让流程灯跑起来,但是会发现流水灯的执行速度不一样。
烧到nand 会执行的更快一点, 因为使用nand时,程序是跑在内部的sram里面,而使用nor时,程序要从外部的nor flash读取数据,因此会偏慢一点。