[转载]自己动手编写嵌入式Bootloader之(3)

【实战指南】OpenClaw Telegram 群聊不响应完整解决方案:从获取群 ID 到关闭隐私模式全流程 将整个从“获取群组 ID → 配置 OpenClaw Telegram 通道 → 关闭隐私模式 → 测试”的全流程都整合进去。 阅读详情
第三部分:源代码,运行结果

这一部分将对前文没有提到的几段关键代码进行简单说明,介绍一下源代码组织结构和Makefile系统,展示一下实验运行结果,并提供全部源代码下载。

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 。程序如下:

    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()函数实现为向串口发送字符, 这样标准输入输出就跟串口联系在一起了。

/* 发送一个字符 */
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. 提供全部源代码下载:


文件:S-Boot.tar.gz
大小:41KB
下载:下载


5. 运行结果截图



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

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


 

 TAG ARM Linux Bootloader
商品评论中的实体情感分析 在之前的文章中我已经将商品评论中的评价实体,也就是特征(feature)给挖掘出来了,我看了一下,挖掘的效果还是不错的。原文链接:http://blog.csdn.net/chixujohnny/article/details/52794685 下面一步要做的是施加情感因子,也就是说,要将人们评论时候所连带的感情程度施加一下。这项任务的归类应该是情感分析技术。 目前该类问题的情感分析技 阅读详情

相关推荐

ARM学习(0)NXP MCU BootLoader的理解与实现

BootLoader的理解与实现

张一西的博客 5593

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

jun2ran的专栏 890

bldc1.zip_bldc1_无刷 仿真_无刷直接转矩_直接转矩控制

永磁无刷直流电机的直接转矩控制仿真,包含转矩磁链比较以及开关矢量表

自己动手写一个简单的bootloader

自己动手写一个简单的bootloader 15年10月31日19:44:27 (一) start.S 写这一段代码前,先要清楚bootloader开始的时候都做什么了。无非就是硬件的初始化,我们想要写一个简单的bootloader,它的功能只是要能启动内核就行,因此,与uboot相比,它要做的东西很少。 总结出来就是: (1)关看门狗; (2)初始化时钟,设置分频系数,让板子跑的更

吾皇的博客 1万+

浅析嵌入式系统之uboot详解(5.3)—PWM定时器(番外)

uboot详解—PWM定时器(番外)1. 前言2. PWM定时器2.1 概述2.2 实例3. 小结 1. 前言   这篇文章是一些拓展内容,如果想了解uboot的启动过程,可以先跳过本章,如果需要掌握一些编写裸机程序或者内核与计时器相关的驱动的知识,可以了解一下。 2. PWM定时器 2.1 概述   脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量、通信到功率控制与变换的许多

一只立志于养老婆的程序猿 1369

这样开发PWM,保证你准时下班

摘要本篇笔记主要记录基于MBD模型设计的PWM输出步骤方法。前期工具箱的安装不在本文档讨论范围内。准备工作安装好MPC5744基于模型的工具箱支持包,这个可以在NXP官网下载2,按照文档配置编译器设置路径以及license激活,QSG里面写的很清楚,这里不在赘述。模型设计正确安装MBD工具箱后,在simulink里面可以看到到MPC5744的外设例程,这里我们设计...

wgp2hpp的博客 839

[转载]自己动手编写嵌入式Bootloader之(2)

第二部分:通过网口下载内核映像要实现通过网口下载文件的功能,从底层到上层需要做的工作包括:开发板上的网卡芯片的驱动程序;TCP/IP协议栈的实现;TFTP客户端应用程序的实现。我们使用的OK2440开发板配备CS8900A网卡芯片。 为了简单起见,网络数据包的发送接收都使用轮询方式,不使用中断;协议栈只使用ARP/IP/UDP协议,不涉及TCP及其他协议;应用程序

DerekYao的专栏 1217

[转载]自己动手编写嵌入式Bootloader之(1)

第一部分:基本功能流程CPU上电后会从IO空间的某地址取第一条指令。但此时:PLL没有启动,CPU工作频率为外部输入晶振频率,非常低;CPU工作模式、中断设置等不确定;存储空间的各个BANK(包括内存)都没有驱动,内存不能使用。在这种情况下必须在第一条指令处做一些初始化工作,这段初始化程序与操作系统独立分开,称之为bootloader。实际上,很少有必要自己写一个Bootloader,因为

