裸板调节和测试,arm裸板驱动总结

在裸板2440中,当大家接纳nand运营时,2440会自动将前4k字节复制到内部sram中,如下图所示:

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

  1. 点灯法

    1 led_flicker:
    2 ldr r0,=0x56000050
    3 ldr r1,=(1<<(4*2)) 4 str r1,[r0] 5 ldr r0,=0x56000054 6 ldr r1,=0 7 ldr r2,(1<<4) 8 loop: 9 str r1,[r0] 10 bl delay 11 str r2,[r0] 12 bl delay 13 b loop 14 delay: 15 ldr r3,=30000 16 1b: 17 sub r3,r3,#1 18 cmp r3,#0 19 bne 1b 20 mov pc,lr

图片 1

图片 2

可平昔采取bl led_flicker来使用该程序。

不过此时的SDRAM、nandflash的决定时序等都还没开端化,所以大家就不得不选择前0~4095地点,在前4k地址里来早先化SDRAM,nandflash,初步化实现后,才能将nandflash的4096至前边的地址内容存放到SDRAM里去.

不过此时的SDRAM、nandflash的决定时序等都还没初叶化,所以我们就只能选择前0~4095地点,在前4k地址里来初阶化SDRAM,nandflash,伊始化达成后,才能将nandflash的4096至前边的地址内容存放到SDRAM里去.

  1. 串口打字与印刷及栈开头分析
    2.1 使用的前提是串口已经起先化实现且能够平常使用

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

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

  直接在急需稳定错误的地方加上printk语句,看串口是还是不是有相应的出口即可。
2.2 nand_setup的分析

  • 1.写makefile
  • 2.写lds链接脚本 (供makefile调用)
  • 3.写真正要实行的文本代码,比如初叶化nand,sdram,串口等
  • 1.写makefile
  • 2.写lds链接脚本 (供makefile调用)
  • 3.写真正要实践的公文代码,比如伊始化nand,sdram,串口等

  a.栈和一些变量是目前变卦的

怎么要写lds链接脚本?

缘何要写lds链接脚本?

  b.局地变量的开头值在哪?函数一开端的一部分读出那个值,用来初步化局地变量

先是lds链接脚本的功效正是将多少个*.o文件的相继段链接在一块,告诉链接器那么些各样段存放的地址先后顺序,它的裨益正是,确认保证裸板2440的前4k地址里存放的是起初化SDRAM,nandflash的内容

率先lds链接脚本的效果就是将三个*.o文件的逐一段链接在同步,告诉链接器那个各类段存放的地点先后顺序,它的便宜正是,确认保障裸板2440的前4k地址里存放的是开端化SDRAM,nandflash的剧情

  1. JTAG调试器


做事规律:

 

 

  a.CPU发出的地址/数据信号都因此JTAG

1.写makefile

1.写makefile

  b.JTAG可以控制CPU

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

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

    当Addr=xxx,截至CPU(硬件断点,七个,重要用以调试nor/rom上的主次)

在写裸板从前率先要来写Makefile,如下所示:

在写裸板在此以前率先要来写Makefile,如下所示:

    当Data=xxx,结束CPU(软件断点,无数个,该地方是可写的,所以不可能调节和测试nor/rom上的先后)

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 $@ $<         //将*.c文件生成*.o文件
//$<:指向第一个依赖文件, 也就是.c文件
//$@:指向目标文件,也就是.o文件
//-Wall:编译若有错,便打印警告信息     -O2:编译优化程度为2级



%.o:%.S                       
    arm-linux-gcc -Wall -c -O2 -o $@ $<    //将*.S文件生成*.o文件



clean:                           //输入make clean,即进入该项,来删除所有生成的文件
    rm -f  nand.dis nand.bin nand_elf *.o   //通过rm命令来删除
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 $@ $<         //将*.c文件生成*.o文件
//$<:指向第一个依赖文件, 也就是.c文件
//$@:指向目标文件,也就是.o文件
//-Wall:编译若有错,便打印警告信息     -O2:编译优化程度为2级



%.o:%.S                       
    arm-linux-gcc -Wall -c -O2 -o $@ $<    //将*.S文件生成*.o文件



