驱动(二)系统移植
一、Makefile
工程管理:Makefile
语法:要生成的文件:依赖的所有文件
生成的方式
步骤:make->Makefile、makefile->寻找文件中要生成的目标a.out->寻找依赖的所有文件->存在,则利用命令生成目标文件/ ->不存在,Makefile向下寻找依赖文件的生成方式
- := 覆盖原来的值
- ?=如果原来没值赋值新值,原来有值,不赋新值
- += 原来值的基础上新加一个值
通过make传参
二、Uboot编译
1.概念
uboot是bootloader的一种,主要作用是在内核加载前对硬件设备初始化,并为内核的运行提供引导功能。
2.编译
1.uboot (跨平台)
(1).初始化硬件设备
(2).为加载内核做准备
2.使用宏实现条件编译,满足条件的加入编译中,不满足的从编译中去除最顶层目录.config配置文件(宏的配置,如果宏配置到该文件中则代码加入uboot编译)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_alientek_emmc_defconfigmake V=0 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
ARCH:指定目标硬件架构CROSS_COMPILE:设置交叉编译工具链V=0:控制编译输出的详细程度,0表示启动静默/精简输出模式,1表示完整的编译命令,便于调试。
最终生成:uboot.bin -> uboot.imx
三、uboot启动流程
第一阶段
1.开发板上电,加载0x0地址,IMX6ULL96k出厂固件代码(ROM),会根据启动方式将对应的存储设备前面的一段uboo代码加载到ARM执行
2.出厂固件代码及uboot 通过IMX6uLL内部的RAM执行
3.对外设初始化(初始化DDR和EMMC) //自身资源空间有限
4.将IMX6ULL96kROM固件代码搬移到DDR中运行
第二阶段
5.外设逐一初始化
6.进入与用户交互的系统中,等待用户按下按键
7.如果用户不操作uboot,倒计时结束后,会执行bootcmd
8.引导启动内核(需要串口、需要网口、文件系统的方式及路径、需要设备树...)
内核启动阶段:加载驱动和五大功能实现的展开
9.内核启动执行init进程(0号内核进程)挂载文件系统
用户态
10.启动用户进程(fork+exec)启动所有的进程
11.启动终端 (可以与用户实现交互)
四、内核与设备树文件
1.概念
2.编译
(2).进入源代码中
(3).编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean //清除之前的编译的文件
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_erhmc_defconfig//将imx_alientek_emmc_defconfig中的设置作为.config设置,.config决定了哪些代码加入编译。哪些代码从编译中去除
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfigmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf--j16//用户可以通过界面对.config完成修改
distclean:表示清除之前的编译内容imx_alientek_emmc_defconfig:将该配置作为内核的.config配置menuconfig:通过图形界面配置.configall -j16:通过16核编译代码ARCH=arm:编译arm32位平台CROSS_COMPILE=arm-linux-gnueabihf-:设置编译工具链
- 设备树文件:arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
- 内核镜像文件:arch/arm/boot/zlmage
cp arch/arm/boot/zImage ~/tftpboot/ -rfcp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb ~/tftpboot/ -rf
3.make menuconfig
- *驱动项 编译生成的zImage中包含该驱动项 驱动 (静态加载)
- M驱动项 编译生成的zImage中不包含该驱动项,该驱动将会成为一个独立的模块 .ko 驱动 (动态加载)
步骤:
1.打开图形界面->1.图形界面中的配置项(根据Kconfig中的语法解析得到)
2.勾选/去除/模块选择编译目录->1.根据用户勾选将对应的宏写入.config 宏名=y/m
make -j16 -> 顶层的makefile会调用每一层的makefile,每层makefile都会根据.config中的宏的配置决定哪些.o文件加入编译,哪些.o文件不加入编译 -> vmlinux(可以调试的内核)-> Image(对vmlinux压缩后的镜像文件)/ ->zImage(添加解压算发的Image)
五、文件系统
1.概念
2.操作步骤
内核启动后->挂载文件系统->执行linuxrc->/etc/inittab脚本(决定系统启动执行哪些脚本、重启执行哪些脚本、ctrlaltdel执行哪些脚本)->系统启动执行/etc/init.d/rcS脚本->设置环境变量、参数->挂载所有文件系统(mount-a)->执行/etc/fstab文件->挂载所有文件系统->/etc/profile脚本->启动bash shell->与用户交互->./a.out->fork+exec(./a.out)
注:如何实现开机自启动? 将./a.out& 放在/etc/profile脚本或etc/init.d/rcS脚本
六、驱动
驱动是针对内核的开发
驱动要解决的问题:
- 1.如何在一个已经运行起来的代码中执行我们的驱动代码
- 2.驱动代码的编写的框架是什么?
- 3.驱动代码最终还是完成对寄存器的操作,如何实现虚拟地址和物理地址的映射
(一)内核编写驱动
1.静态加载
驱动加加入zImage中,zImage直接支持该驱动
(1).编写驱动led.c
(2).编译代码
1.修改K.config,增加一条该驱动的说明,需要增加一个关于该条驱动的宏名,得到 menu config
2.用户通过make menuconfig选择该条驱动<*>,会引|起.config中的配置的变化宏名=y
3.修改Makefile obj-$(宏名)+=led.o,驱动编译时就会加入led.c代码的编译
4.代码编译->链接->压缩->ZImage(包含了led驱动)
(3).通过ttp下载最新的zImage来执行内核,此时内核中增加了led驱动
2.动态加载
编译成内核模块(ko),insmod加载到一个正在执行的Linux系统中
(1).编写驱动led.c
(2).编译代码
1.在makefile中 obj-m+=led.o
2.在内核中通过make modules来完成对内核模块的编译
3.编译成功后生成led.ko内核模块文件
(3).将内核模块拷贝到~/nfs/rootfs
(4).在开发板中(Linux系统已经启动),通过insmod命令加载内核模块,通过rmmod命令卸载内核模块
3.工程搭建和代码编译
4.加载驱动
insmod demo_drv.ko