第六部分 应用程序的移植
1构造目标板的根目录及文件系统
1.1 建立一个目标板的空根目录
我们将在这里构建构建根文件系统,创建基础目录结构. 存放交叉编译后生成的目标应用程序
(BUSYBOX,TINYLOGIN),存放库文件等。
[arm@localhost rootfs]# mkdir my_rootfs
[arm@localhost rootfs]# pwd
/home/arm/dev_home/rootfs/my_rootfs
[arm@localhost rootfs]# cd my_rootfs
[arm@localhost my_rootfs]#
1.2 在my_rootfs中建立Linux目录树
[arm@localhost my_rootfs]#mkdir bin dev etc home lib mnt proc sbin sys tmp root usr
[arm@localhost my_rootfs]#mkdir mnt/etc
[arm@localhost my_rootfs]#mkdir usr/bin usr/lib usr/sbin
[arm@localhost my_rootfs]#touch linuxrc
[arm@localhost my_rootfs]#tree
|bin
|dev
|etc
|home
|lib
|linuxrc
/* 此文件为启动脚本,是一shell脚本文件。本文后面有专门介绍 */
|mnt
| `etc
|proc
|sbin
|sys
|tmp
|root
`usr
|bin
|lib
`sbin
权限参照你的linux工作站即可,基础目录介绍参见本文参考资料(未尾)。
需要说明的一点就是etc目录存放配置文件,这个目录通常是需要修改的,所以在linuxrc脚本当中将etc目录
挂载为ramfs文件系统,然后将mnt/etc目录中的所有配置文件拷贝到etc目录当中,这在下一节的linuxrc脚本
文件当中会有体现。
1.3 创建linuxrc文件
1. 创建linuxrc,加入如下内容:
[arm@localhost my_rootfs]#vi linuxrc
#!/bin/sh
#挂载/etc为ramfs, 并从/mnt/etc下拷贝文件到/etc目录当中
echo "mount /etc as ramfs"
/bin/mount n
t
ramfs ramfs /etc
/bin/cp a
/mnt/etc/* /etc
echo "recreate
the /etc/mtab entries"
# recreate
the /etc/mtab entries
/bin/mount f
t
cramfs o
remount,ro /dev/mtdblock/2 /
#mount some file system
echo "mount
/dev/shm as tmpfs"
/bin/mount n
t
tmpfs tmpfs /dev/shm
#挂载/proc为proc文件系统
echo "mount
/proc as proc"
/bin/mount n
t
proc none /proc
#挂载/sys为sysfs文件系统
echo "mount
/sys as sysfs"
/bin/mount n
t
sysfs none /sys
exec /sbin/init
2. 修改权限
[arm@localhost my_rootfs]#chmod 775 linuxrc
[arm@localhost my_rootfs]#ls linuxrc al
rwxrwxrx
1 root root 533 Jun 4 11:19 linuxrc
当编译内核时,指定命令行参数如下
Boot options >
Default kernel command string: 我的命令行参数如下
noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200
其中的init指明kernel执行后要加载的第一个应用程序,缺省为/sbin/init,此处指定为/linuxrc
2 移植Busybox
2.1 下载busybox
从http://www.busybox.net/downloads/busybox1.1.3.
tar.gz/下载busybox1.1.3
到/tmp目录当中,并解压.
2.2 进入解压后的目录,配置Busybox
[arm@localhost busybox1.1.3]$
make menuconfig
Busybox Settings >
General Configuration >
[*] Support for devfs
Build Options >
[*] Build BusyBox as a static binary (no shared libs)
/* 将busybox编译为静态连接,少了启动时找动态库的麻烦 */
[*] Do you want to build BusyBox with a Cross Compiler?
(/usr/local/arm/3.3.2/bin/armlinux)
Cross Compiler prefix
/* 指定交叉编译工具路径 */
Init Utilities >
[*] init
[*] Support reading an inittab file
/* 支持init读取/etc/inittab配置文件,一定要选上 */
Shells >
Choose your default shell (ash) >
/* (X) ash 选中ash,这样生成的时候才会生成bin/sh文件
* 看看我们前头的linuxrc脚本的头一句:
* #!/bin/sh 是由bin/sh来解释执行的
*/
[*] ash
Coreutils >
[*] cp
[*] cat
[*] ls
[*] mkdir
[*] echo (basic SuSv3 version taking no options)
[*] env
[*] mv
[*] pwd
[*] rm
[*] touch
Editors >
[*] vi
Linux System Utilities >
[*] mount
[*] umount
[*] Support loopback mounts
[*] Support for the old /etc/mtab file
Networking Utilities >
[*] inetd
/*
* 支持inetd超级服务器
* inetd的配置文件为/etc/inetd.conf文件,
* "在该部分的4: 相关配置文件的创建"一节会有说明
*/
2.3 编译并安装Busybox
[arm@localhost busybox1.1.3]$
make TARGET_ARCH=arm CROSS=armlinux\
PREFIX=/home/arm/dev_home/rootfs/my_rootfs/ all install
PREFIX指明安装路径:就是我们根文件系统所在路径。
*这里需要注意一点的是,只要install busybox,我们根文件系统下先前建好的linuxrc就会被覆盖为一同名二进
制文件。
所以要事先备份我们自己的linuxrc,在安装完busybox后,将linuxrc复制回去就好。
3 移植TinyLogin
3.1 下载
从http://tinylogin.busybox.net/downloads/tinylogin1.4.
tar.bz2 下载tinylogin1.4
到/tmp目录当中,并解压.
3.2 修改tinyLogin的Makefile
[arm@localhost tinylogin1.4]$
vi Makefile
修改记录如下:
指明静态编译,不连接动态库
DOSTATIC = true
指明tinyLogin使用自己的算法来处理用户密码
USE_SYSTEM_PWD_GRP = false
USE_SYSTEM_SHADOW = false
3.3 编译并安装
[root@localhost tinylogin1.4]#
make CROSS=armlinuxPREFIX=/
home/arm/dev_home/rootfs/my_rootfs all install
PREFIX指明根文件路径
4 相关配置文件的创建
进入mnt/etc中, 这里是我们存放配置文件的路径
[arm@localhost my_rootfs]$ cd mnt/etc
4.1 创建帐号及密码文件
[arm@localhost etc]$ cp /etc/passwd .
[arm@localhost etc]$ cp /etc/shadow .
[arm@localhost etc]$ cp /etc/group .
这3个文件是从你工作站当中拷贝过来的,删除其中绝大部分不需要的用户,经过删减后的上述3个文件如下。
那么现在root的登陆密码
和你工作站上的登陆口令一致了,这可能透露你工作站的信息
[arm@localhost etc]$ cat passwd
root:x:0:0:root:/root:/bin/sh /* 改为/bin/sh */
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[arm@localhost etc]$ cat shadow
root:$1$2LG20u89$UCEEUzBhElYpKMNZQPU.e1:13303:0:99999:7:::
bin:*:13283:0:99999:7:::
daemon:*:13283:0:99999:7:::
[arm@localhost etc]$ cat group
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
4.2 创建profile文件
[arm@localhost etc]$ vi profile
# Set search library path
# 这条语句设置动态库的搜索路径,极其重要!!!
echo "Set search library path int /etc/profile"
export LD_LIBRARY_PATH=/lib:/usr/lib
# Set user path
echo "Set user path in /etc/profile"
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH
4.4 创建fstab文件
[arm@localhost etc]$ vi fstab
none /proc proc defaults 0 0
none /dev/pts devpts mode=0622 0 0
tmpfs /dev/shm tmpfs defaults 0 0
4.5 创建inetd.conf配置文件
此处只是一个经过修改的示例配置文件,用于代理监听telnetd的23端口。
读者可以根据自己需求进行修改。该配置文件可以从busybox的examples目录中获得
[arm@localhost etc]$ vi inetd.conf
# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
#ftp stream tcp nowait root /usr/sbin/ftpd
telnet stream tcp nowait root /usr/sbin/telnetd
5 移植inetd
5.1 inetd的选择及获取
Busybox1.1.3提供了inetd支持。如果读者使用的是较低版本的不提供inetd的Busybox,那么可以考虑使
用netkit套件来提供网络服务。强烈建议使用高版本的Busybox。此节后半部分介绍如果编译布署netkit当中的
inetd。
5.1.1 获取inetd
Netkit套件可以从ftp://ftp.uk.linux.org/pub/linux/Networking/netkit/下载。
其中netkitbase0.17
中包括inetd程序。下载netkitbase0.17
到/tmp目录并解压。
5.2 编译inetd
5.2.1 修改configure文件
开始配置netkitbase
之前需要先修改configure脚本以免它在主机上执行测试程序。
[arm@localhost netkitbase0.17]#
vi configure
将每一行出现的 ./__conftest || exit 1;
修改成:
# ./__conftest || exit 1;
5.2.2 编译
[arm@localhost netkitbase0.17]$
CC=armlinuxgcc
./configure
[arm@localhost netkitbase0.17]$
make
5.3 配置inetd
5.3.1 拷贝inetd到根文件系统的usr/sbin目录中
[arm@localhost netkitbase0.17]$
cp inetd/inetd /home/arm/dev_home/rootfs/my_rootfs/usr/sbin/
拷贝inetd的配置文件inetd.conf到根文件系统的/mnt/etc目录中
[arm@localhost netkitbase0.17]$
cp etc.sample/inetd.conf /home/arm/dev_home/rootfs/my_rootfs/mnt/etc
5.3.2 根据需要,修改inetd.conf配置文件
例如:支持telnetd的inetd.conf配置文件如下
# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
telnet stream tcp nowait root /usr/sbin/telnetd
5.3.3 拷贝配置文件
etc.sample目录下有许多网络相关配置文件,其中有一些需要拷贝到根文件系统的etc目录当中,记录如下:
[arm@localhost netkitbase0.17]$
cd etc.sample/
[arm@localhost etc.sample]$ cp host.conf /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
[arm@localhost etc.sample]$ cp hosts /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
[arm@localhost etc.sample]$ cp networks /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
[arm@localhost etc.sample]$ cp protocols /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
[arm@localhost etc.sample]$ cp resolv.conf /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
[arm@localhost etc.sample]$ cp services /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
以上重要配置文件说明如下:
host.conf:在系统中同时存在着DNS域名解析和/etc/hosts的主机表机制时,由文件/etc/host.conf来说明了解析器
的查询顺序
hosts:记录主机名到IP地址的映射
protocols:记录常用网络协议及端口别名关系,网络应用程序依赖于此文件
resolv.conf:指定DNS服务器
services:记录知名网络服务及端口,网络编程依赖于此文件
6 移植thttpd Web服务器
6.1 下载
从http://www.acme.com/software/thttpd/ 下载thttpd到/tmp目录当中,并解压.
6.2 编译thttpd
[arm@localhost thttpd2.25b]$
CC=armlinuxgcc
./configure host=
armlinux
[arm@localhost thttpd2.25b]$
vi Makefile
指定静态链接二进制文件
LDFLAGS = static
[arm@localhost thttpd2.25b]$
make LDFLAGS="static"
6.3 配置
6.3.1 拷贝thttpd二进制可执行文件到根文件系统/usr/sbin/目录中
[arm@localhost thttpd2.25b]$
cp thttpd /home/arm/dev_home/rootfs/my_rootfs/usr/sbin/
6.3.2 修改thttpd配置文件
[arm@localhost thttpd2.25b]$
vi contrib/redhatrpm/
thttpd.conf
# This section overrides defaults
dir=/etc/thttpd/html #指明WebServer存放网页的根目录路径
chroot
user=root #以root身份运行thttpd
logfile=/etc/thttpd/log/thttpd.log #日志文件路径
pidfile=/etc/thttpd/run/thttpd.pid #pid文件路径
拷贝thttpd.conf配置文件到根文件系统的mnt/etc/目录,
系统加载后,linuxrc脚本会自动将mnt/etc/下的所有文件拷贝到/etc目录中。
[arm@localhost thttpd2.25b]$
cp contrib/redhatrpm/
thttpd.conf /home/arm/dev_home/rootfs/my_rootfs/mnt/etc/
6.3.3 转移到根文件系统目录,创建相应的文件
[arm@localhost etc]$ cd /home/arm/dev_home/rootfs/my_rootfs
[arm@localhost my_rootfs]$ cd mnt/etc/
创建thttpd目录
[arm@localhost etc]$ mkdir thttpd
[arm@localhost etc]$ cd thttpd
thttpd目录下的目录结构
|html
| `index.
html Web Server网页根目录下的默认HTML文件
|log
| `thttpd.
log 创建一个空文件就可
`run
`thttpd.
pid 创建一个空文件就可
html目录下的index.html文件内容如下:
<html>
<head>
<title> Welcome to here^^ </title>
</head>
<body>
<marquee>
<font color=red>
Welcome to here^^!!!
</font>
</marquee>
</body>
</html>
7 建立根目录文件系统包
7.1 建立CRAMFS包
7.1.1 下载cramfs工具
从http://prdownloads.sourceforge.net/cramfs/cramfs1.1.
tar.gz 下载源代码包.
把下载包拷贝到dev_home/tools下.
[arm@localhost tools]$tar xzvf
cramfs1.1.
tar.gz
[arm@localhost tools]$cd cramfs1.1
[arm@localhost tools]$make
[arm@localhost tools]$su root
[root@localhost tools]$cp mkcramfs /usr/bin
[arm@localhost tools]$exit
注意:如果你的系统中已经安装了mkcramfs工具, 则在/usr/bin目录下是一个软link, 请先删除该文件之后, 再拷
贝该mkcramfs到/usr/bin下.
7.1.2 制作cramfs包
[arm@localhost tools]$mkcramfs my_rootfs my_rootfs.cramfs
7.1.3 写cramfs包到Nand Flash
[arm@localhost tools]$su root
[root@localhost tools]$cp my_rootfs.cramfs /tftpboot/
打开minicom, 进行ARM板的终端模式:
CRANE2410 #
8 参考资料
1. linux目录结构介绍
http://www.uplooking.com/content/view/1487/2/
2. <<BUILDING EMBEDDED LINUX SYSTEMS>>
中文名:<<构建嵌入式Linux系统>>
第七部分 Nand flash驱动的编写与移植
1 Nand flash工作原理
S3C2410板的Nand Flash支持由两部分组成:Nand Flash控制器(集成在S3C2410 CPU)和Nand Flash存储
芯片(K9F1208U0B)两大部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命
令才能完成。所以, Nand Flash相当于S3C2410的一个外设,而不位于它的内存地址区.
1.1 Nand flash芯片工作原理
Nand flash芯片型号为Samsung K9F1208U0B,数据存储容量为64MB,采用块页式存储管理。8个I/O
引脚充当数据、地址、命令的复用端口。
1.1.1 芯片内部存储布局及存储操作特点
一片Nand flash为一个设备(device), 其数据存储分层为:
1设备(Device) = 4096 块(Blocks)
1块(Block) = 32页/行(Pages/rows) ;页与行是相同的意思,叫法不一样
1块(Page) = 528字节(Bytes) = 数据块大小(512Bytes) + OOB块大小(16Bytes)
在每一页中,最后16个字节(又称OOB)用于Nand Flash命令执行完后设置状态用,剩余512个字节又
分为前半部分和后半部分。可以通过Nand Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位通过
Nand Flash内置的指针指向各自的首地址。
存储操作特点:
1. 擦除操作的最小单位是块。
2. Nand Flash芯片每一位(bit)只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前要一定将相应
块擦除(擦除即是将相应块得位全部变为1).
3. OOB部分的第六字节(即517字节)标志是否是坏块,如果不是坏块该值为FF,否则为坏块。
4. 除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码(关于硬件ECC码请参看
Nandflash 控制器一节).
1.1.2 重要芯片引脚功能
I/O0I/
O7:复用引脚。可以通过它向nand flash芯片输入数据、地址、nand flash命令以及输出数据和操作
状态信息。
CLE(Command Latch Enable): 命令锁存允许
ALE(Address Lactch Enable): 地址锁存允许
CE:
芯片选择
RE:
读允许
WE:
写允许
WP:
在写或擦除期间,提供写保护
R/B:
读/忙输出
1.1.3 寻址方式
Samsung K9F1208U0B Nand Flash 片内寻址采用26位地址形式。从第0位开始分四次通过I/O0-I/O7进行
传送,并进行片内寻址。具体含义如下:
0-7位:字节在上半部、下半部及OOB内的偏移地址
8位:值为0代表对一页内前256个字节进行寻址
值为1代表对一页内后256个字节进行寻址
9-13位:对页进行寻址
14-25位:对块进行寻址
当传送地址时,从位0开始
1.1.4 Nand flash主要内设命令详细介绍
Nand Flash命令执行是通过将命令字送到Nand Flash控制器的命令寄存器来执行。
Nand Flash的命令是分周期执行的,每条命令都有一个或多个执行周期,每个执行周期都有相映代码表示该周
期将要执行的动作。
主要命令有:Read 1、Read 2、Read ID、Reset、Page Program、Block Erase、Read Status。
详细介绍如下:
1. Read 1:
功能:表示将要读取Nand flash存储空间中一个页的前半部分,并且将内置指针定位到前半部分的第一个字节。
命令代码:00h
2. Read 2:
功能:表示将要读取Nand flash存储空间中一个页的后半部分,并且将内置指针定位到后半部分的第一个字节。
命令代码:01h
3. Read ID:
功能:读取Nand flash芯片的ID号
命令代码:90h
4. Reset:
功能:重启芯片。
命令代码:FFh
5. Page Program:
功能:对页进行编程命令, 用于写操作。
命令代码:首先写入00h(A区)/01h(B区)/05h(C区), 表示写入那个区; 再写入80h开始编程模式(写入模式),接
下来写入地址和数据; 最后写入10h表示编程结束.
6. Block Erase
功能:块擦除命令。
命令代码:首先写入60h进入擦写模式,然后输入块地址; 接下来写入D0h, 表示擦写结束.
7. Read Status
功能:读取内部状态寄存器值命令。
命令代码:70h
1.2 Nand Flash 控制器工作原理
对Nand Flash存储芯片进行操作, 必须通过Nand Flash控制器的专用寄存器才能完成。所以,不能对Nand
Flash进行总线操作。而Nand Flash的写操作也必须块方式进行。对Nand Flash的读操作可以按字节读取。
1.2.1 Nand Flash控制器特性
1. 支持对Nand Flash芯片的读、检验、编程控制
2. 如果支持从Nand Flash启动, 在每次重启后自动将前Nand Flash的前4KB数据搬运到ARM的内部RAM中
3. 支持ECC校验
1.2.2 Nand Flash控制器工作原理
Nand Flash控制器在其专用寄存器区(SFR)地址空间中映射有属于自己的特殊功能寄存器,就是通过将Nand
Flash芯片的内设命令写到其特殊功能寄存器中,从而实现对Nand flash芯片读、检验和编程控制的。特殊功能
寄存器有:NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT、NFECC。寄存详细说明见下一节。
1.3 Nand flash 控制器中特殊功能寄存器详细介绍
1. 配置寄存器(NFCONF)
功能:用于对Nand Flash控制器的配置状态进行控制。
在地址空间中地址:0x4E000000,其中:
Bit15:Nand Flash控制器使能位,置0代表禁止Nand Flash控制器,置1代表激活Nand Flash控制器;
要想访问Nand Flash芯片上存储空间,必须激活Nand Flash控制器。在复位后该位自动置0,因此在初始化时
必须将该位置为1。
Bit12:初始化ECC位,置1为初始化ECC;置0为不初始化ECC。
Bit11:Nand Flash芯片存储空间使能位,置0代表可以对存储空间进行操作;置1代表禁止对存储空
间进行操作。在复位后,该位自动为1。
Bit10-8:TACLS位。根据此设定CLE&ALE的周期。TACLS的值范围在0-7之间。
Bit6-4、2-0分别为:TWRPH0、TWRPH1位。设定写操作的访问周期。其值在0-7之间。
2. 命令寄存器(NFCMD)
功能:用于存放Nand flash芯片内设的操作命令。
在地址空间中地址:0x4E000004,其中:
Bit0-7:存放具体Nand flash芯片内设的命令值。其余位保留以后用。
3. 地址寄存器(NFADDR)
功能:用于存放用于对Nand flash芯片存储单元寻址的地址值。
在地址空间中地址:0x4E000008,其中:
Bit0-7:用于存放地址值。因为本款Nand flash芯片只有I/O0-7的地址/数据复用引脚且地址是四周
期每次8位送入的,所以这里只用到8位。其余位保留待用。
4. 数据寄存器(NFDATA)
功能:Nand flash芯片所有内设命令执行后都会将其值放到该寄存器中。同时,读出、写入Nand flash
存储空间的值也是放到该寄存器。
在地址空间中地址:0x4E00000C,其中:
Bit0-7:用于存放需要读出和写入的数据。其余位保留代用。
5. 状态寄存器(NFSTAT)
功能:用于检测Nand flash芯片上次对其存储空间的操作是否完成。
在地址空间中地址:0x4E000010,其中:
Bit0:置0表示Nand flash芯片正忙于上次对存储空间的操作;置1表示Nand flash芯片准备好接收新
的对存储空间操作的请求。
6. ECC校验寄存器(NFECC)
功能:ECC校验寄存器
在地址空间中地址:0x4E000014,其中:
Bit0Bit7:
ECC0
Bit8Bit15:
ECC1
Bit16Bit23:
ECC2
1.4 Nand Flash 控制器中的硬件ECC介绍
1.4.1 ECC产生方法
ECC是用于对存储器之间传送数据正确进行校验的一种算法,分硬件ECC和软件ECC算法两种,在
S3C2410的Nand Flash 控制器中实现了由硬件电路(ECC 生成器)实现的硬件ECC。
1.4.2 ECC生成器工作过程
当写入数据到Nand flash存储空间时, ECC生成器会在写入数据完毕后自动生成ECC码,将其放入到
ECC0-ECC2。当读出数据时Nand Flash 同样会在读数据完毕后,自动生成ECC码将其放到ECC0-ECC2当
中。
1.4.3 ECC的运用
当写入数据时,可以在每页写完数据后将产生的ECC码放入到OOB指定的位置(Byte 6)去,这样就完成了
ECC码的存储。这样当读出该页数据时,将所需数据以及整个OOB读出,然后将指定位置的ECC码与读出数
据后在ECC0-ECC1的实际产生的ECC码进行对比,如果相等则读出正确,若不相等则读取错误需要进行重
读。
2 在ADS下flash烧写程序
2.1 ADS下flash烧写程序原理及结构
基本原理:在windows环境下借助ADS仿真器将在SDRAM中的一段存储区域中的数据写到Nand flash存
储空间中。烧写程序在纵向上分三层完成:
第一层: 主烧写函数(完成将在SDRAM中的一段存储区域中的数据写到Nand flash存储空间中);
第二层: 为第一层主烧写函数提供支持的对Nand flash进行操作的页读、写,块擦除等函数;
第三层:为第二层提供具体Nand flash控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正的
将数据能够在SDRAM和Nand flash之间实现传送的函数。
下面对其三层进行分述:
2.2 第三层实现说明
2.1.1 特殊功能寄存器定义
#define rNFCONF (*(volatile unsigned int *)0x4e000000)
#define rNFCMD (*(volatile unsigned char *)0x4e000004)
#define rNFADDR (*(volatile unsigned char *)0x4e000008)
#define rNFDATA (*(volatile unsigned char *)0x4e00000c)
#define rNFSTAT (*(volatile unsigned int *)0x4e000010)
#define rNFECC (*(volatile unsigned int *)0x4e000014)
#define rNFECC0 (*(volatile unsigned char *)0x4e000014)
#define rNFECC1 (*(volatile unsigned char *)0x4e000015)
#define rNFECC2 (*(volatile unsigned char *)0x4e000016)
2.1.2 操作的函数实现
1. 发送命令
#define NF_CMD(cmd) {rNFCMD=cmd;}
2. 写入地址
#define NF_ADDR(addr) {rNFADDR=addr;}
3. Nand Flash芯片选中
#define NF_nFCE_L() {rNFCONF&=~(1<<11);}
4. Nand Flash芯片不选中
#define NF_nFCE_H() {rNFCONF|=(1<<11);}
5. 初始化ECC
#define NF_RSTECC() {rNFCONF|=(1<<12);}
6. 读数据
#define NF_RDDATA() (rNFDATA)
7. 写数据
#define NF_WRDATA(data) {rNFDATA=data;}
8. 获取Nand Flash芯片状态
#define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));}
0/假: 表示Nand Flash芯片忙状态
1/真:表示Nand Flash已经准备好
2.3 第二层实现说明
2.3.1 Nand Flash 初始化
void NF_Init(void)
{
/* 设置Nand Flash配置寄存器, 每一位的取值见1.3节 */
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
/* 复位外部Nand Flash芯片 */
NF_Reset();
}
2.3.2 Nand flash复位
static void NF_Reset(void)
{
int i;
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0xFF); /* 复位命令 */
for(i=0;i<10;i++); /* 等待tWB = 100ns. */
NF_WAITRB(); /* wait 200~500us; */
NF_nFCE_H(); /* 取消Nand Flash 选中*/
}
2.3.3 获取Nand flash ID
返回值为Nand flash芯片的ID号
unsigned short NF_CheckId(void)
{
int i;
unsigned short id;
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x90); /* 发送读ID命令到Nand Flash芯片 */
NF_ADDR(0x0); /* 指定地址0x0,芯片手册要求 */
for(i=0;i<10;i++); /* 等待tWB = 100ns. */
id=NF_RDDATA()<<8; /* 厂商ID(K9S1208V:0xec) */
id|=NF_RDDATA(); /* 设备ID(K9S1208V:0x76) */
NF_nFCE_H(); /* 取消Nand Flash 选中*/
return id;
}
2.3.4 Nand flash写入
以页为单位写入.
参数说明:block 块号
page 页号
buffer 指向内存中待写入Nand flash中的数据起始位置
返回值: 0:写错误
1:写成功
static int NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer)
{
int i;
unsigned int blockPage = (block<<5)+page;
unsigned char *bufPt = buffer;
NF_RSTECC(); /* 初始化 ECC */
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x0); /* 从A区开始写 */
NF_CMD(0x80); /* 写第一条命令 */
NF_ADDR(0); /* A0~A7 位(Column Address) */
NF_ADDR(blockPage&0xff); /* A9A16,
(Page Address) */
NF_ADDR((blockPage>>8)&0xff); /* A17A24,
(Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
for(i=0;i<512;i++)
{
NF_WRDATA(*bufPt++); /* 写一个页512字节到Nand Flash芯片 */
}
/*
* OOB一共16 Bytes, 每一个字节存放什么由程序员自己定义, 通常,
* 我们在Byte0Byte2
存ECC检验码. Byte6 存放坏块标志.
*/
seBuf[0]=rNFECC0; /* 读取ECC检验码0 */
seBuf[1]=rNFECC1; /* 读取ECC检验码1 */
seBuf[2]=rNFECC2; /* 读取ECC检验码2 */
seBuf[5]=0xff; /* 非坏块标志 */
for(i=0;i<16;i++)
{
NF_WRDATA(seBuf[i]); /* 写该页的OOB数据块 */
}
NF_CMD(0x10); /* 结束写命令 */
/* 等待Nand Flash处于准备状态 */
for(i=0;i<10;i++);
NF_WAITRB();
/* 发送读状态命令给Nand Flash */
NF_CMD(0x70);
for(i=0;i<3;i++);
if (NF_RDDATA()&0x1)
{ /*如果写有错, 则标示为坏块 */
NF_nFCE_H(); /* 取消Nand Flash 选中*/
NF_MarkBadBlock(block);
return 0;
} else { /* 正常退出 */
NF_nFCE_H(); /* 取消Nand Flash 选中*/
return 1;
}
}
2.3.5 Nand flash读取
参数说明:block:块号
page:页号
buffer:指向将要读取到内存中的起始位置
返回值:1:读成功
0:读失败
static int NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer)
{
int i;
unsigned int blockPage;
unsigned char ecc0, ecc1, ecc2;
unsigned char *bufPt=buffer;
unsigned char se[16];
page=page&0x1f;
blockPage=(block<<5)+page;
NF_RSTECC(); /* 初始化 ECC */
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x00); /* 从A区开始读 */
NF_ADDR(0); /* A0~A7 位(Column Address) */
NF_ADDR(blockPage&0xff); /* A9A16,
(Page Address) */
NF_ADDR((blockPage>>8)&0xff); /* A17A24,
(Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
/* 等待Nand Flash处于再准备状态 */
for(i=0;i<10;i++);
NF_WAITRB(); /*等待 tR(max 12us) */
/* 读整个页, 512字节 */
for(i=0;i<512;i++)
{
*bufPt++=NF_RDDATA();
}
/* 读取ECC码 */
ecc0=rNFECC0;
ecc1=rNFECC1;
ecc2=rNFECC2;
/* 读取该页的OOB块 */
for(i=0;i<16;i++)
{
se[i]=NF_RDDATA();
}
NF_nFCE_H(); /* 取消Nand Flash 选中*/
/* 校验ECC码, 并返回 */
if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2])
return 1;
else
return 0;
}
2.3.6 Nand flash标记坏块
如果是坏块, 通过写OOB块的Byte6把该块标记为坏块。
参数说明:block块号
返回值:1:ok,成功完成标记。
0:表示写OOB块正确.
static int NF_MarkBadBlock(unsigned int block)
{
int i;
unsigned int blockPage=(block<<5);
seBuf[0]=0xff;
seBuf[1]=0xff;
seBuf[2]=0xff;
seBuf[5]=0x44; /* 设置坏块标记 */
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x50); /* 从C区开始写 */
NF_CMD(0x80); /* 发送编程命令, 让Nand Flash处理写状态 */
NF_ADDR(0x0); /* A0~A7 位(Column Address) */
NF_ADDR(blockPage&0xff); /* A9A16,
(Page Address) */
NF_ADDR((blockPage>>8)&0xff); /* A17A24,
(Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
/* 写OOB数据块 */
for(i=0;i<16;i++)
{
NF_WRDATA(seBuf[i]);
}
NF_CMD(0x10); /* 结束写命令 */
/* 等待NandFlash准备好 */
for(i=0;i<10;i++); /* tWB = 100ns. */
NF_WAITRB();
/*读NandFlash的写状态 */
NF_CMD(0x70);
for(i=0;i<3;i++); /* twhr=60ns */
if (NF_RDDATA()&0x1)
{
NF_nFCE_H(); /* 取消Nand Flash 选中*/
return 0;
} else {
NF_nFCE_H(); /* 取消Nand Flash 选中*/
}
return 1;
}
2.3.7 Nand Flash检查坏块
检查指定块是否是坏块.
参数说明:block:块号
返回值:1:指定块是坏块
0:指定块不是坏块。
static int NF_IsBadBlock(U32 block)
{
int i;
unsigned int blockPage;
U8 data;
blockPage=(block<<5);
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x50); /* Read OOB数据块 */
NF_ADDR(517&0xf); /* A0~A7 位(Column Address) */
NF_ADDR(blockPage&0xff); /* A9A16,
(Page Address) */
NF_ADDR((blockPage>>8)&0xff); /* A17A24,
(Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
/* 等待NandFlash准备好 */
for(i=0;i<10;i++); /* wait tWB(100ns) */
NF_WAITRB();
/* 读取读出值 */
data=NF_RDDATA();
NF_nFCE_H(); /* 取消Nand Flash 选中*/
/* 如果data不为0xff时, 表示该块是坏块 */
if(data != 0xff)
return 1;
else
return 0;
}
2.3.8 擦除指定块中数据
参数说明:block 块号
返回值:0:擦除错误。(若是坏块直接返回0;若擦除出现错误则标记为坏块然后返回0)
1:成功擦除。
static int NF_EraseBlock(unsigned int block)
{
unsigned int blockPage=(block<<5);
int i;
/* 如果该块是坏块, 则返回 */
if(NF_IsBadBlock(block))
return 0;
NF_nFCE_L(); /* 片选Nand Flash芯片*/
NF_CMD(0x60); /* 设置擦写模式 */
NF_ADDR(blockPage&0xff); /* A9A16,
(Page Address) , 是基于块擦*/
NF_ADDR((blockPage>>8)&0xff); /* A17A24,
(Page Address) */
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */
NF_CMD(0xd0); /* 发送擦写命令, 开始擦写 */
/* 等待NandFlash准备好 */
for(i=0;i<10;i++); /* tWB(100ns) */
NF_WAITRB();
/* 读取操作状态 */
NF_CMD(0x70);
if (NF_RDDATA()&0x1)
{
NF_nFCE_H(); /* 取消Nand Flash 选中*/
NF_MarkBadBlock(block); /* 标记为坏块 */
return 0;
} else {
NF_nFCE_H(); /* 取消Nand Flash 选中*/
return 1;
}
}
2.4 第一层的实现
2.4.1 NandFlash烧写主函数说明
参数说明: block 块号
srcAddress SDRAM中数据起始地址
fileSize 要烧写的数据长度
返回值: 无
void K9S1208_Program(unsigned int block, unsigned int srcAddress, unsigned int fileSize)
{
int i;
int programError=0;
U32 blockIndex;
U8 *srcPt, *saveSrcPt;
srcPt=(U8 *)srcAddress; /* 文件起始地址 */
blockIndex = block; /* 块号 */
while(1)
{
saveSrcPt=srcPt;
/* 如果当前块是坏块, 跳过当前块 */
if(NF_IsBadBlock(blockIndex))
{
blockIndex++; /* 到下一个块 */
continue;
}
/* 在写之前, 必须先擦除, 如果擦除不成功, 跳过当前块 */
if(!NF_EraseBlock(blockIndex))
{
blockIndex++; /* 到下一个块 */
continue;
}
/* 写一个块, 一块有32页 */
for(i=0;i<32;i++)
{
/* 写入一个页, 如果出错, 停止写当前块 */
if(!NF_WritePage(blockIndex,i,srcPt))
{
programError=1;
break;
}
/* 如果操作正常, 文件的写位置加上1页偏移,到下一页的起始位置 */
srcPt+=512;
/* 如果写地址没有超过文件长度, 继续; 超出则终止写 */
if((U32)srcPt>=(srcAddress+fileSize))
break;
}
/* 如果写一个块时, 其中某一页写失败, 则把写地址恢复写该块之前, 并跳过当前块 */
if(programError==1)
{
blockIndex++;
srcPt=saveSrcPt;
programError=0;
continue;
}
/* 如果写地址没有超过文件长度, 继续; 超出则终止写 */
if((U32)srcPt >= (srcAddress+fileSize))
break;
/* 如果正常写成功, 继续写下一个块 */
blockIndex++;
}
}
Logo

更多推荐