U-BOOT小全(四):BootLoader源码(SPL-UBoot 1)

对于概念性我们有了一定的了解以后,来瞅瞅UBoot的源码。

不过在这之前,整个图来回以一下前面讲的UBoot的启动跳转和硬件布局。

在这里插入图片描述
BootRom引导SPL,SPL引导U-Boot,然后U-Boot再引导内核。

下面我们来看看这个源码是什么样子的?

SPL代码追踪–start.S

从前面分析的u-boot-spl.lds链接文件可知,启动代码就是arch/arm/cpu/armv7/start.S。start.S主要做的事情就是初始化系统的各个方面。

从大的方面分,可以分成这几个部分:

1)设置CPU模式。
2)关闭看门狗。
3)关闭中断。
4)设置堆栈sp指针。
5)清除bss段。
6)异常中断处理。
/*
 * armboot - Startup Code for OMAP3530/ARM Cortex CPU-core
 *
 * Copyright (c) 2004        Texas Instruments <r-woodruff2@ti.com>
 *
 * Copyright (c) 2001        Marius Gr ger <mag@sysgo.de>
 * Copyright (c) 2002        Alex Züpke <azu@sysgo.de>
 * Copyright (c) 2002        Gary Jennejohn <garyj@denx.de>
 * Copyright (c) 2003        Richard Woodruff <r-woodruff2@ti.com>
 * Copyright (c) 2003        Kshitij <kshitij@ti.com>
 * Copyright (c) 2006-2008 Syed Mohammed Khasim <x0khasim@ti.com>
 *
 * SPDX-License-Identifier:        GPL-2.0+
 */
#include <asm-offsets.h>
#include <config.h>
#include <version.h>
#include <asm/system.h>
#include <linux/linkage.h>
.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
ldr pc, _fiq
#ifdef CONFIG_SPL_BUILD
_undefined_instruction:        .word _undefined_instruction
_software_interrupt:        .word _software_interrupt
_prefetch_abort:        .word _prefetch_abort
_data_abort:        .word _data_abort
_not_used:        .word _not_used
_irq:        .word _irq
_fiq:        .word _fiq
_pad:        .word 0x12345678 /* now 16*4=64 */
#else
.globl _undefined_instruction
_undefined_instruction:      .word undefined_instruction
.globl _software_interrupt
_software_interrupt:         .word software_interrupt
.globl _prefetch_abort
_prefetch_abort:             .word prefetch_abort
.globl _data_abort
_data_abort:                 .word data_abort
.globl _not_used
_not_used:                   .word not_used
.globl _irq
_irq:                        .word irq
.globl _fiq
_fiq:                        .word fiq
_pad:                        .word 0x12345678 /* now 16*4=64 */
#endif        /* CONFIG_SPL_BUILD */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef

首先使用.globl_start声明_start变量,并且告诉链接器此变量是全局的,外部可以访问,所以在arch/arm/cpu/armv7/sunxi/u-boot-spl.lds中,有用到此变量:

ENTRY(_start)

1、ENTRY(_start) 入口

即指定入口为_start,_start就是整个start.S的最开始,也是U-Boot代码的最开始。

_start: b    reset

_start后面加上一个冒号“:”,表示一个标号,类似于C语言goto语句中的标号。

_start: b   reset /* b是不带返回的跳转(bl是带返回的跳转),意思是无条件直接跳转到reset标号处执行程序*/
    ldr pc, _undefined_instruction /*未定义指令异常向量,ldr的作用是,
    将符号_undefined_instruction指向的地址的内容加载到pc*/
    ldr pc, _software_interrupt /*软件中断向量*/
    ldr pc, _prefetch_abort     /*预取指令异常向量*/
    ldr pc, _data_abort         /*数据操作异常向量*/
    ldr pc, _not_used           /*未使用*/
    ldr pc, _irq                /*irq中断向量*/

这几行定义的内容对应于ARM处理器的7种异常处理模式。

