Bootloader

  Bootloader是嵌入式系统加电执行的第一段代码,代码初始化cpu和相关硬件资源,最终实现引导内核加载,启动操作系统运行。Bootloader引导完内核,资源操作权便交给了内核,这时内核就像个永不退出的while(1)循环执行着。正因为这种隔离交付,内核开发者会疑惑内核怎么就能跑起来了,代码堆栈环境是如何建立起来的?等

uboot

  bootloader并不所属于linux内核任何子系统,但既然是理解linux内核框架,内核引导肯定是要了解一下的,下面就以一个arm平台uboot的设计要点描述,来感性的认知一下,同时也给出一个驱动实例来做具体的讲解。BTW,uboot是bootloader的一种,有关简介可自行了解。

图1

  图示,为针对S3c2440处理器的uboot简单构造流程。对于不同的处理器芯片,uboot的设计实现是需要考虑的。若是S3c6410的开发板,在核心初始化关闭mmu和cache后,应该进行外设基地址的初始化。而若是S3c210的板子,内存初始化时后需要取消存储保护区,还要初始化iram-irom。
  上图示代码化,一个简单可引导内核的uboot就实现了。知道了bootloader需要干些什么东西,那具体是怎么实现的,需要掌握些什么知识呢,对每个需实现的的小模块,能不能试着概括出一个较为标准化的实现流程?

init_stack:
    ldr sp, =0x34000000
    mov pc, lr

  上示设置堆栈起始地址,并保存当前地址至lr寄存器,其实就基本实现了C语言编程堆栈环境的初始化,清除bss段仅是为防错而已,因为bss段中值多而杂,若不清零初始化,取值可能报错。代码就几行,可知对于一个开发者来说,首先需要掌握的是处理器硬件及架构方面的知识,是否能做一个通用的uboot,就要掌握不同处理器的特性(可能需具体到芯片的io引脚),0x34000000是S3c2440的堆栈起始地址,但在S3c6410上是0x54000000。其实有些还真没多大合不合理的,就像x86 0x7c00这个魔值一样,记住就行。然后当然就是,掌握编程语言,能够根据芯片手册实现代码逻辑了。如下裸驱:RS-232串口驱动实例,来讲解一下该怎么来实现一个裸驱。
  明确串口驱动实现:1,串口的初始化;2,数据发送及数据接收;3,验证。首先需要了解一下硬件的电气特性,进而得知需要什么硬件资源再做对应的初始化,先来看RS-232 9帧串口的电气特性:
    逻辑1:-3 — -15v
    逻辑0:+3 — +15v
  RS-232正负代表01逻辑,我们知道处理器使用TTL电平(无负电平),因为这个特性,处理器连接时需要加入电平转换芯片:实现RS-232与TTL电平转换



图2

  串口初始化包括:引脚初始化,帧格式设置,工作模式设置,波特率设置。如图可知初始化相应引脚,主要关注2,3,5,分别是代表接收,发送及地io引脚;帧格式主要设置多少奇偶位及多少停止位;工作模式设置双工传输方式;波特率设置标准的115200即可。针对串口驱动,如帧格式设置,这时你需要查看芯片手册,做相应定制。主要可能需要了解:
  一帧数据是怎样的?一般包括起始位,数据位,奇偶校验位以及停止位。TTL起始位空闲时为高电平,一出现下降沿则视为起始位;数据位即所需传输数据;奇偶校验位检验数据的合法安全;停止位表数据传输结束。BTW,起始位空闲时为高电平是很有道理的,可防因抖动引起的数据错误,低电平变高可比高电平变低来的容易啊。还有就是具体的简单时序图要没问题。
  例如,对于16进制数据55aaH,采用8数据位,1停止位传输时,信号线TTL波形图3,和RS-232波形图4

  如TTL图3:波形与数据格式一致
  55H=01010101B,55H的数据格式为1010101010B,起始位0,停止位1
  aaH=10101010B,aaH的数据格式为1101010100B,起始位0,停止位0
  如RS232图4:相反
  55H=01010101B,取反后10101010B,加入一个起始位1,一个停止位0
  aaH=10101010B,取反后01010101B,加入一个起始位1,一个停止位0



图3


图4

  如下是具体的代码实现:

//寄存器定义 
#define GPHCON (*(volatile unsigned long*)0x56000070)
#define ULCON0 (*(volatile unsigned long*)0x50000000)
#define UCON0 (*(volatile unsigned long*)0x50000004)
#define UTRSTAT0 (*(volatile unsigned long*)0x50000010)
#define UTXH0 (*(volatile unsigned long*)0x50000020)
#define URXH0 (*(volatile unsigned long*)0x50000024)
#define UBRDIV0 (*(volatile unsigned long*)0x50000028)
#define PCLK 5000000  //串口时钟来源
#define BAUD 115200  //波特率
void uart_init()
{
    //配置引脚功能
        GPHCON &= ~(0xf << 4);
        GPHCON |= (0xa << 4);
        //设置数据格式
        ULCON0 = 0b11;
        //设置工作模式
        UCON0 = 0b0101;
        //设置波特率
        UBRDIV0 = (int)(PCLK / (BAUD*16) – 1);
}
void putc(unsigned char ch)
{
    while(!(UTRSTAT0 & (1<<1)));  //等待寄存器变化,数据被取走
    UTXH0 = ch; //往寄存器赋值
}
unsigned char getc(void)
{
    unsigned char ret;
    while(!(UTRSTAT0 & (1<<0))); //等待寄存器变化,数据发送过来
    //取数据
    ret = TRXH0;  //取值
    if((ret ==0x0d) || (ret == 0x0a)){  //异常处理
        putc(0x0d);
        putc(0x0a);
    }else{
        putc(ret);
    }
    return ret;
}
//验证:查看串口输出,两次输出均为字符A即验证成功
void test()
{
uart_init();
putc(‘A’);
getc();
}   

  如上代码,代码并不多,主要是要理解其中的原理

小结

  是不是感觉bootloader不再那么的神秘了。当然可称为是一个简单操作系统的东西还是很复杂的,只是大概知道了框架是怎样的,也对构成模块的代码化有了个直观的理解。

参考资料:
  百度文库:RS-232串口通讯详解
  成都国嵌相关资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值