嵌入式LINUX驱动开发入门之hello驱动(基于IMX6ULL-MINI开发板)

1.驱动前提

编译驱动程序之前要编译内核,原因主要是:

(1)驱动程序要用到内核文件:

比如驱动程序中这样包含头文件: #include <asm/io.h>, 其中的asm是一个链接文件,指向asm-arm或asm-mips,这需要先配置、编译内核才会生成asm这个链接文件。

(2)编译驱动时用的内核,开发板上运行到内核,要一致:

开发板上运行到内核是出厂时烧录的,你编译驱动时用的内核是你自己编译的,这两个内核不一致时会导致- -些问题。所以我们编译驱动程序前,要把自己编译出来到内核放到板子上去,替代原来的内核。

(3)更换板子上的内核之后,板子上的其他驱动也要更换

板子使用新编译出来的内核时,板子上原来的其他驱动也要更换为新编译出来的。所以在编译我们自己的第1个驱动程序之前,要先编译内核、模块,并且放到板子上去。

2.编译内核

首先切换到内核所在目录

cd /home/xxl/100ask_imx6ull_mini-sdk/Linux-4.9.88

之后进行下面的操作

make mrproper
make 100ask_imx6ull_mini_defconfig
make zImage -j4

编译后生成的文件如下:

然后进行下面操作,也就是

在arch/arm/boot/dts 目 录 下 生 成 设 备 树 的 二 进 制 文 件 100ask_imx6ull_mini.dtb 。把这 2 文件复制到 /home/book/nfs_rootfs 录下备用
make dtbs
cp arch/arm/boot/zImage ~/nfs_rootfs
cp arch/arm/boot/dts/100ask_imx6ull_mini.dtb ~/nfs_rootfs

3.编译安装内核模块

3.1编译内核模块

首先进入内核源码目录,编译内核模块

cd /home/xxl/100ask_imx6ull_mini-sdk/Linux-4.9.88

 然后输入下面的指令,编译完成如下图

make modules

 

 3.2安装内核模块到Ubuntu某个目录下面备用

可以先把内核模块安装到 nfs 目录 (/home/book/nfs_rootfs)
然后:
cd /home/xxl/100ask_imx6ull_mini-sdk/Linux-4.9.88

 然后执行

make ARCH=arm INSTALL_MOD_PATH=/home/xxl/nfs_rootfs modules_install

 安装好如下图所示:

 4.安装内核和模块到开发板上面

假设:在Ubuntu的/home/book/nfs_ _rootfs 目录下,已经有了zImage、dtb文件,并且有1ib/modules子目录(里面含有各种模块)。

接下来要把这些文件复制到开发板上。假设Ubuntu IP 为192.168.5.11, .在开发板上执行以下命令,首先进行挂载,挂载之前记得先检查是否可以“ping 192.168.5.11”可以通顺然后是每次重启开发板之后都要进行这个挂载操作

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/xxl/nfs_rootfs /mnt

然后执行:

cp /mnt/zImage /boot
cp /mnt/100ask_imx6ull_mini.dtb /root
cp /mnt/lib/modules /lib -rfd
sync

最后重启开发板之后,就可以使用zImage、dtb模块了

5.hello驱动

5.1驱动入门知识

1.首先我们通常都是在Linux的终端上打开一个可执行文件,然后可执行文件就会执行程序。那么这个可执行文件做了什么呢?

2.可执行文件先是在应用层读取程序,其中会有很多库函数,库函数是属于内核之中。而内核又会往下调用驱动层程序。最终驱动层控制具体硬件。

    其实应用程序到库是比较容易理解的,比如我们刚学习C语言的时候,使用了printf,scanf等等这些函数。而这些函数就在库中。
    库可以和系统内核相连接,我们写了一个驱动程序,就需要告诉内核,这个过程叫做注册。我们注册了驱动之后,内核里面就会有这个驱动程序的信息,然后上层应用就可以调用。

3.所以我们只需要知道,需要编写两个程序,一个是驱动层的,一个是应用层的最后驱动层需要注册进入内核,应用层才能够使用。其他的先不要管。

4.我们在应用层调用read函数,对应驱动层的read函数。write函数和write函数对应。open函数和open函数对应。close函数和release函数对应(这个为什么不一样我们也不用管)。