第一行就是重启复位异常的处理。

#ifdef CONFIG_SPL_BUILD
_undefined_instruction:         .word _undefined_instruction
_software_interrupt:            .word _software_interrupt
_prefetch_abort:                .word _prefetch_abort
_data_abort:                    .word _data_abort
_not_used:                      .word _not_used
_irq:                           .word _irq
_fiq:                           .word _fiq
_pad:                           .word 0x12345678 /* now 16*4=64 */
#else
.globl _undefined_instruction
_undefined_instruction:         .word undefined_instruction
.globl _software_interrupt
_software_interrupt:            .word software_interrupt
.globl _prefetch_abort
_prefetch_abort:                .word prefetch_abort
.globl _data_abort
_data_abort:                    .word data_abort
.globl _not_used
_not_used:                      .word not_used
.globl _irq
_irq:                           .word irq
.globl _fiq
_fiq:                           .word fiq
_pad:                           .word 0x12345678 /* now 16*4=64 */
#endif        /* CONFIG_SPL_BUILD */

如果是编译SPL,那么CONFIG_SPL_BUILD生效:

ldr pc, _undefined_instruction
_undefined_instruction: .word _undefined_instruction

那么这里是生效的,但是ldr pc,_undefined_instruction并无实际的执行动作。

如果是编译U-Boot,那么CONFIG_SPL_BUILD不生效:

ldr pc, _undefined_instruction
.globl _undefined_instruction
_undefined_instruction: .word undefined_instruction

而在后面的代码中可以看到:

/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction

.word的用法是.word expr,用于分配一个字大小的内存单元,并用expr初始化字内存单元。

这里就相当于在_undefined_instruction处分配了一个32位的地址空间,里面放置着undefined_instruction。类似于C语言中的:

_undefined_instruction = &undefined_instruction

那么ldr pc,_undefined_instruction就相当于PC指到了undefined_instruction处,直接跳到异常处理函数了。

.global _end_vect
_end_vect:
    .balignl 16,0xdeadbeef

这里定义了** _end_vect**,标志着向量表的结束。

接着使用.balignl做16位字节对齐。

不足之处是,用0xdeadbeef填充。其实用任何数据进行字节对齐的填充都是可以的,

这里选用0xdeadbeef是因为在十六进制下,只有abcdef这六个字母可用,从而表示出来的单词就非常少了,因此选择了这么一个有一定意思的单词。

接下来我们看实际的复位代码。

2、reset:

_start: b   reset /* b是不带返回的跳转(bl是带返回的跳转),意思是无条件直接跳转到reset标号处执行程序*/

将CPU设置为SVC模式

/*************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * setup Memory and board specific bits prior to relocation.
 * relocate armboot to ram
 * setup stack
 *
 *************************************************************************/
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
    .word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de
/*
 * the actual reset code
 */
reset:
bl save_boot_params
/*
 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
 * except if in HYP mode already
 */
mrs r0, cpsr
and r1, r0, #0x1f           @ mask mode bits
teq r1, #0x1a               @ test for HYP mode
bicne  r0, r0, #0x1f        @ clear all mode bits
orrne  r0, r0, #0x13        @ set SVC mode
orr r0, r0, #0xc0           @ disable FIQ and IRQ
msr cpsr,r0

首先我们**定义了IRQ_STACK_START_IN,**这里存放的是0x0badc0de,这个其实是表示bad code,为了在程序运行时引起用户注意。

3、save_boot_params

reset:
bl save_boot_params

接下来执行bl save_boot_params跳转指令。

在<arch/arm/cpu/armv7/start.S>中找到的save_boot_params的定义如下:

ENTRY(save_boot_params)
         bx      lr                      @ back to my caller
ENDPROC(save_boot_params)
         .weak   save_boot_params

