安装工具:
1.安装远程(串口/ssh)调试工具:Secure CRT.exe
2.安装FTP服务器软件:CuteFTP Professional.exe
3..安装TFTP文件传输工具:Tftpd32.exe
韦东山的uboot的相关指令:
1.q--退出菜单;
2.menu--进入菜单;
3.print--打印环境变量;
4.set--设置相关参数;
5.save--保存;
6.reset--重启U-boot;
7.md.w
8.直接回车则会执行上一次指令;
9.help--短帮助信息;help
10.mtd--查看flash分区;
一、使用TFTP工具在Nandflash上烧写裸板程序
1.0.安装远程(串口/ssh)调试工具:Secure CRT.exe
从Norflash启动。
通过串口连接目标板,操作uboot.
1.1.OpenJTAG> print
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
bootdelay=2
baudrate=115200
ethaddr=08:00:3e:26:0a:5b
netmask=255.255.255.0
mtdids=nand0=nandflash0
mtdparts=mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)
stdin=serial
stdout=serial
stderr=serial
partition=nand0,0
mtddevnum=0
mtddevname=bootloader
ipaddr=192.168.10.4
serverip=192.168.10.2
Environment size: 443/131068 bytes
1.2.设置windows和目标板uboot的IP
a.设置PC网卡的静态IP为:
192.168.10.2
255.255.255.0
192.168.10.1
DNS不用设置
只有连接上目标板后,PC才能识别到目标板所在的以太网段,而且和无线外网并不冲突。正常情况下,通过能上网的PC输入“ipconfig all”可查看当前网络的网关和DNS。关闭防火墙,否则无法从虚拟机ping到Windows。
配置防火墙:sudo ufw allow from 192.168.10.1
b.设置目标板的静态IP和Windows服务器的IP:
OpenJTAG> set ipaddr 192.168.10.4
OpenJTAG> set serverip 192.168.10.2 // 此处为TFTP Windows 服务器IP
OpenJ
1.3.打开Tftpd32.exe
选择文件夹中的目标文件lcd.bin和服务器地址192.168.10.2
1.4.烧写uboot代码
a.tftp 30000000 lcd.bin
b.nand erase bootloader
c.nand write 30000000 bootloader
二、使用TFTP工具(Tftpd32.exe)在Nandflash上重烧整个系统
2.1.norflash上的uboot给nandflash烧写uboot
a.tftp 30000000 u-boot.bin
b.nand erase bootloader
c.nand write 30000000 bootloader
2.2.norflash上的uboot给nandflash烧写内核
a.tftp 30000000 uImage
b.nand erase kernel
c.nand write.jffs2 30000000 kernel // write.jffs2防止位翻转
2.3.norflash上的uboot给nandflash烧写文件系统
a.tftp 30000000 fs_qtopia.yaffs2 // tftp 30000000 fs.yaffs2
b.nand erase root
c.nand write.yaffs 30000000 0x00260000 $(filesize) // write.yaffs 防止位翻转
or
a.tftp 30000000 fs_qtopia.jffs2
b.nand erase root
c.nand write.jffs2 30000000 0x00260000 $(filesize) // write.yaffs2防止位翻转
使用NFS在Nandflash上重烧整个系统
2.4.Linux服务器下通过nfs给nandflash烧写内核和文件系统
a.nfs 30000000 192.168.10.3:/work/system/linux-2.6.22.6/arch/arm/boot/uImage // 失败
b.nand erase kernel
c.nand write.jffs2 30000000 kernel // write.jffs2防止位翻转
a.nfs 30000000 192.168.10.3:/work/nfs_root/tmp/fs.yaffs2
b.nand erase root
c.nand write.yaffs 30000000 0x00260000 $(filesize) // write.yaffs 防止位翻转
使用Linux上的DNW重烧整个系统
2.5.把Linux上的DNW考到服务器的/bin下,在服务器进行如下操作:
$ sudo chmod +x /bin/dnw
$ sudo chmod +s /bin/dnw
先让VMware处于前台,再接USB
$ lsusb // 查看USB设备已连接
在uboot界面输入k
$ /bin/dnw arch/arm/boot/uImage // 即可完成USB烧写
三、使用韦东山做好的ubuntu服务器
3.1.下载安装VMware
3.2.解压ubuntu压缩包
3.3.设置VMware网络环境,一个物理网卡只能桥接一个虚拟网卡
“编辑/虚拟网络编辑器/”设置为桥接;
“虚拟机/设置/网络适配器/”设置为桥接,并选上“复制物理网络连接状态”;
3.4使用VMware打开ubuntu
3.5使用SecureCRT的ssh登陆Linux服务器
Hostname:192.168.10.3
Username:book
Key:123456
注意:修改work目录的用户权限
# sudo chown book:book /work -R
3.6安装TFTP工具CuteFTP Professional.exe,传输文件。
前提是配置好网络:
网关:192.168.10.1
Windows主机:192.168.10.2
Linux服务器:192.168.10.3
Linux目标板:192.168.10.4
--------------------------修改ip地址----------------------------
先用ifconfig -a 查看所有的网卡设备。
a.即时生效:
# ifconfig eth3 192.168.10.3 netmask 255.255.255.0
b.重启生效:
Linux服务器:
# sudo vi /etc/network/interfaces
auto lo
iface lo inet loopback
# The primary network interface
auto eth3
iface eth3 inet static
address 192.168.10.3
netmask 255.255.255.0
gateway 192.168.10.1
#network 192.168.10.0
#broadcast 192.168.10.255
重启网卡:
# sudo /etc/init.d/networking restart
Linux目标板:
# sudo vi /etc/init.d/rcS
#!/bin/sh
ifconfig eth0 192.168.10.4
或者
$ sudo ifconfig eth0 192.168.10.4
$ sudo ifconfig eth0 down // poen it
$ sudo ifconfig eth0 up // close it
---------------------修改default gateway---------------------
Linux服务器:
a.即时生效:
# route add default gw 192.168.10.1
b.重启生效:
# vim /etc/network/interfaces
-------------------------------修改DNS---------------------------
Linux服务器:
不用互联网则不用配置。
# sudo vi //etc/resolv.conf
search chotim.com
nameserver 192.168.1.1
重启网卡服务即可
# sudo /etc/init.d/networking restart
--------------------------修改host name-----------------------
Linux服务器:
使用下面的命令来查看当前主机的主机名称:sudo /bin/hostname
使用下面的命令来设置当前主机的主机名称:sudo /bin/hostname newname
系统启动时,它会从/bin/hostname来读取主机的名称。
--------------------------------------------------------------------
四、给uboot和目标板的Linux原始内核打补丁,制作根文件系统
4.1.uboot打补丁-- uboot.bin
a.$ tar xjf u-boot-1.1.6.tar.bz2 // 目录在:韦东山\000_开发板光盘\CD1\system
b.$ cd u-boot-1.1.6/ // Linux服务器中的位置:work/system/u-boot-1.1.6
c.$ patch -p1 < ../u-boot-1.1.6_jz2440.patch
d.$ cd ..
e.$ tar cjf u-boot-1.1.6_jz2440.tar.bz2 u-boot-1.1.6 // 放到Windows下的source insight下查看
f.$ cd u-boot-1.1.6/
g.$ make 100ask24x0_config
h.$ make // 即可得到/work/system/u-boot-1.1.6/u-boot.bin
4.2.Linux内核打补丁--uImage
a.$ tar xjf linux-2.6.22.6.tar.bz2
b.$ cd linux-2.6.22.6/
c.$ patch -p1 < ../linux-2.6.22.6_jz2440.patch
d.$ cd ..
e.$ tar cjf linux-2.6.22.6_jz2440.tar.bz2 linux-2.6.22.6 // 放到Windows下source insight下查看
f.$ cd linux-2.6.22.6/
g.$ cp config_ok .config
h.$ make uImage // 即可得到/work/system/linux-2.6.22.6/arch/arm/boot/uImage
4.3.制作根文件系统-- fs.yaffs2
a.$ sudo tar xjf fs_mini_mdev.tar.bz2
// 制作Yaffs2文件系统,要用到mkyaffs2image工具,光盘work/linux/tools下,复制到服务器 /bin
b.$ sudo cp mkyaffs2image /bin
c.$ sudo chmod +x /bin/mkyaffs2image
d.$ cd /work/nfs_root/tmp/
e.$ mkyaffs2image fs_mini_mdev fs.yaffs2 // 即可得到/work/nfs_root/tmp/fs.yaffs2
// 烧写到目标板
a.nfs 30000000 192.168.10.3:/work/nfs_root/tmp/fs.yaffs2
b.nand erase root
c.nand write.yaffs 30000000 0x00260000 $(filesize) // write.yaffs 防止位翻转
五、使用mount指令将NFS作为根文件系统
5.1.手工mount服务器上的文件夹/work/nfs_root到目标板上/mnt
1.Linux服务器开启nfs服务:
sudo apt-get install nfs-kernel-server portmap
vi /etc/exports 增加以下内容: /work/nfs_root *(rw,sync,no_root_squash)
sudo /etc/init.d/nfs-kernel-server restart // 重启nfs服务
2.目标板挂载nfs:# mount -t nfs -o nolock,vers=2 192.168.10.3:/work/nfs_root /mnt
或者 # mount -t nfs -o nolock,vers=2 192.168.10.3:/work/nfs_root/tmp/fs_mini_mdev /mnt
5.2.使用NFS作为根文件系统来启动
指令格式:
nfsroot=[
ip=
从SDRAM启动:
OpenJTAG> set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
OpenJTAG> save
OpenJTAG> reset
从网络文件系统启动: // 设置失败,可能是网关少写了个0,在uboot中能ping通Linux服务器,但是无法使用nfs.
OpenJTAG> set bootargs noinitrd root=/dev/nfs nfsroot=192.168.10.3:/work/nfs_root/tmp/fs_mini_mdev ip=192.168.10.4:192.168.10.3:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
OpenJTAG> save
OpenJTAG> reset
5.2.在服务器的/etc/init.d/rcS启动脚本中添加指令自动挂载nfs目录:
a.修改rcS并保存:
# vi /etc/init.d/rcS
#!/bin/sh
ifconfig eth0 192.168.10.4
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
mount -t nfs -o nolock,vers=2 192.168.10.3:/work/nfs_root/tmp/fs_mini_mdev /mnt
b.reboot后即可。
六、利用NFS挂载的文件夹,在服务器进行交叉编译,在目标板进行测试验证
6.1.在服务器Linux中编译驱动和测试程序
(1).编译驱动程序
$ cd first_drv/
$ vi Makefile // 确定开发版的内核位置KERN_DIR = /work/system/linux-2.6.22.6
(2).编译测试程序
$ arm-linux-gcc -o firstdrvtest firstdrvtest.c
6.2.nfs将服务器的指定文件夹mount到目标板的目录/mnt中,先在服务器的指定文件夹中编译,然后在目标板测试相关程序
# /work/nfs_root/tmp/fs_mini_mdev/drivers_and_test/first_drv/
# insmod first_drv.ko // 加载驱动
# ./firstdrvtest // 运行测试程序
Usage :
./firstdrvtest
# ./firstdrvtest on // led on
# ./firstdrvtest off // led off
七、修改编译器版本和驱动
7.1.安装arm-linux-gcc-x.x.x
1.解压
$ tar xjf arm-linux-gcc-3.4.5-glibc-2.3.6.tar.bz2
$ cd /arm-linux-gcc-3.4.5-glibc-2.3.6/bin
2.设置环境变量PATH,在环境变量中添加/work/tools/gcc-3.4.5-glibc-2.3.6/bin
$ vi /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/tools/gcc-3.4.5-glibc-2.3.6/bin"
3.立马生效编译工具
$ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/tools/gcc-3.4.5-glibc-2.3.6/bin
7.2.修改目标版(TQ2440或者MINI2440)的LCD和DM9000驱动
1.将驱动代码从\CD1\drivers_and_test\10th_lcd\4th\lcd.c拷贝到/work/system/linux-2.6.22.6/drivers/video
$ cd /work/system/linux-2.6.22.6/drivers/video
$ vi Makefile
obj-$(CONFIG_FB_S3C2410) += lcd.o #s3c2410fb.o
八、裸机驱动
文件:xxx.S xxx.c xxx.lds Makefile
目标:xxx.o xxx._elf xxx.bin xxx.dis
烧写:tftp或者nfs烧写到nandflash的0地址,程序从该处启动会自动搬移4KB到SRAM。
8.1.裸机程序的启动
NorFlash启动:直接从0地址获取指令。
NandFlash启动:硬件自动将NandFlash的低4K内容移到片内SRAM,从SRAM0地址开始取指令。
启动流程:
1.上电,自动复制NandFlash上的代码;
2.关闭看门狗;
3.初始化时钟;
4.初始化存储器等外设;
5.把代码复制到SDRAM
注意:
1.连接地址:是指运行时,程序所存储的位置。
0x00000000地址对应片内SDRAM;
0x30000000对应片外SDRAM。
2.MMU:存储物理地址的表格:页表(段、大页、小页、极小页),每个表项1M,总共4096个表项目。
3.NandFlash 的读写方式和RAM、寄存器的读取方式不同。
8.2.中断模式:
1.用户模式;
2.管理模式;
3.系统模式;
4.中断模式;
5.快速中断模式;
6.未定义指令终止模式;
7.数据访问终止模式
8.3.时钟系统;
FCLK:400MHz,cpu用。
HCLK:100MHz~133MHz,SDRAM等用。
PCLK:50MHz,Uart、定时器用。
九、u-boot原理分析
9.1.bootloader的作用:
1.启动内核:
flash和sdram读写;
初始化sdram;
启动内核;
2.升级新固件:
网络;
串口;
usb;
sd卡等。
9.2.uboot启动后要做的事:
0.设置为管理模式
1.关闭看门狗,屏蔽中断;
2.初始化时钟;
3.初始化SDRAM;
4.从flash中复制程序到SDRAM;
5.设置栈,SP指向内存;
6.代码重定位;
7.清理BSS段;
6.调用start.armboot,跳转到内核c代码,运行。
分析uboot的Makefile可知:
1.第一个文件:cpu/arm920t/start.S
2.链接地址:board/100ask24x0/u-boot.lds
uboot在内存中的地址TEXT_BASE=0x33f80000 在board/100ask24x0/config.mk
uboot中的关键函数:
1.s=getenv("bootcmd"); // 获取环境变量
run_command(s); // 执行环境变量对应的指令
3.readline(); // 读取menu界面串口输入的指令
run_command(s);
make clean仅仅是清除之前编译的可执行文件及配置文件。
而make distclean要清除所有生成的文件。
flash分区:
MTDPARTS_DEFAULT
name size offset
from 0 to 256k is bootloader: 0x00040000 0x00000000
next 128K is params: 0x00020000 0x00040000
next 2M is kernel: 0x00200000 0x00060000
next all is root: 0x0fda0000 0x00260000
OpenJTAG> mtd
#: name size offset mask_flags
0: bootloader 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: root 0x0fda0000 0x00260000 0
active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000
defaults:
mtdids : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root)
9.3.u-boot源代码分析:
1.uboot打补丁/编译/烧写/试验。
2.uboot功能/结构分析,结合Makefile。
3.uboot的命令操作,添加一个指令。
4.启动内核的过程:
(1)读出flash: nand.read();
(2)启动内核:do_boot
(a)读出uImage的头部(获取加载地址和写入地址,校验等)
(b)设置启动参数TAG:
0x30000100 -- 即bd->bi_boot_params
setup_start_tag(bd);
setup_memery_tag(bd);
setup_commandline_tag(bd, commandline);
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
setup_end_tag(bd);
(c)跳到内核入口地址:
theKernel(0, bd->bi_arch_numer, bd->bi_boot_params);
// bd->bi_arch_numer = MACH_TYPE_S3C2440;机器ID
// bd->bi_boot_params = 0x30000100;
十、内核启动流程
10.1.内核打补丁/编译/烧写/试验
patch -p<1> <补丁文件名>
10.2.内核功能/结构,结合Makefile/Kconfig分析
(1)配置:
method1: make menuconfig,自定义配置
method2: use default config,change little in directory of "arch/arm/configs".
执行:make xxx_defconfig 和 make menuconfig,在出现的菜单中进行配置。
method3: use vendor config
(2)分析Makefile和链接脚本
内核启动阶段一:处理uboot传入的参数; // in arch/arm/kernel/head.s
判断是否支持该架构和单板,获取机器ID;
建立一级页表;
设置arm920CPU核,禁用I、D caches等;
使能MMU;
复制数据段,清楚BSS段,设置栈指针,保存CPU ID到processor id;
跳转__machine_arch_type变量调用到start_kernel(),这是init/main.c的c函数;
内核启动阶段二:
输出Linux版本信息;
设置与体系结构相关的环境;
初始化控制台;
启动reset_init()
10.3.内核启动过程分析
内核启动的完整过程如下:
start_kernel()
-setup_arch() // 解析uboot传入的参数
-setup_command_line() // 解析uboot传入的参数
+do_early_param
-unknown_bootoption
+obsolete_checksetup
-reset_init()
+kernel_init()
prepare_namespace()
mount_root() // 挂载根文件系统
init_post() // 启动应用程序。
十一、构建最小根文件系统
11.1.根文件系统的启动
busybox->init_main
parse_inittab
file = fopen(INITTAB, "r"); // 打开配置文件"/etc/inittab"
new_init_action // 新建一个new_init_action结构体,并加入链表init_action_list
//
run_actions(SYSINIT);
waitfor // 等待process执行
run(a) // 创建子进程
waitpid // 等待结束
delete_init_action
run_actions(WAIT);
run_actions(ONCE);
while(1)
{
run_actions(RESPAWN);
run_actions(ASKFIRST);
wpid = wait(NULL); // 等待子进程退出
while (wpid > 0) {
a->pid = 0;
}
}
Format for inittab:
exp:new_init_action(ASKFIRST, "-/bin/sh", "/dev/tty2");
11.2.最小根文件系统所需内容和工具:
操作终端:/dev/console /dev/null
busybox产生的init程序:/sbin/init
配置文件:/etc/inittab
配置文件里指定的应用程序:
库文件:c库(printf/fopen/fwrite...)
11.3.构建自己的最小根文件系统
a.配置、编译busybox
win10资料目录:CD1\system\busybox-1.7.0
linux服务器目录:/work/system
$ tar xjf busybox-1.7.0.tar.bz2
$ cd busybox-1.7.0/ // INSTALL有操作说明 :make menuconfig/make/make install
$ vi Makefile // 加入下面一段
CROSS_COMPILE ?= arm-linux-
$ make menuconfig
Busybox Settings ---> Busybox Library Tuning --->
[*] Tab completion
$ mkdir -p /work/nfs_root/first_fs
$ make CONFIG_PREFIX=/work/nfs_root/first_fs install // 即可生成文件系统主体
b. 操作终端,添加文件节点 /dev/console /dev/null
$ mkdir dev
$ sudo mknod console c 5 1
$ sudo mknod null c 5 3
c.设置配置项/etc/inittab
$ cd /work/nfs_root/first_fs/
$ mkdir etc
$ vi etc/inittab // 加入下面
console::askfirst:-/bin/sh
d.安装C库
$ cd /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
$ mkdir /work/nfs_root/first_fs/lib
$ cp *.so* /work/nfs_root/first_fs/lib -d
到此,得到最小根文件系统:bin dev etc lib linuxrc sbin usr
d.指定应用程序
e.制作yaffs映像文件
$ cd /work/system
$ tar -xjf yaffs_source_util_larger_small_page_nand.tar.bz2
$ cd Development_util_ok
$ cd yaffs2/
$ cd util
$ make
$ sudo cp mkyaffs2image usr/local/bin
$ sudo chmod +x /usr/local/bin/mkyaffs2image
$ cd /work/nfs_root
$ mkyaffs2image first_fs first_fs.yaffs2 // 即可得到文件系统的映像文件
11.4 完善最小文件系统
inittab中指定初始化配置文件列表,包括终端“consol::askfirst:-/bin/sh”和启动脚本"::sysinit:/etc/init.d/rcS"等等。
/etc/init.d/rcS中mount -t proc none /proc 就可以使用“ps”命令
或者在rcS中写“mount -a”替换上面mount指令,然后在/etc/fstab目录下面加入要mount的设备信息。
11.5 从nfs启动根文件系统
服务器允许别人挂接:在服务器的/etc/exports中加入"/work/nfs_root/first_fs *(rw,sync,no_root_squash )"
然后重启nfs服务: sudo /etc/init.d/nfs_kernel_server restart
在目标板手动挂载目录到服务器上:sudo mount -t nfs 192.168.1.19:/work/nfs_root/first_fs /mnt
修改启动时的命令行参数,在/linux-2.6.22.6/Documentation/nfsroot.text中有介绍:
十二、字符设备驱动
12.1.字符设备的调用关系
应用层:c库实现open/read/write
VFS:sys_open/sys_read/sys_write
驱动层:xxx.open/xxx.read/xxx.write
12.2.第一个字符设备驱动,点亮led
1.在win10中的CD1\Workspace\linux_projects\linux-2.6.22.6\test_file中新建一个first_drv.c的文件,编辑代码。
2.放到linux服务器/work/nfs_root/first_fs/test_file目录编译,/work/nfs_root目标被mount到了目标板的/mnt目录。
3.目标板上加载模块:
# cd /mnt/first_fs/cd test_file/
# insmod /mnt/first_fs/test_file/first_drv.ko
4.# cat /proc/devices 查看设备
5.linux服务器/work/nfs_root/first_fs/test_file目录中从win10复制一个first_drv_test.c,并用arm-linux-gcc编译,
6.手动创建底层驱动设备节点,建立节点和底层驱动(设备类型和设备号)之间的联系,以给上层的应用调用:# mknod /dev/xxx c 252 0
7.在目标板# ./first_drv_test,即可看到打印的信息。
8.移除字符设备# rmmod first_drv
9.在代码中更新系统信息,让系统自动加载设备节点。
class_create(); class_device_create();
class_device_unregister(); class_destroy();
物理地址到虚拟地址的映射:gpfcon = (volatile unsigned long) ioremap(0x56000050, 16);
用户空间和内核空间传递数据:copy_from_user(&val, buf, count); // copy_to_user();
加入当前许可证信息:MODULE_LICENSE("GPL");
10.目标板/sys/class/firstdrv/xyz下即为设备节点。
11.# rmmod first_drv后,设备节点也会自动删除。
# cat /proc/devices 查看所有运行的设备
# ls -l /dev/xyz 查看节点设备类型
# cd /sys/class/firstdrv/xyz 查看设备节点的具体内容
12.3.查询方式获取按键值
创建file_operations结构体;
建立相应的操作函数;
创建初始化设备的函数,设置主设备号和名字,创建设备类让系统自动加载设备节点 ,gpio物理地址到虚拟地址的映射 ;
创建卸载函数;
加入到系统,module_init
说明许可证类型,MODULE_LICENSE("GPL");
top查看任务管理器。
12.4.中断方式获取按键值
手动执行按键底层驱动
# exec 5
# cat /proc/interrupts // 查看所有的中断信息
# ps // 查看进程ID等信息
# ls -l /proc/771/fd
# exec 5<& // 关闭/dev/buttons
# top 查看任务管理器
# ./fifth_drv_test & // 后台运行
12.5.设定时间内poll方式获取按键值,超时后退出
12.6.异步通知方式获取按键值
给进程发信号指令: kill -USR1 PID
kill -USR1 833 // kill -10 833
kill -9 833
过程:注册信号处理函数、驱动通过kill指令发送信号给app的PID
12.7.同步互斥阻塞方式获取按键值
同一个时刻只能有一个app能操作底层接口。
a.使用原子操作:
b.信号量
十三、自己写u-boot
13.1.自己写bootloader