clean:                           //输入make clean,即进入该项,来删除所有生成的文件
    rm -f  nand.dis nand.bin nand_elf *.o   //通过rm命令来删除

  c.让JTAG直接待上访问外设
3.1 命令行调节和测试

2.写lds链接脚本

2.写lds链接脚本

3.2 源码级别的调剂
前提
a. 程序必须已经重定位好,位于它的链接地址
a.1 如果程序的链接地址是SDRAM, 使用openocd初阶化SDRAM
a.2 使用arm-linux-gdb/arm-elf-gdb下载程序

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

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

b. 链接脚本必须把text,rodata,data,bss等分别存放
c. 被调剂的先后为ELF格式,内含调节和测试消息(即编写翻译时有-g选项)

 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段的结束位置

}
 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段的结束位置

}

地方的链接地址=0x三千0000,表示程序运营的地方应该位于0x三千0000处,0x三千0000便是我们的SDRAM营地址,而一上电后,nand的前4k地址会被2440自动装载到中间ram中,所以大家发轫化了sdram和nand后,就须求把程序有所内容都复制到链接地址0x三千0000上才行

上边的链接地址=0x贰仟0000,表示程序运转的地点应当位于0x三千0000处,0x三千0000正是我们的SDRAM营地址,而一上电后,nand的前4k地址会被2440自行李装运载到个中ram中,所以大家开首化了sdram和nand后,就供给把程序有所剧情都复制到链接地址0x两千0000上才行

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

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

定义__bss_start和__bss_end符号,是用来先后开头从前将那几个未定义的变量清0,节省里部存储器
且__bss_start
-0x30000000就也正是该bin文件的字节大小,完毕动态复制

定义__bss_start和__bss_end符号,是用来先后开端从前将这个未定义的变量清0,节外省部存款和储蓄器
且__bss_start
-0x三千0000就极度该bin文件的字节大小,完成动态复制

2.3为何链接地址在0x30000000处,为何在先导化sdram和nand在此之前,还是能运作前4k地址的始末?

2.3怎么链接地址在0x三千0000处,为啥在伊始化sdram和nand从前,还是能运作前4k地址的剧情?

作者们先来探视head.S第二个对象文件,就知道了:

我们先来看望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
.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)

(参考地方非亲非故码(bl)与相对地方码(ldr):
http://www.cnblogs.com/lifexy/p/7117345.html)

从地点代码来看,能够窥见在复制数据到sdram此前,都是使用的相持跳转命令bl,bl是一个地方毫无干系码,约等于说无论该代码放在内部存款和储蓄器的哪位地点,都能科学运维.

从下边代码来看,能够窥见在复制数据到sdram在此以前,都是应用的相对跳转命令bl,bl是1个职位毫无干系码,也便是说无论该代码放在内部存款和储蓄器的哪些地点,都能正确运转.

而ldr正是纯属跳转命令,是二个万万地点码,当一上电时,大家的链接地址0x三千0000上是未曾先后的,因为程序都留存nand
flash上,也正是0地址上,而只要在复制数据到sdram此前,使用ldr去实践的话,就会直接跳转到0x三千0000上,就会运行出错.

而ldr就是相对跳转命令,是三个相对地点码,当一上电时,大家的链接地址0x三千0000上是从未有过先后的,因为程序都存在nand
flash上,也正是0地址上,而一旦在复制数据到sdram从前,使用ldr去实施的话,就会一向跳转到0x两千0000上,就会运作出错.

与此同时在复制数据到sdram此前,执行的代码里都不可能用静态变量、全局变量、以及数组,因为这几个开首值量的地址与岗位有关的,必须将nand的始末复制到sdram地址中,才能用.

并且在复制数据到sdram在此以前,执行的代码里都不能够用静态变量、全局变量、以及数组,因为那么些起始值量的地点与职务有关的,必须将nand的内容复制到sdram地址中,才能用.

 

 

2.4诸如,下边memsetup
()函数,正是个会出错的函数

2.4诸如,上边memsetup
()函数,就是个会出错的函数

其中的mem_cfg_val[]数组的内部存款和储蓄器是存在链接地址0x贰仟0000上,正是与职责有关,在未复制内容前边使用将会出错

其中的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,     //BANKCON3 

                                            0x00000700,     //BANKCON4

                                            0x00000700,     //BANKCON5

                                            0x00018005,     //BANKCON6

                                            0x00018005,     //BANKCON7

                                            0x008C07A3,     //REFRESH

                                            0x000000B1,     //BANKSIZE

                                            0x00000030,     //MRSRB6

                                            0x00000030,     //MRSRB7

                                    };



       for(; i < 13; i++)

              p[i] = mem_cfg_val[i];

}
#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,     //BANKCON3 

                                            0x00000700,     //BANKCON4

                                            0x00000700,     //BANKCON5

                                            0x00018005,     //BANKCON6

                                            0x00018005,     //BANKCON7

                                            0x008C07A3,     //REFRESH

                                            0x000000B1,     //BANKSIZE

                                            0x00000030,     //MRSRB6

                                            0x00000030,     //MRSRB7

                                    };



       for(; i < 13; i++)

              p[i] = mem_cfg_val[i];

}