可以看到save_boot_params有一个.weak限定符,它表明save_boot_params是一个弱符号,也就是说如果没有重新定义save_boot_params,则**使用<arch/arm/cpu/armv7/start.S>中的save_boot_params。**如果其他地方有定义,则使用新的定义。在这里并没有其他的定义,所以save_boot_params是一个空函数。函数定义如下:

ENTRY(save_boot_params)
         bx      lr                      @ back to my caller
ENDPROC(save_boot_params)
         .weak   save_boot_params

空函数之后的几行代码利用r0充当临时变量,以设定CPSR寄存器,将处理器模式设为SVC模式并禁用FIQ和IRQ。之所以将处理器模式设为SVC模式,有两个方面的考虑:

1)我们先简单地分析一下ARM处理器的7种模式:

  • ·中止ABT和未定义UND模式。

    • 中止ABT和未定义UND模式都是不太正常的模式,此处程序是正常运行的,所以不应该设置CPU为其中的任何一种模式,因此可以排除这两种模式。
  • ·快中断FIQ和中断IRQ模式。

    • 对于快中断FIQ和中断IRQ来说,U-Boot初始化的时候,也还没有中断要处理和能够处理的,而且即使是注册了中断服务程序后,能够处理中断,那么这两种模式也是自动切换过去的,因此,此处也不应该将CPU设置为其中的任何一种模式。
  • ·用户USR模式。

    • 虽然从理论上来说,可以设置CPU为用户USR模式,但是由于此模式无法直接访问很多的硬件资源,而U-Boot初始化时,就必须要去访问这类资源,所以此处可以排除,不能设置CPU为用户USR模式。
  • ·系统SYS模式VS管理SVC模式。

    • 首先,SYS模式和USR模式相比,所用的寄存器组都是一样的,但是增加了一些访问USR模式下不能访问的资源。而SVC模式本身就属于特权模式,本身就可以访问那些受控资源,而且相比SYS模式还多了些自己模式下的影子寄存器,所以,相对SYS模式来说,可以访问资源的能力相同,但是拥有更多的硬件资源。

从理论上来说,虽然CPU可以设置为SYS模式和SVC模式的任一种,但是从U-Boot方面考虑,其要做的事情是初始化系统的相关硬件资源,因此需要获取尽量多的权限,以方便操作硬件,初始化硬件。

从U-Boot的目的是初始化硬件的角度来说CPU设置为SVC模式,更有利于其工作。因此,此处将CPU设置为SVC模式。

2)U-Boot作为一个BootLoader来说,最终的目的是启动Linux的内核,在做好准备工作(即初始化硬件,准备好内核和rootfs等)跳转到内核之前,本身就要满足一些条件,其中一个条件就是要求CPU处于SVC模式的。

因此,U-Boot在最初的初始化阶段,就将CPU设置为SVC模式也是最正确的。之所以禁用IRQ和FIQ,是因为在U-Boot引导阶段不需要使用IRQ和FIQ,禁用后可排除中断的影响。

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0  @ Read CP15 SCTRL Register
bic r0, #CR_V        @ V = 0
mcr p15, 0, r0, c1, c0, 0        @ Write CP15 SCTRL Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0       @Set VBAR
#endif

这一段代码用于**设置协处理器中的寄存器SCTRL。**在《Cortex-A7 MPCore Technical Reference Manual》数据手册中有对SCTLR的介绍。SCTLR提供系统的顶层控制,包括内存系统。
该小节提供了访问SCTLR寄存器的方法:

MRC p15, 0, <Rt>, c1, c0, 0 ; Read System Control Register
MCR p15, 0, <Rt>, c1, c0, 0 ; Write System Control Register

在代码中,使用的是r0:

bic r0, #CR_V    @ V = 0中是设置SCTLR中的V位。

CR_V的定义在./arch/arm/include/asm/system.h中:

#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000        */

V位(bit13)的说明如下:

  • ·向量位。该位选择异常向量的基地址。
  • ·0:异常向量的基地址为0x00000000。软件可以使用VBAR来重新映射该基地址。
  • ·1:高位的异常向量的基地址为0xFFFF0000。该基地址不能被重映射。

