自己动手编写嵌入式Bootloader之(3)

自己动手编写嵌入式Bootloader之(3)

 1. 定时器初始化和延时程序

  因为在 CS8900A的驱动程序中需要用到延时,因此有必要对S3C2440的计时器进行使能和初始化,并编写延时程序。

  S3C2440A共有5个定时器,编号为Timer0 ~ Timer4。其中Timer0 ~ Timer3都有输出引脚,可以通过定时器来控制引脚电平周期性的变化,这称为脉冲宽度调制(PWM:Pulse Width Modulation)功能。而Timer4没有输出引脚,也就没有PWM功能,所以Timer4常被程序里的延时函数使用。

  定时器部件的时钟源为PCLK,但是需要经过两级预分频之后才真正供定时器使用。第一级预分频由TCFG0寄存器控制,其位[7:0]设置预分频器0的值,供Timer0和Timer1使用,位[15:8]设置预分频器1的值,供Timer2 ~ Timer4使用。第二级预分频由TCFG1寄存器控制,其每四位控制一个定时器,可以从2分频、4分频、8分频、16分频、外接TCLK0/TCLK1 这五种频率中选择。

  我们的延时函数使用Timer4,其它定时器全部关闭。初始化程序中设置:TCFG0 = 0x0f00; 表示Timer4的第一级预分频值为 15+1 = 16。寄存器TCFG1使用默认值全0,表示第二级预分频为2分频。前面已经设置PCLK为50MHz,这样Timer4实际的工作频率为:

  50MHz/16/2 = 50000000/32 = 1562500Hz

  注意计算时钟频率时的MHz是指10^6,而不是2^20;同理KHz是指1000Hz,而不是1024Hz。

  我们在TCON中把Timer4设为”自动加载“。当Timer4启动时,TCNTB4的值将被自动装入内部寄存器TCNT4,然后在工作频率下,TCNT4开始减1计数,当到达0时,TCNTB4的值又被自动装入TCNT4,下一个计数流程开始。我们把TCNTB4设为15625,则一个计数流程的的长度为10毫秒。

  假设要延时的时间为msec毫秒,则共需要的计数值为 tmo = msec*15625/10,设一个变量timestamp保存已经过去的时间戳,每次读取TCNT4的值后更新timestamp,直到它大于 tmo 。程序如下:



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

        while ( timestamp  <  tmo )

  {

  thisdec 
=  TCNTO4  &  0xffff;

  
if ( lastdec  >=  thisdec )  /*  normal mode  */

  {

  timestamp 
+=  lastdec  -  thisdec;

  }

  
else   /*  we have an overflow ...  */

  {

  timestamp 
+=  lastdec  +  TIMER_LOAD_VAL  -  thisdec;

  }

  lastdec 
=  thisdec;

  }

  TCNT4的值可由寄存器TCNTO4读出。程序中保存了最近两次读出的TCNTO4值, 如果本次值比上次小,说明在同一个计数流程内;如果本次值比上次大,说明已经进入了下一个计数流程。  

内容导航

  2. 串口标准输入输出

  要想在Bootloader中使用scanf()和print()并不容易,因为不能直接使用C库函数。scanf()要从串口获得输入, print()要向串口进行输出。必须自己实现常用的C库函数, 不仅包括输入输出函数,还包括字符串操作函数如strcmp(), strcpy()等。幸好在《嵌入式Linux应用开发完全手册》这本书的源代码中提供了这样简化的C库,所以就直接拿来用了。

  代码中定义了两个全局数组作为输入输出缓冲区:

  static unsigned char g_pcOutBuf[ 1024 ];

  static unsigned char g_pcInBuf[ 1024 ];

  其实我们可以把这两个缓冲区定位在CPU的 SteppingStone 里面,这样可以节省2K的空间。

  scanf()的实现里面调用 getc() 函数, printf() 的实现里面调用 putc() 函数。我们自己写getc()函数为从串口读取字符, putc()函数实现为向串口发送字符, 这样标准输入输出就跟串口联系在一起了。  



Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

        /*  发送一个字符  */

  void putc(unsigned char c)

  {

  
/*  等待,直到发送缓冲区中的数据已经全部发送出去  */

  
while  (!(UTRSTAT0  &  TXD0READY));

  
/*  向UTXH0寄存器中写入数据,UART即自动将它发送出去  */

  UTXH0 
=  c;

  }

  
/*  接收字符  */

  unsigned char getc(void)

  {

  unsigned char ret;

  
/*  等待,直到接收缓冲区中的有数据  */

  
while (!(UTRSTAT0  &  RXD0READY));

  
/*  直接读取URXH0寄存器,即可获得接收到的数据  */

  ret 
=  URXH0;

  
if  (ret  ==  0x0d || ret  ==  0x0a)

  {

  putc(0x0d);

  putc(0x0a);

  }

  
else

  {

  putc(ret);

  }

  return ret;

  }
 内容导航

  3. 源代码组织结构

  源代码跟目录下只有两个文件, 主Makefile和链接脚本sboot.lds。

  文件夹start内有start.S和nand.c,前者是上电后最初运行的汇编代码,后者含有Nand Flash的读函数,负责把S-Boot代码从Nand拷贝到RAM中。

  文件夹main内有main.c,是一个死循环,提供若干菜单供用户选择,然后调用相应功能的程序。

  文件夹lib内是简化和移植过的C标准库,包括输入输出和字符串操作函数。

  文件夹include内是一些头文件。

  文件夹app内有boot_linux.c和tftp.c,从名字就能看出它们的功能。

  文件夹device内含有设备驱动程序,如串口初始化、定时器初始化和延时函数、网卡驱动、网络协议实现等。

  每个文件夹内都有自己的Makefile,根目录下的主Makefile会进入各个子目录并调用各自的Makefile。每个子目录下的Makefile把自己编译的代码链接成一个build-in.o文件, 主Makefile把各个子目录下的build-in.o链接成一个可执行文件。

  编译器使用自己制作的 arm-hwlee-linux-gnueabi-gcc. 可以从这里下载。 给gcc增加 -nostdinc 选项, 表示不使用标准C库函数,不到/usr/include目录下寻找包含文件, 只在-I$(INCLUDEDIR)指定的目录寻找包含文件。

内容导航

  4. 提供全部源代码下载:

                      自己动手编写嵌入式Bootloader之三

                            文件:S-Boot.tar.gz

                            大小:41KB

                            下载:下载

  5. 运行结果截图

  自己动手编写嵌入式Bootloader之三

  图中,首先选择3从TFTP服务器下载内核到RAM中, 然后选择4从RAM成功启动内核。

  选择2还有通过串口Kermit协议下载内核的功能,前文没有对这部分代码作分析,有时间再补上。下面附一张截图:

  自己动手编写嵌入式Bootloader之三

  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值