如下一个图所示,通过反汇编来看,上边的数组内容都以存在SDRAM的链接地址上边包车型客车rodata段0x三千05d0里,在大家从没伊始化SDRAM,复制数据到SDRAM在此之前,那么些数量是无能为力读取到的

正如3个图所示,通过反汇编来看,上边的数组内容都以存在SDRAM的链接地址下面的rodata段0x两千05d0里,在我们没有起首化SDRAM,复制数据到SDRAM在此之前,那些数据是不只怕读取到的

图1,使用bl跳到相持地址0x贰仟0094处:

图1,使用bl跳到相对地址0x20000094处:

 图片 3

 图片 4

图2,使用ldr,使ip跳到相对地址0x两千05d0:

图2,使用ldr,使ip跳到相对地址0x贰仟05d0:

 图片 5

 图片 6

图3,0x三千05d0里保存的.redata只读数据段,也正是 mem_cfg_val[]的内容:

图3,0x三千05d0里保存的.redata只读数据段,也正是 mem_cfg_val[]的内容:

 图片 7

 图片 8

2.5就此要修改memsetup
()函数为以下才行:

2.5据此要修改memsetup
()函数为以下才行:

#define   MEM_CTL_BASE            0x48000000           //SDRAM寄存器基地址
void memsetup()
{
       unsigned long *p = (unsigned long *)MEM_CTL_BASE;

    /* 设置SDRAM 13个寄存器的值 */
   p[0]  =0x22011110,     //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] =0x008C07A3,     //REFRESH
   p[10] =0x000000B1,     //BANKSIZE
   p[11] = 0x00000030,     //MRSRB6
   p[12] =0x00000030,     //MRSRB7
}
#define   MEM_CTL_BASE            0x48000000           //SDRAM寄存器基地址
void memsetup()
{
       unsigned long *p = (unsigned long *)MEM_CTL_BASE;

    /* 设置SDRAM 13个寄存器的值 */
   p[0]  =0x22011110,     //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] =0x008C07A3,     //REFRESH
   p[10] =0x000000B1,     //BANKSIZE
   p[11] = 0x00000030,     //MRSRB6
   p[12] =0x00000030,     //MRSRB7
}

因而反汇编来看,可以看看这一个赋值,都以靠mov,add等一声令下来加加减减拼出来的

因而反汇编来看,可以看出那几个赋值,都以靠mov,add等一声令下来加加减减拼出来的

正如图,我们以地方的代码p[0]  =0x22011110为例:

一般来说图,大家以地方的代码p[0]  =0x22011110为例:

 图片 9

 图片 10

图片 11

图片 12

 

 

3.在裸板中调剂有以下几步

3.在裸板中调剂有以下几步

3.1点灯法:

3.1点灯法:

LED_SHOW:
               ldr   r0,  =0x56000050                        
               ldr  r1,  =(1<<(4*2))                 @设置GPFCON寄存器的GPF4为输出引脚
               str  r1,  [r0]                                           
               ldr  r0,  =0x56000054                       @GPFDAT寄存器
               ldr  r1,  =0                                @设置GPF4=0,亮灯
               ldr  r2,  =(1<<4)                           @设置GPF4=1,灭灯                            

LED_LOOP:                        @死循环闪灯

               str  r1,  [r0]           @亮灯
               bl   DELAY
               str  r2,  [r0]         @灭灯
               bl   DELAY   
               b    LED_LOOP 

