Linux驱动初探(hello word)

1.开发环境搭建:

系统:ubuntu16.04

1.1:SSH环境搭建

这里我是用的是使用buildroot制作的最小文件系统,在选项里把SSH服务勾上,制作得到具备SSH功能的文件系统。这里大家可以自行搜索,或者开发板自带文件系统已经有SSH功能可以略过。

1.1.1搭建网络

设备:win10笔记本,网线,iMX6uL/ULL,网线转接口(轻薄本无法直接连接网线)
第一步:共享主机网络(我这里共享的是无线网卡)

Linux驱动初探(hello word)
这里选择以太网4是因为我开发板的网卡适配器就是以太网4,大家这里这里根据实际选择。
第二部:设置开发板适配器的静态IP
Linux驱动初探(hello word)
配置iPv4属性,IP地址可以随便填,只要不冲突(双击适配器看详细信息,如果出现两个IPv4说明IP冲突,需要更改),注意这里的IP前3段需要跟主机的相同,也就是所谓的同一网段,这里大家可以查看自己的无线网IPLinux驱动初探(hello word)
这里可以看出我的网段就是192.168.31.xx ,所以开发板适配器只要网段一致即可。
开发板使用 命令设置静态IP,例如我的地址设为192.168.31.20:

ifconfig eth0 192.168.31.20 up //eth0可根据实际更改

配置好后,win10与开发板互ping
Linux驱动初探(hello word)
Linux驱动初探(hello word)
注意不要ping win10的无线网卡。

1.1.2开发板配置SSH

终端输入 vi /etc/ssh/sshd_config (注意这里一定是SSHD_config,sshh后面有个d不要改错)
Linux驱动初探(hello word)
将登录密码选项改掉:
Linux驱动初探(hello word)

改为
permitRootLogin yes :

否则win10登录将被拒。
配置好后,键入命令

/etc/init.d/S50ssh restart //这里的ssh的文件名字可能不一样,但是都在init.d这个目录下,自己找到

Linux驱动初探(hello word)
这里用到的的软件是L:
Linux驱动初探(hello word)
连接192.168.31.20,连接成功就可以在win10十分轻松拖动文件到开发板运行。
把文件拖到这里就可以同步在开发板,是不是很方便。
Linux驱动初探(hello word)

1.2:linux内核编译,与设备树移植

为了方便大家编译设备树源码,本章节将介绍如何编译 imx6ull 设备树源码。
第一步设置交叉编译环境
1 Ubuntu14.04(参考用户手册或者入门视频教程,关于搭建编译环境的部分)
输入以下命令安装设备树编译器,如下图所示:

apt-get install device-tree-compiler

2 下载 uboot 和内核的源码和编译器,源码是 linux-imx-rel_imx_4.1.15_2.1.0_ga_20200611.tar.gz,编译器是gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
3 编译器的安装和设置环境变量
拷贝 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz 到 Ubuntu“/usr/local/arm/”目录下,然
后输入以下命令解压,如下图所示:
tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
Linux驱动初探(hello word)
拷贝内核源码到 Ubuntu 上并解压,进入解压源码“linux-imx-rel_imx_4.1.15_2.1.0_ga”目录下,输入以
下命令设置临时环境变量(注意,设置临时环境变量后,此终端窗口不能关闭,关闭了的话,设置的临时
环境变量就失效了),如下图所示

导入环境变量//
export ARCH=arm //指明系统架构
export CROSS_COMPILE=arm-linux-gnueabihf-
//这里大家灵活变通,交叉编译链能用就行

第二步 编译命令
设置好环境变量后,在“linux-imx-rel_imx_4.1.15_2.2.0_ga”目录下,输入 kernel 和设备树一起编译的
命令,如下图所示:

cp arch/arm/configs/imx_v7_defconfig .config
./create.sh

create.sh的脚本代码:

#!/bin/bash
  
export ARCH=arm

#export CROSS_COMPILE=/usr/local/arm/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi-

export CROSS_COMPILE=arm-linux-gnueabihf-

#make mrproper  # means CLEAN

make imx_v7_defconfig

#if   [ "$1" = "nand" ]
#then
#       cp arch/arm/boot/dts/imx6ul-14x14-evk_nand.dts arch/arm/boot/dts/imx6ul-14x14-evk.dts
#else
#       cp arch/arm/boot/dts/imx6ul-14x14-evk_emmc.dts arch/arm/boot/dts/imx6ul-14x14-evk.dts
#fi

make uImage LOADADDR=0x10008000 -j8

make modules

make topeet_emmc_4_3.dtb
make topeet_emmc_5_0.dtb
make topeet_emmc_7_0.dtb
make topeet_emmc_1024x600.dtb
make topeet_emmc_9_7.dtb
make topeet_emmc_10_1.dtb
make topeet_emmc_hdmi.dtb

