一.嵌入式linux系统部署
面试题:谈谈对嵌入式linux系统的认识
场景:给一块开发板,给一台电脑,给一周的时间
在开发板上跑linux系统
实施步骤:
1.上位机搭建嵌入式linux开发环境
安装纯linux系统(也可以使用虚拟机)
安装必要的软件
vim/vimplus/kermit/minicom/tftpd-hpa/nfs-kernel-server
ctags/cscope/wine+sourceinsight
安装添加部署交叉编译器
问:交叉编译器从何而来?
答:交叉编译器的获取途径有四种
1.从芯片厂家或者开发板(参考板)的厂家直接获取
2.从ARM相关交叉编译器的网站下载
3.利用自动化脚本工具crosstools-ng自己制作
4.自己下载gcc,linux内核源码,各种工具源码
自己编译制作
总结:切记:交叉编译器一定要从芯片厂家或者开发板的
厂家获取
5.有了交叉编译器,只需在上位机的linux系统中的
PATH环境变量中添加即可!
2.掌控下位机的硬件信息
粗看
首先看三大件
CPU:处理器型号,哪家公司的处理器
内存:容量和内存的起始地址
闪存: 容量和类型
闪存类型:
Norflash:容量和起始地址
Nandflash:容量和Nand控制器
emmc:容量和SDIO控制器
切记:容量,型号,起始地址都可以咨询
硬件工程师
然后看外围接口
哪些接口需要,哪些接口不需要,依赖用户将来
对产品的需求,举例子:手机和WIFI路由器对LCD
显示屏的需求
但是在产品研发阶段,UART接口和网口必须有
前者用于调试,后者用于软件下载,加快软件的
调试进度
细看:法宝:原理图和芯片手册
要具体硬件外设具体分析
例如:现在要研究mma8653三轴加速度传感器
那就要首先看这个芯片的原理图,本质目的明确
此芯片和CPU之间采用哪种硬件通信方式,当然
mma8653采用I2C总线方式,并且画出一个简要的
硬件连接示意图(CPU核+I2C控制器+mma8653),看图交代
访问的操作流程;
然后再看两个手册:
mma8653本身的芯片手册,而手册中重点关注两点:
1.mma8653相关的读写操作时序图
2.mma8653相关的片内寄存器的特性
片内寄存器地址
片内寄存器的bit含义
片内寄存器的访问不能以地址指针的形式访问
必须严格按照时序图操作
紧接着最后看处理器手册中I2C控制器相关的说明:
1.了解I2C控制器的基本操作原理
2.掌握I2C控制器内部的特殊功能寄存器
寄存器的地址
寄存器的bit位含义
以地址指针的形式访问
如果有必要,可以顺带大谈特谈I2C总线!
3.硬件信息掌控完毕,然后向下位机部署添加软件
3.1.首先要明确下位机部署的软件是裸板程序
还是基于操作系统的软件
如果是裸板程序,谈谈裸板程序的开发流程
此时此刻需要向下位机跑操作系统,linux系统
3.2.然后明确下位机运行linux系统所包含的必要
至少三部分软件和三个软件各自特点:
bootloader:boot(启动)+loader(加载)
仅仅是一个统称
uboot属于bootloader的一种
本质就是一个裸板程序
类似PC机的BIOS
上电CPU首先运行此程序
bootloader初始化硬件完毕以后,会从闪存上加载(拷贝)
linux内核到内存,并且从内存中启动linux内核
启动linux内核之前,还要给linux内核传递
启动参数,告诉linux内核将来根文件系统rootfs
在哪里,linux内核参数到某个地方去找根文件系统
rootfs
切记:一旦linux内核启动,bootloader的
生命周期到此结束
做的硬件初始化工作如下:
初始化CPU:关闭cache功能
初始化内存
关闭中断
关闭看门狗
初始化时钟:800MHz
初始化闪存
初始化UART
初始化网卡
LCD?
注意:有些硬件必须初始化(例如内存,闪存)
但有些硬件的初始化要根据用户需求来定
(例如用户需要在bootloader运行时,在
LCD显示屏上显示LOGO,此时bootloader必须
做LCD显示屏的初始化,如果没有此需求,
一律不初始化,只要做初始化了,势必影响
系统的启动时间)
linux内核:操作系统内核
由bootloader启动
永远运行内存中
掉电它的生命周期才结束
包含7大功能:
内存管理子系统
负责内存的映射,分配,销毁等
进程管理子系统
负责进程的创建,调度,销毁等
文件系统
NTFS/FAT32/EXT4/UBIFS/CRAMFS/YAFFS2等
系统调用
open/close/read/write/fork/exit等
设备驱动
各种硬件的驱动程序
网络协议栈
TCP/IP协议栈
平台相关代码
linux内核既可以运行在X86架构
也可以运行在ARM架构,也可以运行
POWERPC/MIPS/DSP/FPGA等
说明linux内核势必有相关的架构
支持的软件代码,此类代码检查
平台相关代码
根文件系统rootfs:
仅仅是一个代名词而已,什么都不是
它里面包含的内容就是linux系统中
的"/"下的所有东西,也就是:
cd /
ls //查看根目录下的内容
bin sbin lib etc dev sys proc ...
这些内容合在一起形成了根文件系统rootfs
3.3.嵌入式linux系统软件启动流程
上电->CPU首先运行emmc上的uboot->uboot启动以后
做大量的硬件初始化工作,然后从EMMC上读取linux内核
到内存,然后给linux内核传递启动参数,此参数就是
告诉linux内核将来内核找的根文件系统rootfs在哪里
->linux内核正式投入运行(uboot生命结束)->linux
内核也做大量的硬件和软件的初始化工作,之后根据
uboot传递的参数到某个地方去找根文件系统rootfs
->linux内核一旦找到根文件系统rootfs,linux内核
就会把控制权交给根文件系统rootfs->此时linux内核
会运行根文件系统中/sbin/init第一号进程,第一号
进程一旦运行,会创建一个子进程来执行根文件系统
/bin/sh,至此启动了一个shell程序,用户即可通过
shell程序来输入各种命令和linux系统交互
4.实战演练:向X6818部署(烧写)嵌入式linux系统
实施步骤:
1.先获取部署的软件
ftp://PORTING/system.rar
normal_system/
uboot.bin:uboot二进制镜像文件
uImage:linux内核的二进制镜像文件
rootfs_ext4.img:根文件系统rootfs的二进制镜像文件
2.明确:嵌入式开发模式
上位机下位机通过网线和tftp服务进行下载烧写
将三部分软件放到上位机的tftp的下载目录
cp uImage /tftpboot
cp rootfs_ext4.img /tftpboot
3.烧写三部软件到emmc上,首先进行分区规划
EMMC的分区规划:
0----512-------1M-------65M--------819M--------剩余
预留 uboot uImage rootfs 大片 //自定义名称
隐藏分区 mmcblk0p0 mmcblk0p1 mmcblk0p2 mmcblk0p3 //官方名称
4.正式的下载烧写
明确:emmc一个sector(块)为512字节
1.烧写uImage
重启下位机,进入uboot命令行模式,执行
tftp 48000000 uImage
注意:下载完毕以后,最后会提示uImage下载的文件大小
mmc write 48000000 0x800 0x3000
说明:
mmc write:向emmc写入数据
48000000:内存的起始地址,按字节为单位
0x800:要写的emmc的起始地址,按sector为单位
0x800=0x100000/512
0x3000:要向emmc写入的数据大小,按sector为单位
0x3000=uImage文件大小/512
2.烧写rootfs_ext4.img
重启下位机,进入uboot命令行模式,执行:
tftp 480000000 rootfs_ext4.img
注意观察rootfs_ext4.img的文件大小
mmc write 48000000 0x20800 0x32000
说明:
0x20800=65M/512
0x32000=rootfs_ext4.img文件大小/512
至此:uboot/uImage/rootfs_ext4.img烧写完毕
5.烧写完毕,一定要记得设置系统的启动参数
重启下位机,进入uboot的命令行模式,执行:
setenv bootcmd mmc read 0x48000000 0x800 0x3000 ; bootm 48000000
saveenv
说明:
bootcmd是uboot的一个非常重要的环境变量,此环境变量的
作用是用于uboot从某个地方加载linux内核到内存并且
启动内核,上电,uboot运行以后,如果不按任何键,倒计时321
以后,uboot会自动执行bootcmd的命令
所以bootcmd的参数为:mmc read 0x48000000 0x800 0x3000 ; bootm 48000000
mmc read 0x48000000 0x800 0x3000:
此命令是从emmc的0x800地址开始,读0x3000这么多的
数据到内存的0x48000000,注意:0x800和0x3000都是按sector为单位
bootm 0x48000000:从内存0x48000000启动linux内核
重启下位机,进入uboot命令行模式,还要执行:
setenv bootargs root=/dev/mmcblk0p2 init=/linuxrc
console=ttySAC0,115200 rootfstype=ext4 maxcpus=1 lcd=vs070cxn(或者lcd=wy070ml) tp=gslx680-linux
saveenv
说明:
bootargs同样属于uboot的非常非常重要的一个环境变量
此变量就是给linux内核传递的启动参数,将来内核根据
这个启动参数去到某个地方找根文件系统rootfs
root=/dev/mmcblk0p2:告诉linux内核根文件系统在emmc的第三分区
init=/linuxrc:启动运行的第一个脚本linuxrc
linuxrc会启动/sbin/init第一号进程
console=ttySAC0,115200:告诉linux内核,调试串口用第一个串口
注意一定要符合硬件的调试串口编号
rootfstype=ext4:文件系统格式为ext4
maxcpus=1:只启动CPU0
lcd=vs070cxn:指定LCD显示屏的型号
或者
lcd=wy070ml:指定新板子的LCD显示屏型号
tp=gslx680-linux:指定触摸屏的型号
6.测试下位机的linux系统是否正常启动
重启下位机,此时看到uboot的倒计时321时,别按键盘
时间一到,uboot会自动运行bootcmd指定的命令
支持linux系统正式运行,串口终端上可以看到linux内核
启动时候的打印信息,启动到最后,会提示输入:
用户名:root
密码:123456
进入下位机的linux的shell终端,执行命令:
cd /
ls
观察LCD显示屏是否显示一个QT界面,可以尝试玩玩QT界面
ps //查看当前进程
...
/usr/local/qttest //QT进程
kill QT进程的PID
再去操作触摸屏,QT程序还有反应吗?
手动再次启动QT程序:
/usr/local/qttest //启动以后,再去操作LCD显示屏
出现的问题:下位机linux系统无限重启问题或者QT界面没有
问题原因:linux内核启动到最后找根文件系统rootfs失败
失败原因的两点:
1.要不rootfs_ext4.img没有烧写
只需重新烧写
2.要不分区无效
只需重新分区
解决办法:
重启进入uboot命令行,执行:
fdisk 2 2 0x100000:0x4000000 0x4100000:0x2f200000
然后重新烧写rootfs_ext4.img
fdisk:uboot的分区工具命令
第一2:表示对EMMC进行重新分区,如果是SD0写0,如果是SD1写1
第二2:表示要分2两个分区,注意uboot分区不算
这两个分区分别表示uImage和rootfs所在的分区
0x100000:uImage所在分区的起始地址,为1M开始
0x4000000:uImage所在分区的大小,为64M(65M-1M)
0x4100000:rootfs所在分区的起始地址,65M
0x2f200000:rootfs所在分区的大小,754M(819M-65M)
问:如何将uboot的环境变量清空呢?
答:只需将环境变量设置为空即可,例如:
setenv bootargs
saveenv
print //查看bootargs是否还有信息
5.案例:
问:如何在现在的下位机的linux系统运行一个
自己的应用程序?
问:如何更新qttest应用程序呢,前提是在不进行
对emmc频繁的烧写?
答:只需利用大名鼎鼎的NFS网络服务即可
NFS网络服务就是在上位机启动,并且指定一个
共享目录,将来下位机可以从这个目录中获取
到要运行的文件
实施步骤:
1.上位机安装NFS网络服务
sudo apt-get install nfs-kernel-server
2.上位机配置NFS网络服务,添加共享目录
sudo vim /etc/exports 文件最后添加:
/opt/rootfs *(rw,sync,no_root_squash)
保存退出
说明:
/opt/rootfs:指定一个共享目录
*:任何一个IP客户端都可以来访问
rw:客户端可以对共享目录中的文件进行读和写
sync:客户端如果对文件进行修改了,要同步更新到服务器上
no_root_squash:普通用户也可以访问
注意:可以指定多个共享目录
3.上位机创建共享目录,添加一个应用程序
mkdir /opt/rootfs
cd /opt/rootfs/
vim pthread.c
arm-cortex_a9-linux-gnueabi-gcc -o pthread pthread.c -lpthread
4.上位机重启NFS网络服务
sudo service nfs-kernel-server restart
5.下位机的linux系统只需访问上位机的NFS网络服务
指定的共享目录/opt/rootfs即可:
首先确保下位机的linux系统已经启动完毕
只需在下位机执行一下命令即可:
ifconfig //发现网卡eth0压根就没有IP地址
ifconfig eth0 192.168.1.110
ping 192.168.1.8 //下位机ping上位机,测试
mount -t nfs -o nolock 192.168.1.8:/opt/rootfs /mnt
说明:
mount:挂接命令,让下位机去找上位机的NFS共享目录
-t nfs:采用NFS网络服务
-o nolock:对共享目录里的文件访问无需锁定
192.168.1.8:/opt/rootfs:指定下位机将来要找的上位机和上位机指定的共享目录
/mnt:属于下位机linux系统的一个目录,作为挂接目录
结果是将上位机的/opt/rootfs共享目录挂接到下位机的
/mnt目录,将来下位机只需访问/mnt目录本质就是在
访问上位机的/opt/rootfs目录
cd /mnt //本质就是进入上位机的/opt/rootfs
ls
helloworld.c hellworold pthread pthread.c
./helloworld //下位机运行
./pthread //下位机运行
注意:pthread程序在运行之前,下位机的linux系统
会自动利用网络从共享目录下载到下位机的
内存上运行(这个过程是偷偷摸摸的)
6.总结:
利用NFS网络服务将来可以大大提高软件调试测试的
效率,关键是将来更新软件测试软件无需频繁烧写
EMMC,大大提高了EMMC的使用寿命!
问:uboot如何擦除EMMC分区的数据呢?
答:用mmc erase命令
例如:在uboot命令行执行:
mmc erase 0x800 0x3000 //把uImage所在的分区擦除
6.案例:目前内核启动根据bootargs=/dev/mmcblk0p2到
EMMC的第三分区找根文件系统rootfs,现在能否
让内核启动的时候到上位机去找根文件系统呢?
答:是可以,同样需要利用NFS网络服务
实施步骤:
1.首先在上位机添加一个根文件系统rootfs
ftp://PORTING/system.rar/
nfs_rootfs/rootfs_qt.tar.bz2(制作好的rootfs)
cp rootfs_qt.tar.bz2 /opt/
rm /opt/rootfs -fr //删除上一个实验创建的目录
tar -xvf rootfs_qt.tar.bz2 //得到rootfs目录
cd /opt/rootfs //进入根文件系统rootfs,并且还是
NFS网络服务的共享目录
ls //查看根文件系统的内容
bin sbin lib etc ...
2.修改linux内核启动参数,告诉linux内核将来找根文件
系统rootfs不要再去emmc的第三分区找了,直接到
某个上位机去找根文件系统rootfs
重启下位机,进入uboot命令行模式,执行:
setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs
ip=192.168.1.110:192.168.1.8:192.168.1.1:255.255.255.0
init=/linuxrc console=ttySAC0,115200
maxcpus=1 lcd=vs070cxn tp=gslx680-linux
savenv
说明:
root=/dev/nfs:告诉linux内核,根文件系统rootfs在上位机上
nfsroot=192.168.1.8:/opt/rootfs:所要找的上位机的ip为192.168.1.8
并且这个上位机的/opt/rootfs就是要
找到的根文件系统
ip=下位机的ip:上位机的ip:网关:掩码
重启开发板,验证下位机是否能够启动
如果下位机的linux系统完毕,测试:
上位机编写一个程序:
cd /opt/rootfs/
vim helloworld.c
arm...gcc -o helloworld helloworld.c
下位机的linux系统测试:
cd /
ls
helloworld helloworld.c
./helloworlda
结论:将来软件的编辑编译在上位机
软件的运行只需在下位机运行即可
无需向EMMC烧写根文件系统rootfs和其中的
应用程序,提高了开发的效率和EMMC的使用寿命!