5.我们对 Linux 应用程序对驱动程序的调用流程有一个简单了解之后,我得知道整个程序编写流程应该怎么做。至于流程为什么是这样的,我们记住即可。因为这些都是人规定的,如果之后学的深了再进行深究也不迟,现在我们主要是入门。

5.2整体框架和步骤

1.整体框架流程图

编写驱动主要为以下七个步骤:

  1.     确定主设备号,也可以让内核分配
  2.     定义自己的 file_operations 结构体
  3.     实现对应的 drv_open/drv_read/drv_write 等函数,填入 file operations 结构体
  4.     把 file_operations 结构体告诉内核: register_chrdev
  5.     谁来注册驱动程序啊? 得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
  6.     有入口函数就应该有出口函数: 卸载驱动程序时,出口函数调用unregister_chrdev
  7.     其他完善:提供设备信息,自动创建设备节点: class_create,device_create

5.3hello程序的实现

主要包括三个部分,驱动程序,测试程序,MakeFile文件

(1)驱动程序-hello_drv.c

#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>/*确定主设备号*/
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;#define MIN(a,b)(a<b?a:b)/*3.实现对应的open/read/write等函数,填入
file_operations结构体*/
//read
static ssize_t hello_drv_read(struct file *file,char _user *buf,
size_t size,loff_t *offset)
{int err;printk("s line %d\n",_FILE_,_FUNCTION_,_LINE_);err = copy_to_user(buf,kernel_buf,MIN(1024,size));return MIN(1024,size);
}
//write
static ssize_t hello_drv_write(struct file *file,const char _user *buf,
size_t size,loff_t *offset)
{int err;printk("%s %s line %d\n",_FILE_,_FUNCTION_,_LINE_);err = copy_from_user(kernel_buf,buf,MIN(1024,size));return MIN(1024,size);
}
//open
static int hello_drv_open(struct inode *node,struct file *file)
{printk("%s %s line %d\n",_FILE_,_FUNCTION_,_LINE_);return 0;
}
//close
static int hello_drv_open(struct inode *node,struct file *file)
{printk("%s %s line %d\n",_FILE_,_FUNCTION_,_LINE_);return 0;
}/*2.定义自己的file_operations结构体*/
static struct file_operations hello_drv={.owner = THIS_MODULE,.open = hello_drv_open,.read = hello_drv_read,.write = hello_drv_write,.release = hello_drv_close,
};/*4.将file_operations结构体告诉内核:注册驱动程序*/
/*5.需要入口函数,也就是注册驱动程序,安装驱动程序时
就会去调用这个入口函数*/
static int __init hello_init(void)
{int err;printk("%s %s line %d\n",_FILE_,_FUNCTION_,_LINE_);major = register_hrdev(0,"hello",&hello_drv); /*/dev/hello*/hello_class = class_create(THIS_MODULE,"hello_class");err = PTR_ERR(hello_class);if(IS_ERR(hello_class)){printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);unregister_chrdev(major,"hello");return -1;}device_create(hello_class,NULL,MKDEV(major,0),NULL,"hello");/*/dev/hel*/return 0;
}/*6.出口函数,卸载驱动程序时就会调用这个出口函数*/
static void __exit hello_exit(void)
{printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);device_destroy(hello_class,MKDEV(major,0));class_destroy(hello_class);unregister_chrdev(major,"hello");
}/*7.其他完善:提供设备信息,自动创建设备节点*/module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");

(2)测试程序-hello_drv_test.c

//编写测试程序,实现读写功能#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>int main(int argc,char **argv)
{int fd;char buf[1024];int len;/*1.判断参数*/if(argc < 2){printf("Usage: %s -w <string>\n",argv[0]);printf("   %s -r\n",argv[0]);return -1;}/*2.打开文件*/fd = open("/dev/hello",O_RDWR);if(fd == -1){printf("can not open file /dev/hello\n");return -1;}/*3.写文件或者读文件*/if((strcmp(argv[1],"-w"))&&(argc == 3)){len = strlen(argv[2]) +1;len = len < 1024 ? len:1024;write(fd,argv[2],len);}else {len = read(fd,buf,1024);buf[1023] = "\0";printf("App read : %s\n",buf);}close(fd);return 0;
}

