×
单片机 > 单片机程序设计 > 详情

arm裸板驱动总结(makefile+lds链接脚本+裸板调试)

发布时间:2020-06-08 发布时间:
|

在裸板2440中,当我们使用nand启动时,2440会自动将前4k字节复制到内部sram中,如下图所示:

然而此时的SDRAM、nandflash的控制时序等都还没初始化,所以我们就只能使用前0~4095地址,在前4k地址里来初始化SDRAM,nandflash,初始化完成后,才能将nandflash的4096至后面的地址内容存放到SDRAM里去.


而裸板驱动的步骤如下所示:

  • 1.写makefile

  • 2.写lds链接脚本 (供makefile调用)

  • 3.写真正要执行的文件代码,比如初始化nand,sdram,串口等

为什么要写lds链接脚本?

首先lds链接脚本的作用就是将多个*.o文件的各个段链接在一起,告诉链接器这些各个段存放的地址先后顺序,它的好处就是,确保裸板2440的前4k地址里存放的是初始化SDRAM,nandflash的内容


 

1.写makefile

(参考makefile初步制作:http://www.cnblogs.com/lifexy/p/7065175.html)

在写裸板之前首先要来写Makefile,如下所示:


objs := head.o init.o nand.o main.o   

//定义objs变量,表示obj文件,包含生成boot.bin目标文件需要的依赖文件, 使用$(objs)就可以使用这个变量了

//‘:=’:有关位置的等于(比如:”x:=a  y:=$(x)  x:=b”,那么y的值取决于当时位置的a,而不是b) 

//‘=’:无关位置的等于(比如:”x=a  y=$(x)  x=b”,那么y的值永远等于最后的b ,而不是a)                                                     


 


nand.bin : $(objs)   //冒号前面的是表示目标文件, 冒号后面的是依赖文件,这里是将所有*.o文件编译出nand.bin可执行文件

arm-linux-ld -Tnand.lds    -o nand_elf $^   //将*.o文件生成nand_elf链接文件

//-T:指向链接脚本, $^:指向所有依赖文件,


arm-linux-objcopy -O binary -S nand_elf $@ //将nand_elf链接文件生成nand.bin文件

//$@:指向目标文件:nand.bin

//-O :选项,其中binary就是表示生成的文件为.bin文件


arm-linux-objdump -D -m arm  nand_elf > nand.dis //将nand.bin文件反汇编出nand.dis文件

//-D :反汇编nand.bin里面所有的段, -m arm:指定反汇编文件的架构体系,这里arm架构


 


%.o:%.c            //冒号前面的是目标文件,冒号后面的是依赖文件,%.o表示所有.o文件,


arm-linux-gcc -Wall -c -O2 -o $@ $

//$<:>

//$@:指向目标文件,也就是.o文件

//-Wall:编译若有错,便打印警告信息     -O2:编译优化程度为2级


 


%.o:%.S                       

    arm-linux-gcc -Wall -c -O2 -o $@ $


 


clean:                           //输入make clean,即进入该项,来删除所有生成的文件

    rm -f  nand.dis nand.bin nand_elf *.o   //通过rm命令来删除


2.写lds链接脚本

(参考lds脚本解析: http://www.cnblogs.com/lifexy/p/7089873.html)


 SECTIONS {

    . = 0x30000000;             //指定当前的链接地址=0x30000000


.text          :   {

head.o(.text)    //添加第一个目标文件,里面会调用这些函数

init.o(.text)      //添加第二个目标文件,里面存放关看门狗,初始化SDRAM等函数

nand.o(.text)   //添加第三个目标文件,里面存放初始化nand函数

*(.text)    // *(.text) 表示添加剩下的全部文件的.text代码段

}


.rodata ALIGN(4) : {*(.rodata)}       //指定只读数据段


.data ALIGN(4) : { *(.data) }     //指定读写数据段,     *(data):添加所有文件的数据段


__bss_start = .;     //把__bss_start赋值为当前地址位置,即bss段的开始位置


.bss ALIGN(4)  : { *(.bss)  *(COMMON) }     //指定bss段,里面存放未被使用的变量


__bss_end = .;        //把_end赋值为当前地址位置,即bss段的结束位置


}


上面的链接地址=0x30000000,表示程序运行的地方应该位于0x30000000处,0x30000000就是我们的SDRAM基地址,而一上电后,nand的前4k地址会被2440自动装载到内部ram中,所以我们初始化了sdram和nand后,就需要把程序所有内容都复制到链接地址0x30000000上才行

2.1为什么要在bss段的前后设置两个符号__bss_start, __bss_end?

定义__bss_start和__bss_end符号,是用来程序开始之前将这些未定义的变量清0,节省内存
且__bss_start -0x30000000就等于该bin文件的字节大小,实现动态复制

2.3为什么链接地址在0x30000000处,为什么在初始化sdram和nand之前,还能运行前4k地址的内容?

我们先来看看head.S第一个目标文件,就知道了:


.text                                                           @设置代码段


           @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义

            ldr     sp, =4096               @设置堆栈

            bl      disable_watch_dog       @关WATCH DOG

            bl      memsetup                @初始化SDRAM

            bl      nand_init               @初始化NAND Flash


            ldr sp,=0x34000000              @64Msdram,所以设置栈SP=0x34000000,避免堆栈溢出

                                            

                           @nand_read_ll函数需要3个参数:

            ldr     r0,     =0x30000000     @1. 目标地址=0x30000000,这是SDRAM的起始地址

            mov     r1,     #0              @2.  源地址   = 0         

            ldr     r2,     =__bss_start         

            sub           r2,r2,r0          @3.  复制长度= __bss_start-0x30000000

            bl      nand_read               @调用C函数nand_read,将nand的内容复制到SDRAM中


            ldr     lr, =halt_loop          @设置返回地址


            ldr     pc, =main                @使用ldr命令 绝对跳转到SDRAM地址上

halt_loop:                                   @若main函数跳出后,便进入死循环,避免程序跑飞

            b       halt_loop


(参考位置无关码(bl)与绝对位置码(ldr): http://www.cnblogs.com/lifexy/p/7117345.html)

从上面代码来看,可以发现在复制数据到sdram之前,都是使用的相对跳转命令bl,bl是一个位置无关码,也就是说无论该代码放在内存的哪个地址,都能正确运行.


而ldr就是绝对跳转命令,是一个绝对位置码,当一上电时,我们的链接地址0x30000000上是没有程序的,因为程序都存在nand flash上,也就是0地址上,而如果在复制数据到sdram之前,使用ldr去执行的话,就会直接跳转到0x30000000上,就会运行出错.


而且在复制数据到sdram之前,执行的代码里都不能用静态变量、全局变量、以及数组,因为这些初始值量的地址与位置有关的,必须将nand的内容复制到sdram地址中,才能用.

 

2.4比如,下面memsetup ()函数,就是个会出错的函数

其中的mem_cfg_val[]数组的内存是存在链接地址0x30000000上,就是与位置有关,在未复制内容之前使用将会出错


#define   MEM_CTL_BASE            0x48000000           //SDRAM寄存器基地址

void memsetup()

{

   int   i = 0;

   unsigned long *p = (unsigned long *)MEM_CTL_BASE;


 


    /* SDRAM 13个寄存器的值 */


    unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON


                                            0x00000700,     //BANKCON0


                                            0x00000700,     //BANKCON1


                                            0x00000700,     //BANKCON2


0x00000700, //BANKCO


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
如何为单片机选择合适的负载电容