DELAY:                           @延时

             ldr r3,=30000
1:
               sub  r3,  r3,   #1
               cmp  r3,  #0
               bne     1b
               mov  pc, lr     @跳出循环  PS:寄存器之间赋值只能用mov
LED_SHOW:
               ldr   r0,  =0x56000050                        
               ldr  r1,  =(1<<(4*2))                 @设置GPFCON寄存器的GPF4为输出引脚
               str  r1,  [r0]                                           
               ldr  r0,  =0x56000054                       @GPFDAT寄存器
               ldr  r1,  =0                                @设置GPF4=0,亮灯
               ldr  r2,  =(1<<4)                           @设置GPF4=1,灭灯                            

LED_LOOP:                        @死循环闪灯

               str  r1,  [r0]           @亮灯
               bl   DELAY
               str  r2,  [r0]         @灭灯
               bl   DELAY   
               b    LED_LOOP 

DELAY:                           @延时

             ldr r3,=30000
1:
               sub  r3,  r3,   #1
               cmp  r3,  #0
               bne     1b
               mov  pc, lr     @跳出循环  PS:寄存器之间赋值只能用mov

在调节和测试汇编中:就能够使用 “b 
LED_SHOW”,若LED闪烁,便表明程序已跑过,通过点灯来定位程序在哪出错,

在调节约外汇编中:就可以使用 “b 
LED_SHOW”,若LED闪烁,便表明程序已跑过,通过点灯来恒定程序在哪出错,

缺陷在于亟待频仍烧写才能得出结果,调节和测试分外劳累

缺点在于亟待反复烧写才能得出结果,调试万分麻烦

3.2串口打字与印刷

3.2串口打字与印刷

先是要求经过寄存器来开始化串口

率先必要经过寄存器来开端化串口

在2440中,当没有初阶化MPLLCON和CLKDIVN寄存器时,全部的时钟都由12Mhz晶振提供,所以PCLK=12MHZ,则Porter率最高就是57600,因为UB翼虎DIV0=13000000/(57600*16-1)=13.02,所以串口代码如下所示:

在2440中,当没有起首化MPLLCON和CLKDIVN寄存器时,全部的钟表都由12Mhz晶振提供,所以PCLK=12MHZ,则Porter率最高正是57600,因为UBENCOREDIV0=1贰仟000/(57600*16-1)=13.02,所以串口代码如下所示:

 

 

#define S3C_PCLK            12000000    // PCLK初始值为12MHz
#define S3C_UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define S3C_UART_BAUD_RATE  57600      // 波特率
#define S3C_UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

#define S3C_GPHCON              (*(volatile unsigned long *)0x56000070)
#define S3C_GPHDAT              (*(volatile unsigned long *)0x56000074)
#define S3C_GPHUP               (*(volatile unsigned long *)0x56000078)
/*UART registers*/
#define S3C_ULCON0              (*(volatile unsigned long *)0x50000000)
#define S3C_UCON0               (*(volatile unsigned long *)0x50000004)
#define S3C_UFCON0              (*(volatile unsigned long *)0x50000008)
#define S3C_UMCON0              (*(volatile unsigned long *)0x5000000c)
#define S3C_UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define S3C_UTXH0               (*(volatile unsigned char *)0x50000020)
#define S3C_URXH0               (*(volatile unsigned char *)0x50000024)
#define S3C_UBRDIV0             (*(volatile unsigned long *)0x50000028)


#define TXD0READY   (1<<2)
#define RXD0READY   (1)

void uart0_init(void)
{
    S3C_GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    S3C_GPHUP   = 0x0c;     // GPH2,GPH3内部上拉
    S3C_ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    S3C_UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    S3C_UFCON0  = 0x00;     // 不使用FIFO
    S3C_UMCON0  = 0x00;     // 不使用流控
    S3C_UBRDIV0 = S3C_UART_BRD; // 波特率为115200
}
/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(S3C_UTRSTAT0 & TXD0READY));  
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    S3C_UTXH0 = c;
}
/*
 * 打印一串数字
 * num:数据
 */