因此,我们先将异常向量的基地址设定为0x00000000,接下来重新映射该基地址:

/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0        @Set VBAR

在《ARM®Architecture Reference Manual ARMv7-A and ARMv7-R edition》数据手册中能查到读写VBAR的信息:

MRC p15, 0, <Rt>, c12, c0, 0 ; Read VBAR into Rt
MCR p15, 0, <Rt>, c12, c0, 0 ; Write Rt to VBAR

这里我们将基地址映射为_start的地址。接着有几个函数的调用:

/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15                
bl cpu_init_crit
#endif
bl _main

4、cpu_init_cp15

/*************************************************************************
 *
 * cpu_init_cp15
 *
 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
 * CONFIG_SYS_ICACHE_OFF is defined.
 *
 *************************************************************************/
ENTRY(cpu_init_cp15)
 /*
 * Invalidate L1 I/D
 */
mov r0, #0                             @ set up for MCR
mcr p15, 0, r0, c8, c7, 0              @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0              @ invalidate icache
mcr p15, 0, r0, c7, c5, 6              @ invalidate BP array
mcr     p15, 0, r0, c7, c10, 4         @ DSB
mcr     p15, 0, r0, c7, c5, 4          @ ISB
/*
 * disable MMU stuff and caches
 */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000                @ clear bits 13 (--V-)
bic r0, r0, #0x00000007                @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002                @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800                @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000                @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000                @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0
#ifdef CONFIG_ARM_ERRATA_716044
mrc p15, 0, r0, c1, c0, 0              @ read system control register
orr r0, r0, #1 << 11             @ set bit #11
mcr p15, 0, r0, c1, c0, 0              @ write system control register
#endif
#ifdef CONFIG_ARM_ERRATA_742230
mrc p15, 0, r0, c15, c0, 1             @ read diagnostic register
orr r0, r0, #1 << 4              @ set bit #4
mcr p15, 0, r0, c15, c0, 1             @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1             @ read diagnostic register
orr r0, r0, #1 << 6              @ set bit #6
mcr p15, 0, r0, c15, c0, 1             @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1             @ read diagnostic register
orr r0, r0, #1 << 11             @ set bit #11
mcr p15, 0, r0, c15, c0, 1             @ write diagnostic register
#endif
mov pc, lr                             @ back to my caller
ENDPROC(cpu_init_cp15)

1、Invalidate L1 I/D

该函数的第一部分是Invalidate L1 I/D:

mov r0, #0                               @ set up for MCR
mcr p15, 0, r0, c8, c7, 0                @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0                @ invalidate icache
mcr p15, 0, r0, c7, c5, 6                @ invalidate BP array
mcr     p15, 0, r0, c7, c10, 4           @ DSB
mcr     p15, 0, r0, c7, c5, 4            @ ISB

在《Cortex-A7 MPCore Technical Reference Manual》中还可以查到mcr p15,0,r0,c8,c7,0是为了使TLB失效的,其中该操作会忽略Rt寄存器中的值,因此可以不往r0寄存器中写入0。

同样地,数据手册中的mcr p15,0,r0,c7,c5,0是为了使icache失效的;mcr p15,0,r0,c7,c5,6是为了使分支预测器失效的;mcr p15,0,r0,c7,c10,4执行Data Synchronization Barrier(数据同步屏障)操作,保证完成内存访问。mcr p15,0,r0,c7,c5,4执行**Instruction Synchronization Barrier(指令同步屏障)**操作,保证指令流的正确执行。

2、disable MMU stuff and caches

该函数的第二部分是disable MMU stuff and caches

mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000        @ clear bits 13 (--V-)
bic r0, r0, #0x00000007        @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002        @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800        @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic        r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr        r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
mcr p15, 0, r0, c1, c0, 0