make topeet_nand_4_3.dtb
make topeet_nand_5_0.dtb
make topeet_nand_7_0.dtb
make topeet_nand_1024x600.dtb
make topeet_nand_9_7.dtb
make topeet_nand_10_1.dtb
make topeet_nand_hdmi.dtb

cd ./arch/arm/boot/dts/
./create_dtb imx6ul_topeet_nand.dtb topeet_nand_4_3.dtb topeet_nand_7_0.dtb topeet_nand_10_1.dtb topeet_nand_1024x600.dtb topeet_nand_5_0.dtb topeet_nand_9_7.dtb topeet_nand_hdmi.dtb

arch/arm/configs/imx_v7_deconfig 是 默 认 的 编 译 配 置 文 件 , 如 果 修 改 内 核 配 置 文 件 , 需 要 覆 盖 掉
arch/arm/configs/imx_v7_deconfig,才能编译成功。我们需要在内核源码目录下面输入命令

cp arch/arm/configs/imx_v7_defconfig .config
export ARCH=arm
make menuconfig

我们在图形化界面配置驱动后,然后我们将系统默认配置文件
arch/arm/configs/arch/arm/configs/imx_v7_defconfig备份为arch/arm/configs/imx_v7_defconfigbak,输入以下命
令:

cp arch/arm/configs/imx_v7_defconfig arch/arm/configs/imx_v7_defconfigbak

我们再将根目录上配置好的.config 覆盖原来的配置文件,输入以下命令:

cp .config arch/arm/configs/imx_v7_defconfig

现在我们现在配置好的.config 变成了我们配置的默认文件,返回到我们的内核根目录下,运行我们的
脚本

./create.sh

编译完成如下图所示
Linux驱动初探(hello word)
第三步生成的镜像目录
内核镜像:”arch/arm/boot”目录下生成“zImage”文件
设备树镜像:“arch/arm/boot/dts”目录下生成设备树文件
最后将所有烧写到开发板。
Linux驱动初探(hello word)
成功进入系统后,再重新配置下SSH服务。

驱动编写与编译

Linux 下的应用程序是如何调用驱动程序的?
Linux 设备驱动会以内核模块的形式出现,因为 linux 内核的整体架构就非常庞大,包含的组件也非常多,
如果把所有的功能都编译到 linux 内核中会使得内核非常臃肿,为了解决这个问题,更方便地新增和删除功
能,linux 提供了这样的机制,这种机制被称为模块。为了大家对模块有一个感性的认识,我们先来看一个
最简单的驱动-helloworld。
驱动分为四个部分:
 头文件
 驱动模块的入口函数和出口函数
 声明信息
 功能实现
我们在 windows 上面新建一个 helloworld.c 文件,这里使用 sourceinsight 来编辑文件,大家也可以用其
他编译器来编写程序。
第一步 包含头文件

#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件

第二步 驱动模块的入口函数和出口函数

module_init();
module_exit();

第三步 声明模块拥有开源许可证

MODULE_LICENSE("GPL");

第四步 功能实现:内核模块加载的时候打印 hello world! ,内核模块卸载的时候打印 gooodbye!
注意:内核打印函数不能用 printf,因为内核没有办法使用 C 语言库。

static int hello_init(void){
printk("hello world! \n");
return 0;
}
static void hello_exit(void){
printk("gooodbye! \n");
}

完整的一个最简单的 Linux 内核模块,如下图所示。
Linux驱动初探(hello word)
大致流程如图,但先不必深究,我们现在只需要知道需要两个函数。`

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int hello_init (void)
{

    printk("hello word!\n");
    return 0;

}

static void hello_exit(void)
{

    printk("bye bye !\n");


}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");  //版本申明

写好代码后将代码放在内核源码下进行编译。

代码编译:

创建Makefile文件

obj-m += helloworld.o //先写生成的中间文件的名字是什么,-m 的意思是把我们的驱动编译成模块
KDIR:=/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?=$(shell pwd) //获取当前目录的变量
all:
make -C $(KDIR) M=$(PWD) modules //make 会进入内核源码的路径,然后把当前路径下的代码编译成
模块

注意:
1 KDIR:=/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/是设备树内核的源码路径,请大家根
据自己的实际情况进行修改。
2 设备树内核源码一定要先编译通过,才能编译驱动,否则会报错。
3 make -C 前面的空格是一个 tab 键
设置交叉编译器,输入以下命令:

export CROSS_COMPILE=arm-none-linux-gnueabi- 
export ARCH=arm

编译它会产生 helloworld.ko 目标文件,如下图所示
Linux驱动初探(hello word)
使用SSH传输到开发板即可。
使用命令:

insmod hello.ko//挂载
rmmod hello //卸载

Linux驱动初探(hello word)
第一个驱动成功,麻雀虽小五脏俱全,理解这个简单的驱动就一通百通了。

上一篇:01-鲲鹏生态体系介绍


下一篇:给大学生学习ARM和FPGA的建议