void putnum(unsigned long num) //0xFFFF FFFF         (7:0)
{
        int i ,start=0;
        unsigned char c;
        uart0_init();
        for(i=0;i<100;i++);

        putc('0');
        putc('x');
       for(i=7;i>=0;i--)            //从[7:0]中打印数字,去除有效数字前面的0
       {
              c=( num >> (i*4) )&0xf;
              if(c!=0)
              {
                     if(c>9)    
                            putc(c-10+'A');                //打印A~F
                     else
                            putc(c+'0');                   //打印1~9                   
              if(!start)  start=1;                         //start=1,说明为有效数字
              }
              else if((start||!i)&&c==0)       //若是有效数字,便打印0,且在个位上时,不管是否有效都要打印
              {
                     putc('0');
              }
       }
       putc('\r');
       putc('\n');
}
#define S3C_PCLK            12000000    // PCLK初始值为12MHz
#define S3C_UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define S3C_UART_BAUD_RATE  57600      // 波特率
#define S3C_UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

#define S3C_GPHCON              (*(volatile unsigned long *)0x56000070)
#define S3C_GPHDAT              (*(volatile unsigned long *)0x56000074)
#define S3C_GPHUP               (*(volatile unsigned long *)0x56000078)
/*UART registers*/
#define S3C_ULCON0              (*(volatile unsigned long *)0x50000000)
#define S3C_UCON0               (*(volatile unsigned long *)0x50000004)
#define S3C_UFCON0              (*(volatile unsigned long *)0x50000008)
#define S3C_UMCON0              (*(volatile unsigned long *)0x5000000c)
#define S3C_UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define S3C_UTXH0               (*(volatile unsigned char *)0x50000020)
#define S3C_URXH0               (*(volatile unsigned char *)0x50000024)
#define S3C_UBRDIV0             (*(volatile unsigned long *)0x50000028)


#define TXD0READY   (1<<2)
#define RXD0READY   (1)

void uart0_init(void)
{
    S3C_GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    S3C_GPHUP   = 0x0c;     // GPH2,GPH3内部上拉
    S3C_ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    S3C_UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    S3C_UFCON0  = 0x00;     // 不使用FIFO
    S3C_UMCON0  = 0x00;     // 不使用流控
    S3C_UBRDIV0 = S3C_UART_BRD; // 波特率为115200
}
/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(S3C_UTRSTAT0 & TXD0READY));  
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    S3C_UTXH0 = c;
}
/*
 * 打印一串数字
 * num:数据
 */
void putnum(unsigned long num) //0xFFFF FFFF         (7:0)
{
        int i ,start=0;
        unsigned char c;
        uart0_init();
        for(i=0;i<100;i++);

        putc('0');
        putc('x');
       for(i=7;i>=0;i--)            //从[7:0]中打印数字,去除有效数字前面的0
       {
              c=( num >> (i*4) )&0xf;
              if(c!=0)
              {
                     if(c>9)    
                            putc(c-10+'A');                //打印A~F
                     else
                            putc(c+'0');                   //打印1~9                   
              if(!start)  start=1;                         //start=1,说明为有效数字
              }
              else if((start||!i)&&c==0)       //若是有效数字,便打印0,且在个位上时,不管是否有效都要打印
              {
                     putc('0');
              }
       }
       putc('\r');
       putc('\n');
}

 

 

在调节约外汇编中,就能够利用:

在调节约外汇编中,就可以行使:

mov   r0,#0x100     //参数等于0x100
bl    putnum       //调用打印函数
mov   r0,#0x100     //参数等于0x100
bl    putnum       //调用打印函数

即可打字与印刷0x100数字,
能便捷稳定出程序在哪出错

即可打字与印刷0x100数字,
能火速稳定出程序在哪出错

在c中,直接通过调用函数即可

在c中,直接通过调用函数即可

 

 

3.3
使用JTAG调试器 

3.3
使用JTAG调试器 

JTAG用于芯片的测试与程序调节和测试,JTAG位于CPU内部,当CPU收发引脚上的数据时,都会经过JTAG单元,而JTAG单元会从CPU内部引出TMS,TCK,TDI,TDO,多少个引脚,便能够因而OpenJTAG调节和测试器连接电脑USB,而另一端连接那一个JTAG脚来控制CPU