(3)Makefile文件

  1 2 # 1. 使用不同的开发板内核时, 一定要修改KERN_DIR3 # 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:4 # 2.1 ARCH,          比如: export ARCH=arm645 # 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-6 # 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc    /ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 7 # 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,8 #       请参考各开发板的高级用户使用手册9 10 KERN_DIR = /home/xxl/100ask_imx6ull_mini-sdk/Linux-4.9.8811 12 all:13     make -C $(KERN_DIR) M=`pwd` modules 14     $(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c 15 16 clean:17     make -C $(KERN_DIR) M=`pwd` modules clean18     rm -rf modules.order19     rm -f hello_drv_test20 21 obj-m   += hello_drv.o

 在make之前要进行以下的检查

也就是对环境变量进行检查(这里以IMX6ULL-MINI为例)

vim ~/.bashrc

在最后三行添加上

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/xxl/100ask_imx6ull_mini-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

然后输入下面指令确保生效

source ~/.bashrc

然后就可以输入make进行编译

生成如下:

将这两个文件移动到UBUNTU的挂载目录下

cp *.o hello_drv_test /home/xxl/nfs_rootfs

这时候在开发板进行如下操作

挂载操作

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/xxl/nfs_rootfs /mnt

 然后进行下面的操作即可:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69643.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chrome-mojo C++ Bindings API

概述 Mojo C 绑定 API 利用C 系统 API提供一组更自然的原语&#xff0c;用于通过 Mojo 消息管道进行通信。结合从Mojom IDL 和绑定生成器生成的代码&#xff0c;用户可以轻松地跨任意进程内和进程间边界连接接口客户端和实现。 本文档通过示例代码片段提供了绑定 API 用法的详…

centos 8和centos 9 stream x64的区别

以下是 CentOS 8 与 CentOS Stream 9 的主要区别&#xff0c;从技术架构、更新策略到适用场景等维度进行对比&#xff1a; AI产品独立开发实战营 联系我了解 1. 定位与更新策略 特性CentOS 8CentOS Stream 9定位原为 RHEL 8 的免费稳定复刻版RHEL 9 的上游开发分支&#xff…

物联网软件开发与应用方向应该怎样学习,学习哪些内容,就业方向是怎样?(文末领取整套学习视频,课件)物联网硬件开发与嵌入式系统

随着物联网技术的飞速发展&#xff0c;物联网软件开发与应用方向成为了众多开发者关注的焦点。那么&#xff0c;如何在这个领域中脱颖而出呢&#xff1f;本文将为你提供一份详细的学习指南&#xff0c;帮助你从零开始&#xff0c;逐步掌握物联网软件开发与应用的核心技能。 一…

DeepSeek之于心理学的一点思考

模型和硬件参数对应关系参考 模型参数规模 典型用途 CPU建议 GPU建议 最小内存建议 磁盘空间建议 适用场景 1.5b(15亿) 小型推理、轻量级任务 4核以上(Intel i5/AMD Ryzen5) 可选&#xff0c;入门级GPU(如NVIDIA GTX1650 4GB显存) 8GB 10GB以上SSD 小型NLP任务、文…

DeepSeek 助力 Vue 开发:打造丝滑的步骤条

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

ubuntu安装VMware报错/dev/vmmon加载失败

ubuntu安装VMware报错/dev/vmmon加载失败&#xff0c;解决步骤如下&#xff1a; step1&#xff1a;为vmmon和vmnet组件生成密钥对 openssl req -new -x509 -newkey rsa:2048 -keyout VMW.priv -outform DER -out VMW.der -nodes -days 36500 -subj "/CNVMware/"ste…

5 个释放 安卓潜力的 Shizuku 应用

Shizuku 软件推荐&#xff1a;释放安卓潜力的五款应用 Shizuku (日语&#xff1a;雫&#xff0c;意为“水滴”) 正如其名&#xff0c;是一款轻巧但功能强大的安卓工具。它无需 Root 权限&#xff0c;通过 ADB (Android Debug Bridge) 授权&#xff0c;即可让应用调用系统 API&…

【java API】leetcode常用刷题API及ACM模式

文章目录 ACM输入 Scanner**一、字符串高频API****二、集合高频API****三、栈&#xff08;Stack&#xff09;高频API****1. 推荐用Deque替代Stack类**&#xff08;更高效且线程不安全&#xff0c;适合算法场景&#xff09;**2. 核心操作****3. 经典应用场景****4. 避坑指南** *…

网络防御高级