mrc p15,0,r0,c1,c0,0用于读取SCTLR寄存器的值到r0中。bic r0,r0,#0x00002000指令将bit[13]V位清零,bic r0,r0,#0x00000007指令将bit[2:0]清零,然后orr r0,r0,#0x00000002指令将bit[1]置1。这样bit[2]C位的值为0,表示禁用D-cache。bit[1]A位的值为1,表明启用对齐的错误检查功能。bit[0]M位的值为0,**表示禁用地址转换功能,即禁用MMU。orr r0,r0,#0x00000800设置了bit[11]Z位,表示启动了程序流程的预测功能。bit[12]I位是开关I-cache的,对于引导ARM体系下的Linux,对I-cache的开关没有要求。

综合来看,通过对协处理器CP15的配置,可禁用MMU和Cache。

通过cpu_init_cp15,我们分析一下汇编语言中是如何实现函数调用的。

bl cpu_init_cp15实现函数调用,最后mov pc,lr实现函数的返回。

因为bl指令除了实现跳转到cpu_init_cp15之外,在跳转之前,还将下一条指令地址存放到lr寄存器中,这样,跳转的函数在执行完毕后,将lr再赋值给pc,就可以实现函数的返回,从而继续执行后续指令。

cpu_init_crit的定义如下。

5、cpu_init_crit

cpu_init_crit的定义如下。

/*************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************/
ENTRY(cpu_init_crit)
    /*
     * Jump to board specific initialization...
     * The Mask ROM will have already initialized
     * basic memory. Go here to bump up clock rate and handle
     * wake up conditions.
     */
    b lowlevel_init                @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)

该函数直接调用lowlevel_init函数,lowlevel_init函数在arch/arm/cpu/armv7/lowlevel_init.S中,代码如下。

ENTRY(lowlevel_init)
    /*
     * Setup a temporary stack
     */
    ldr sp, =CONFIG_SYS_INIT_SP_ADDR
    bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_BUILD
    ldr r9, =gdata
#else
    sub sp, sp, #GD_SIZE
    bic sp, sp, #7
    mov r9, sp
#endif
    /*
     * Save the old lr(passed in ip) and the current lr to stack
     */        
    push {ip, lr}
    /*
     * go setup pll, mux, memory
     */
    bl s_init
    pop {ip, pc}
ENDPROC(lowlevel_init)

第19~23行,首先设定栈指针SP为CONFIG_SYS_INIT_SP_ADDR,然后确保SP是8字节对齐。

CONFIG_SYS_INIT_SP_ADDR在include/configs/sunxi-common.h中的定义如下。