JTAG用于芯片的测试与程序调节和测试,JTAG位于CPU内部,当CPU收发引脚上的数码时,都会因此JTAG单元,而JTAG单元会从CPU内部引出TMS,TCK,TDI,TDO,八个引脚,便得以通过OpenJTAG调节和测试器连接电脑USB,而另一端连接这么些JTAG脚来支配CPU

OpenJTAG可以达成:

OpenJTAG能够实现:

  • 读写有个别地方上的多寡
  • 将文件下载到2440的某部地点上,或读取出有个别地点到文件中
  • 查询CPU当前景况、中断CPU运营、恢复CPU运转、复位CPU等
  • 安装CPU的地址断点,比如设置为0x30000000,当CPU运营到这一个地点时,便会结束运行
  • 读写有个别地方上的多寡
  • 将文件下载到2440的某部地方上,或读取出某些地方到文件中
  • 查询CPU当前情景、中断CPU运转、恢复生机CPU运维、复位CPU等
  • 安装CPU的地方断点,比如设置为0x三千0000,当CPU运转到这一个地址时,便会终止运作

断点在调节中分成三种:

断点在调节中分成两种:

硬件断点,在2240中,共有多个硬件断点,也正是最多设置四个硬件断点

硬件断点,在2240中,共有三个硬件断点,相当于最多安装八个硬件断点

软件断点,能够设置重重个断点

软件断点,能够设置过三个断点

1)为什么软件断点能够设置过三个?

1)为何软件断点能够安装重重个?

其实JTAG后台会把各类供给暂停的地点断点里的数量复制到内定地址里,并赋为有个别特殊值(如deeedeee),然后CPU运转时,当有些变量=这一个特殊值(如deeedeee),便通晓到了软件断点,并从钦命地址里把原本的值换回去,然后暂停止运输营

实际上JTAG后台会把种种须求暂停的地方断点里的多少复制到钦赐地方里,并赋为有个别特殊值(如deeedeee),然后CPU运营时,当某些变量=那些新鲜值(如deeedeee),便知道到了软件断点,并从内定地址里把原来的值换回去,然后暂停止运输维

注意:

注意:

出于软件断点,会后台保存断点数据到另三个地址中,前提要必须保障地址可一贯读写,所以在nor
flash,nand
flash下则无从完成调节和测试,若链接地址在SDRAM地址上,则软件断点的地址必须安装在SDRAM起先化后的地方上

由于软件断点,会后台保存断点数据到另二个地方中,前提要务必保险地址可直接读写,所以在nor
flash,nand
flash下则无从兑现调节和测试,若链接地址在SDRAM地址上,则软件断点的地点必须安装在SDRAM初步化后的地方上

3.3.1.因此OCD对JATG进行命令行调节和测试

3.3.1.透过OCD对JATG举行命令行调节和测试

1)安装OpenOCD

1)安装OpenOCD

OpenOCD:既能够烧写nor
flash,也足以烧写nand flash,并得以经过JTAG调节和测试器来开始展览调剂

OpenOCD:既能够烧写nor
flash,也能够烧写nand flash,并得以经过JTAG调试器来进展调节

接上OpenJTAG,并安装OpenJTAG驱动

接上OpenJTAG,并安装OpenJTAG驱动

 

 

2)使用OpenOCD工具连接OpenJTAG调试器

2)使用OpenOCD工具连接OpenJTAG调节和测试器

图片 13

图片 14

如上图所示:

如上海教室所示:

步骤1,选择jtag类型,CPU类型.

步骤1,选择jtag类型,CPU类型.

步骤2,点击连接按钮

步骤2,点击连接按钮

步骤3,能够看看24叁18头扶助三个硬件断点

手续3,能够见到243陆头援助1个硬件断点

里面,work dir 正是须求烧写的文书根目录,
或读取CPU有个别地方内容到文件的公文根目录

中间,work dir 就是需求烧写的文本根目录,
或读取CPU有些地点内容到文件的公文根目录

 

 

3)然后通过telent控制台进行调试

3)然后通过telent控制台举行调节和测试

telent的要害指标,正是发送命令行给连接的OpenJTAG调节和测试器,然后OpenJTAG通过命令来对CPU进行操作

telent的关键目标,正是发送命令行给连接的OpenJTAG调节和测试器,然后OpenJTAG通过命令来对CPU举办操作

率先,在win7下,若没打开telnet客服端:

首先,在win7下,若没打开telnet客服端:

点击发轫 ->控制面板-> 程序和功力-> 打开或关闭Windows作用->打开“telnet客服端”

点击开始 ->控制面板-> 程序和效应-> 打开或关闭Windows成效->打开“telnet客服端”

然后在cmd控制台下,输入 “telnet 127.0.0.1
4444”命令,进入telent控制台,如下图所示:

接下来在cmd控制台下,输入 “telnet 127.0.0.1
4444”命令,进入telent控制台,如下图所示:

 图片 15

 图片 16

 

 

4)**接下去便足以经过命令行来落到实处调节和测试(供给参考反汇编文件,来兑现调节和测试)**

4)**接下去便得以透过命令行来落到实处调节和测试(必要参考反汇编文件,来完结调节和测试)**

常用的吩咐如下所示:

常用的命令如下所示:

poll       

poll       

查阅当前事态

查看当前状态

halt       

halt       

暂停CPU运行

暂停CPU运行

step
      

step
      

单步执行,假使钦赐了 address,则从
address 处起初推行一条指令

单步执行,假若钦定了 address,则从
address 处起头实施一条指令

reg        

reg        

展现CPU的r0、r壹 、r二 、sp、lr、pc等寄存器的值(要求halt后才能见到)

来得CPU的r0、r① 、r贰 、sp、lr、pc等寄存器的值(需求halt后才能观望)

resume 
[addr]

resume 
[addr]

过来CPU运维,若钦命了地方,便从钦命地址运行(必要halt后才能选拔)

回复CPU运营,若钦赐了地方,便从钦赐地点运维(供给halt后才能应用)

譬如说:  resume 0                  
//从0地方运转

比如:  resume 0                  
//从0地方运维

md<w|h|b>
<addr>  [size]      

md<w|h|b>
<addr>  [size]      

read读地址,读出size个内容,w:字,h:半字,b:字节.如下图所示:

read读地址,读出size个内容,w:字,h:半字,b:字节.如下图所示:

 图片 17

 图片 18

mw<w|h|b>
<addr>  <size> 

mw<w|h|b>
<addr>  <size> 

word写地址,写入size个内容,使用形式和地点类似

word写地址,写入size个内容,使用方法和方面类似

(PS:不能够一向读写nand和nor上的地址,只可以读写2440的里边地址(4096),若SDRAM已早先化,也足以兑现读写)

(PS:不可能一直读写nand和nor上的地址,只可以读写2440的当中地址(4096),若SDRAM已初步化,也足以兑现读写)

load_image
<file> <address>  

load_image
<file> <address>  

将文件<file>载入地址为 address
的内部存款和储蓄器,格式有“bin”, “ihex”、 “elf”

将文件<file>载入地址为 address
的内存,格式有“bin”, “ihex”、 “elf”

例如: 

例如: 

load_image  led.bin  0              //烧写led.bin到0地址
load_image  led.bin  0              //烧写led.bin到0地址

 

 

(PS:该公文的目录位于以前在OpenOCD工具的界面里的work
dir里)

(PS:该公文的目录位于从前在OpenOCD工具的界面里的work
dir里)

dump_image
<file> <address> <size>

dump_image
<file> <address> <size>

将内部存款和储蓄器从地址 address 初叶的 size
字节数据读出,保存到文件<file>中

将内部存储器从地址 address 起始的 size
字节数据读出,保存到文件<file>中

bp <addr>
<length> [hw]

bp <addr>
<length> [hw]

在地方 addr 处设置断点,hw
代表硬件断点,length为命令集字节长度,,若未钦定表示软件断点,比如:
stm32是一个字节长,2440是五个字节长,部分MCU拥有多套指令集,长度不稳定,如下图所示: 

在地点 addr 处设置断点,hw
表示硬件断点,length为命令集字节长度,,若未钦定表示软件断点,比如:
stm32是1个字节长,2440是4个字节长,部分MCU拥有多套指令集,长度不固定,如下图所示: 

图片 19

图片 20

rbp <addr>

rbp <addr>

除去地址 addr 处的断点

删除地址 addr 处的断点

bp

bp

打印断点消息

打字与印刷断点信息

3.3.2**通过GDB**对JATG完成源码级其余调节和测试 

3.3.2**通过GDB**对JATG完成源码级其他调节和测试 