DerekYao的专栏 1672

Bootloader

我认为学习应用u-boot对于刚接触的人来说最大的困难在于其功能比较全,文件(.S .c .h makefile文件)非常多,结构也比较复杂,难以理清各方面的关系。熟悉了一段时间之后,发现还是有很多的规律可以总结的。 一、首先要理解bootloader的作用,以及U-boot具体可以实现什么样的一些功能。 二、弄清U-boot的启动流程,这时候需要跟着整个程序走一遍,先不管实现这个流程的代码

颇锐克(公众号:颇锐克科技共享) 1万+

如何学习嵌入式bootloader

本文是摘自一个大侠对vivi源码的分析的一篇文章,个人觉得前面这些文字对那些苦苦挣扎在研究bootloader的同志们会有一些帮助,甚至是鼓励,所以充满感激地将它转载了过来。 通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,

u012138828的专栏 1009

自己动手bootloader

原创作品,允许转载转载时请务必以超链接形式标明文章 原始出处 、作者信息本声明。否则将追究法律责任。http://doublewen.blog.51cto.com/3097132/802508                                                 为了写一个bootloader让板子跑起来,首先我们要知道bootloader是个什么东东才行。简单

shangyaowei的专栏 608

嵌入式linux系统移植(一)

内容: 交叉编译环境 bootloader功能子系统 内核核心子系统 文件系统子系统要点: 搭建交叉编译环境 bootloader的选择移植 kernel的配置、编译、移植调试 根文件系统的制作 移植的基本步骤: 确定目标机、主机的连接方式 安装交叉编译环境 搭建主机-目标机的数据传输通道 编写三大子系统 烧写测试 目标机主机的连接方式...

a619861986的博客 492

ARM与嵌入式linux的入门建议 (六步曲)(转载)

由于很多人总问这个问题,所以这里做一个总结供大家参考。这里必须先说明,以下的步骤都是针对Linux系统的,并不面向WinCE。也许你会注意到,现在做嵌入式的人中,做linux研究的人远比做WinCE的人多,很多产家提供的资料也是以linux为主。我一直很难理解,其实WinCE的界面比linux的界面好看多了,使用起来也很方便,更为重要的是,WinCE的开发Windows下的开发基本一样,学起来简

只有不想做的,没有做不到的 1028

python datetime转时间戳

当前时间戳:time.time() 当前日期:time.ctime() 1、Python下日期到时间戳的转换 import datetime import time dateC=datetime.datetime(2010,6,6,8,14,59) timestamp=time.mktime(dateC.timetuple()) print timestamp 2、Python...

加菲猫小站 3142

嵌入式开发)自己写bootloader编写第二阶段

内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M。 那么uImage又是什么的?它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与...

weixin_30680385的博客 210

通过串口实现printfscanf函数

在做裸板开发时,常常需要通过输出或者通过串口输入一些信息。 在有操作系统机器上,我们很少关心输入输出的问题。因为有很多现成的库函数供我们调用。在做裸板开发时,可没有现成库函数供我们调用,一切都需要我们自己实现。 下面我们通过串口在裸板上实现一个printfscanf函数。 printf主要用来进行格式化输出,scanf函数主要用来进行格式化

maliqiqaz的专栏 1524

PWM 及定时器(定时器初始化成自动装填)

 涉及到的寄存器:TCFG0(DEAD ZONE ;PRESCALER1,PRESCALER0)                         TCFG1(DMA MODE;MUX4,3,2,1,0)                         TCON                         TCNTB0,TCMPB0,TCNTO0                 

wangwei452799的专栏 1347

从零开始写一个简单的bootloader(1)

前言         之前学习嵌入式裸板程序也有一定的时间了,而一个bootloader则是裸板程序的一个集大成者,能包含很多的知识点,所以编写一个bootloader能巩固之前的所学。废话少讲,下面就开始正式的编写。 目标       相信搞嵌入式的都知道bootloader是什么东西,这里我就不作详细介绍,简单说一下我编写bootloader要做什么工作。bootloader的终极目标...

lee_jimmy的博客 1万+
上一篇: [转载]自己动手编写嵌入式Bootloader之(2)
下一篇: boost源码剖析之:Tuple Types(rev#2)
NetGeek
博客等级 码龄21年 15粉丝 2原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值