接口配置&#xff1a; SW2: [sw2]vlan 10 [sw2]vlan 20 [sw2]interface GigabitEthernet 0/0/1 [sw2-GigabitEthernet0/0/1]port link-type trunk [SW2-GigabitEthernet0/0/1]port trunk allow-pass vlan 10 20 [sw2]interface GigabitEthernet 0/0/2 [sw2-GigabitEthernet0/0/…

Kokoro 开源文本转语音引擎上线!多语言支持,无需联网,浏览器内极速运行

Kokoro 是一款轻量级的开源文本转语音(TTS)引擎,凭借其高效能和轻量化设计,迅速在技术社区中引起关注。本文将详细介绍 Kokoro 的主要特点,并提供在浏览器和 Python 环境中的代码示例,帮助您快速上手。 1. Kokoro:可在浏览器中运行的 TTS 引擎 1.1 简介 Kokoro 是一个…

html为<td>添加标注文本

样式说明&#xff1a; /*为td添加相对定位点*/ .td_text {position: relative; }/*为p添加绝对坐标(相对于父元素中的定位点)*/ .td_text p {position: absolute;top: 80%;font-size: 8px; }参考资料&#xff1a;

ASP.NET Core SignalR案例:导入英汉词典

Ecdict 下载词典文件stardict.7z&#xff0c;解压&#xff0c;stardict.csv是一个CSV格式的文本文件&#xff0c;文件的第一行是表头&#xff0c;除第一行外&#xff0c;其他每行文本是一个单词的相关信息&#xff0c;用逗号分隔的就是各个列的值。英汉词典ECDICT中导入单词到…

【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面

【清晰教程】本地部署DeepSeek-r1模型-CSDN博客 目录 安装Docker 配置&检查 Open WebUI 部署Open WebUI 安装Docker 完成本地DeepSeek-r1的部署后【清晰教程】本地部署DeepSeek-r1模型-CSDN博客&#xff0c;通过Docker为本地DeepSeek-r1部署WebUI界面。 访问Docker官…

2025web寒假作业二

一、整体功能概述 该代码构建了一个简单的后台管理系统界面&#xff0c;主要包含左侧导航栏和右侧内容区域。左侧导航栏有 logo、管理员头像、导航菜单和安全退出按钮&#xff1b;右侧内容区域包括页头、用户信息管理内容&#xff08;含搜索框和用户数据表格&#xff09;以及页…

如何在 Qt 中添加和使用系统托盘图标

在 Qt 中实现系统托盘图标是一个常见的需求&#xff0c;尤其是在桌面应用程序中。系统托盘图标可以让应用程序在后台运行时仍然具有可见性&#xff0c;同时避免占用过多的桌面空间。本文将详细介绍如何在 Qt 项目中添加托盘图标&#xff0c;并通过资源系统&#xff08;.qrc 文件…

探索B-树系列

&#x1f308;前言&#x1f308; 本文将讲解B树系列&#xff0c;包含 B-树&#xff0c;B树&#xff0c;B*树&#xff0c;其中主要讲解B树底层原理&#xff0c;为什么用B树作为外查询的数据结构&#xff0c;以及B-树插入操作并用代码实现&#xff1b;介绍B树、B*树。 &#x1f4…

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中&#xff0c;我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart&#xff0c;实现动态生成 W…

QT修仙之路2-2 对话框 尚欠火候

警告对话框 相关代码 错误对话框 相关代码 消息对话框 相关代码 询问对话框 相关代码 相关代码 警告对话框 QMessageBox::warning(this,"错误","账号密码不能为空",QMessageBox::Ok);错误对话框 QMessageBox msgBox(QMessageBox::Critical,"错误…

conda 修复 libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 简便方法

ImportError: /data/home/hum/anaconda3/envs/ipc/bin/../lib/libstdc.so.6: version GLIBCXX_3.4.30 not found (required by /home/hum/anaconda3/envs/ipc/lib/python3.11/site-packages/paddle/base/libpaddle.so) 1. 检查版本 strings /data/home/hum/anaconda3/envs/ipc/…

RTD2775QT/RTD2795QT瑞昱显示器芯片方案

RTD2775QT与RTD2795QT&#xff1a;高性能4K显示驱动芯片 RTD2775QT与RTD2795QT是瑞昱半导体公司推出的两款高性能显示驱动芯片&#xff0c;专为满足现代显示设备对高清、高分辨率的需求而设计。这两款芯片不仅支持4K分辨率&#xff0c;还具备丰富的功能和卓越的性能&#xff0…