在linux中,使用arm-linux-gdb软件

在linux中,使用arm-linux-gdb软件

在win7中,则使用arm-none-eabi-gdb软件

在win7中,则使用arm-none-eabi-gdb软件

选拔GDB工具,就不必要像上个OCD调节和测试那么麻烦了

动用GDB工具,就不需求像上个OCD调节和测试那么辛勤了

1)比如说,想在“int i=0;”处打上断点:

1)比如说,想在“int i=0;”处打上断点:

OCD调试:

OCD调试:

就需求查阅调试的反汇编文件,找到i=0所在的运行地址,然后经过命令在地点上打断点

就须求查阅调节和测试的反汇编文件,找到i=0所在的运维地址,然后经过命令在地点上打断点

GDB调试:

GDB调试:

则能够直接在i=0处的源码上打断点,后台会通过带调节和测试消息的编写翻译文件,来找到i=0处的运转地址,并向OpenOCD发送打断点命令

则足以直接在i=0处的源码上打断点,后台会通过带调节和测试新闻的编写翻译文件,来找到i=0处的运行地址,并向OpenOCD发送打断点命令

 

 

2)上边的带调试新闻的编译文件又是怎么来的?

2)下面的带调节和测试消息的编写翻译文件又是怎么来的?

经过Makefile里的arm-linux  -g 来的,         -g:表示生成的编写翻译文件里富含gdb调试新闻

经过Makefile里的arm-linux  -g 来的,         -g:表示生成的编写翻译文件里带有gdb调试消息

然后大家将方面第一节的Makefile修改,如下图:

下一场我们将方面第壹节的Makefile修改,如下图:

 图片 21

 图片 22

 

 

3)选用gdb以前,供给有限支撑:

3)利用gdb以前,要求保险:

  • 1.调节和测试的源码里面的始末必须放在同三个链接地址上,
    各类段也要分手储存,调节和测试的链接脚本和上面第三节的类似,
  • 2.假若程序的链接地址是SDRAM, 使用openocd开首化SDRAM
  • 1.调节和测试的源码里面包车型客车始末必须放在同多少个链接地址上,
    各类段也要分别储存,调节和测试的链接脚本和下边第③节的接近,
  • 2.假设程序的链接地址是SDRAM, 使用openocd开端化SDRAM

4)常用命令如下所示(以调节上图的nand_elf文件为例):

4)常用命令如下所示(以调节上海教室的nand_elf文件为例):

arm-none-eabi-gdb 
nand_elf

arm-none-eabi-gdb 
nand_elf

开发银行GDB,钦点调节和测试文件为nand_elf

开端GDB,钦命调节和测试文件为nand_elf

target
remote 127.0.0.1:3333            

target
remote 127.0.0.1:3333            

与OpenOCD建立连接

与OpenOCD建立连接

load      

load      

载入nand_elf调节和测试文件

载入nand_elf调节和测试文件

break
[file]:[row]

break
[file]:[row]

打断点,比如:

打断点,比如:

break main.c:21     //在main.c文件的第21行处打断点
break main.c:21     //在main.c文件的第21行处打断点

info br

info br

查阅断点

查看断点

delete
<num>

delete
<num>

除去第多少个断点,如下图所示:

删除第多少个断点,如下图所示:

 图片 23

 图片 24

c

c

复苏程序运转,若使用load后,使用c正是运行程序,
按ctrl+c便暂停运转

苏醒程序运营,若使用load后,使用c正是运营程序,
按ctrl+c便中止运转

step

step

单步执行

单步执行

monitor 
<cmd…>

monitor 
<cmd…>

调用OCD的授命使用,比如 : 

调用OCD的一声令下使用,比如 : 

monitor resume 0              //使用OCD的resume命令,使程序从0地址运行
monitor resume 0              //使用OCD的resume命令,使程序从0地址运行

quit

quit

退出

退出

 

 

 

 



(PS:也得以通过eclipse平台软件来调用GDB,GDB最终转换为命令行,再调用OCD来实现调节和测试,如下图所示)

(PS:也能够经过eclipse平台软件来调用GDB,GDB最终转换为命令行,再调用OCD来达成调节和测试,如下图所示)

 图片 25

 图片 26

 

 

相关文章