47 /* DRAM Base */
 48 #define CONFIG_SYS_SDRAM_BASE           0x40000000
 49 #define CONFIG_SYS_INIT_RAM_ADDR        0x0
 50 #define CONFIG_SYS_INIT_RAM_SIZE        0x8000  /* 32 KiB */
 51    
 52 #define CONFIG_SYS_INIT_SP_OFFSET \
 53         (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
 54 #define CONFIG_SYS_INIT_SP_ADDR \
 55         (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)

其中,GENERATED_GBL_DATA_SIZE在include/generated/generic-asm-offsets.h中的定义如下:

10 #define GENERATED_GBL_DATA_SIZE 160 /* (sizeof(struct global_data) + 15) & ~15  @ */

因此CONFIG_SYS_INIT_SP_ADDR的值为0x8000–160=0x7F60。然后CONFIG_SPL_BUILD在arch/arm/lib/spl.c中定义了ldr r9,=gdata。

gd_t gdata __attribute__ ((section(".data")));

从定义中可以看出,gdata是一个结构体变量,存放在.data段中。所以这里我们将结构体的地址存放在r9中。在后面的处理中我们将看到gdata的位置。

接下来用下面的代码:

push    {ip, lr}
    /*
     * go setup pll, mux, memory
     */
    bl s_init
    pop {ip, pc}

在调用s_init函数之前做了push{ip,lr}压栈和pop{ip,pc}出栈操作。这里之所以做栈操作,是因为存在多级函数调用,需要保护现场。如果在C语言中,函数跳转前后要做的事情都是由C语言编译器实现的,包括查看汇编代码,还有类似的栈处理。

到这里了,辛苦了,快休息一下,下面接着分析s_init。

参考资料:
内容全部来自《深入理解BootLoader》

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
我们整个ARM课程就分为三部分,这是第一部分,实现一个自己的最小bootloader 1.Read Me 一、实现功能 1.硬件初始化 2.延时判断加载操作系统还是进入Bootloader Shell 3.加载操作系统 4.Bootloadershell 二、Bootloader Shell 支持的命令 1.help 帮助,显示所有支持的命令,及命令格式 2.loadx 下载文件到开发板的内存,默认到0x32000000 3.led_on 点亮一个led灯 4.led_off 关闭一个led灯 5.led_test 测试所有led灯,全亮全灭循环3次 6.beep_test 测试蜂鸣器,响3声 7.seg7_test 测试7段数码管 8.dip4_test 测试4位拨码开关 9.flash_load 将NandFlash中的文件搬移到SDARAM中 10.flash_write 将SDRAM中的内容下载到NandFlash中 11.GO 跳到某地址执行,默认到0x32000000 三、文件结构 1.start.s 程序入口,负责硬件初始化,Bootloader自搬移 2.uart.c uart.h 串口驱动的实现 3.load.c 选择加载操作系统还是进入Shell 4.stdlib.h stdlib.c 标准库函数的实现 5.stdio.h stdio.c 标准输入输出函数的实现 6.shell.c shell.h shell命令的实现 7.dip4.h dip4.c 拨码开关相关底层函数 8.seg7.h seg7.c 7段数码管相关底层函数 9.copy_myself.c nan.h NandFlash底层函数 10.xmodem.h xmodem.c xmodem协议实现 11.Datatype.h 数据定义 12.os/os.c 模拟操作系统 13.Makefile 、流程及设计思想 1.硬件初始化 2.Bootloader自搬移 3.延时,判断是否有输入 4.(1)无输入则加载操作系统,操作系统烧写于Nand Flash的第100块,即位于100*32*512 = 0x190000 操作系统加载到内存的Sdram中 (2)有输入则进入shell命令模式 5.解释命令,使用自己实现的标准库函数来匹配输入的命令 6.匹配函数,定义了一个包含字符指针以及函数指针的结构体,可以通过对应关系迅速调用命令对应的函数 所有函数为void fun(void *)形式。 五、测试条件及结果 1. 打开超级终端,给开发板上电,超级终端上打印提示信息 2. 超级终端上开始3秒倒计时,3秒内不动键盘,提示加载操作系统,模拟操作系统的闪灯程序运行,可观察到LED等一闪一灭 3. 重启开发板,3秒内按下任意键,可看到有T-Boot#提示符,程序进入Shell模式 4. 输入help,可看到10条命令的使用方法 5. 输入led_on 1可看到第一个led灯亮 6. 输入led_off 1可看到第一个led灯灭 7. 输入led_test 可看到所有led一闪一灭3次 8. 输入beep_test 可听到蜂鸣器响3声 9. 输入seg7_test 可看到7段数码管每个led循环点亮 10.输入dip4_test 拨动拨码开关可观察到7段数码管对应的LED亮 11.输入loadx,发送文件0x/0s.bin 12.输入go 0x32000000 可观察到led灯一亮一灭 13.输入flash_load 0x190000 0x32000000 0x1000 (0x190000模拟操作系统烧写位置) 14.go 0x32000000 可观察到led一亮一灭 16.输入flash_write 0x32000000 0x200000 0x1000 17.输入flash_load 0x200000 0x31500000 0x1000 18.输入go 0x31500000 可观察到led灯一亮一灭

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TrustZone_Hcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值