【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十二章 LED驱动实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263(加群获取驱动文档+例程)


第四十二章 LED驱动实验

本章导读

本章节将以实践课的方式讲解LED驱动,将从硬件原理图的查看,编写驱动程序,运行测试三个步骤进行讲解。

本章内容对应视频讲解链接(在线观看):

第一个相对完整的驱动实践编写  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=13

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\04-LED驱动实验”路径下。

42.1 实验需求分析

实验需求

1.使用杂项设备完成一个LED驱动。

2.完成一个上层测试应用。

应用要求:

在上层应用中传入参数1为打开LED,传入参数0为关闭LED。

需求分析:

  • 首先完成一个LED的驱动使用的是杂项设备,那么按照杂项设备的注册流程来填充结构体,参考第三十九章节 Linux MISC的内容。
  • 杂项设备注册完毕,我们还要和硬件关联起来,要用到设备节点,因为设备节点是应用和驱动的桥梁,设备节点是通过杂项设备注册生成的。
  • 我们要操作LED,就要完成相关的函数了。比如说,我们要完成read函数,open函数,做驱动就是做的这几个函数。参考第四十章节 Linux用户层和内核层的内容。
  • 我们要完成上层应用要传入参数,就涉及到了应用层向内核层传递数据,我们传递数据不能直接传,我们要用到两个函数,用到copy_to_user()和copy_from_user()。

那么实验需求给大家分析完了。

42.2 硬件分析

42.2.1 硬件原理图分析

iTOP-i.MX8MM开发板是底板加核心板的结构,底板原理图在“8MM开发板\iTOP-i.MX8MM开发板\01-i.MX8MM开发板光盘资料\20210830\01-硬件资料\02-原理图\底板原理图”下载。iTOP-i.MX8MM开发板底板上默认的LED灯被使用了,所以我们可以找个空闲的gpio引脚连接led灯来进行实验。打开底板原理图找到U60 插槽,如下图所示:

我们将led小灯正极插在GPIO_IO13上,负极插在2号引脚接地,硬件连接如下图所示: 

 

42.2.2 芯片手册分析

打开iTOP-i.MX8MM光盘资料“8MM开发板\iTOP-i.MX8MM开发板\01-i.MX8MM开发板光盘资料\20210830\01-硬件资料\03-参考手册”,因为我们的内核里面已经的驱动了,像复用关系的寄存器,电气属性的寄存器,就可以不用设置了,直接设置数据寄存器和方向寄存器就可以了。我们在此文档中查找引脚GPIO_IO13的数据寄存器和方向寄存器,如下图所示,数据寄存器的基地址是0x30200000,方向寄存器的地址是0x30200004,这些地址在我们后面编写驱动的时候需要使用。

 

在驱动开发调试过程,我们可以通过 io 命令访问寄存器,如需判断 iomux, gpio direction/电平,提高/减小驱动强度,使能施密特触发,这是一个高效又准确的调试手段。

42.3 编写LED驱动

我们以iTOP-iMX8MM开发板为例编写驱动文件,实现控制开发板上面LED的效果。

我们在ubuntu的home/topeet/imx8mm/04目录下新建led.c文件,可以在上次实验的驱动代码基础上进行修改,以下代码为完整的驱动代码。

我们已经学会了杂项设备驱动编写的基本流程,其实需求已经完成了一半了,我们已经注册了杂项设备,并生成了设备节点。接下来我们要完成控制LED的逻辑操作,那么控制LED就涉及到了对寄存器的操作,但是对寄存器的操作我们是不能直接访问的,因为linux不能直接访问我们的物理地址,需要把物理地址先映射成虚拟地址,我们完成这一步转换需要用到ioremap函数。

完整的驱动文件如下所示: 

/** @Descripttion: 基于杂项设备的LED驱动*/
#include <linux/init.h>            //初始化头文件
#include <linux/module.h>          //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h>      //包含了miscdevice结构的定义及相关的操作函数。
#include <linux/fs.h>              //文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/uaccess.h>         //包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/io.h>              //包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/kernel.h>          //驱动要写入内核,与内核相关的头文件
#define GPIO1_IO13 0x30200000      //led数据寄存器物理地址
#define GPIO1_IO13_GDIR 0x30200004 //led数据寄存器方向寄存器物理地址
unsigned int *vir_gpio1_io13;      //存放映射完的虚拟地址的首地址
unsigned int *vir_gpio1_io13_gdir; //存放映射完的虚拟地址的首地址/*** @name: misc_read* @test: 从设备中读取数据,当用户层调用函数read时,对应的,内核驱动就会调用这个函数。* @msg: * @param {structfile} *file file结构体* @param {char__user} *ubuf 这是对应用户层的read函数的第二个参数void *buf* @param {size_t} size 对应应用层的read函数的第三个参数* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使偏移量往后移。* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取的字节数。
如果返回负数,内核就会认为这是错误,应用程序返回-1*/
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{printk("misc_read\n ");return 0;
}
/*** @name: misc_write* @test: 往设备写入数据,当用户层调用函数write时,对应的,内核驱动就会调用这个函数。* @msg: * @param {structfile} * filefile结构体* @param {constchar__user} *ubuf 这是对应用户层的write函数的第二个参数const void *buf* @param {size_t} size 对应用户层的write函数的第三个参数count。* @param {loff_t} *loff_t 这是用于存放文件的偏移量的,回想一下系统编程时,读写文件的操作都会使偏移量往后移。* @return {*} 当返回正数时,内核会把值传给应用程序的返回值。一般的,调用成功会返回成功读取的字节数。如果返回负数,内核就会认为这是错误,应用程序返回-1。*/
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{/*应用程序传入数据到内核空间,然后控制LED的逻辑,在此添加*/// kbuf保存的是从应用层读取到的数据char kbuf[64] = {0};// copy_from_user 从应用层传递数据给内核层if (copy_from_user(kbuf, ubuf, size) != 0){// copy_from_user 传递失败打印printk("copy_from_user error \n ");return -1;}//打印传递进内核的数据printk("kbuf is %d\n ", kbuf[0]);*vir_gpio1_io13_gdir |= (1 << 13);if (kbuf[0] == 1) //传入数据为1 ,LED亮{*vir_gpio1_io13 |= (1 << 13);}else if (kbuf[0] == 0){ //传入数据为0,LED灭*vir_gpio1_io13 &= ~(1 << 13);}return 0;
}
/*** @name: misc_release* @test: 当设备文件被关闭时内核会调用这个操作,当然这也可以不实现,函数默认为NULL。关闭设备永远成功。* @msg: * @param {structinode} *inode 设备节点* @param {structfile} *file filefile结构体* @return {0}*/
int misc_release(struct inode *inode, struct file *file)
{printk("hello misc_relaease bye bye \n ");return 0;
}
/*** @name: misc_open* @test: 在操作设备前必须先调用open函数打开文件,可以干一些需要的初始化操作。* @msg: * @param {structinode} *inode 设备节点* @param {structfile} *file filefile结构体* @return {0}*/
int misc_open(struct inode *inode, struct file *file)
{printk("hello misc_open\n ");return 0;
}
//文件操作集
struct file_operations misc_fops = {.owner = THIS_MODULE,.open = misc_open,.release = misc_release,.read = misc_read,.write = misc_write,
};
//miscdevice结构体
struct miscdevice misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name = "hello_misc",.fops = &misc_fops,
};
static int misc_init(void)
{int ret;//注册杂项设备ret = misc_register(&misc_dev);if (ret < 0){printk("misc registe is error \n");}printk("misc registe is succeed \n");//将物理地址转化为虚拟地址vir_gpio1_io13 = ioremap(GPIO1_IO13, 4);if (vir_gpio1_io13 == NULL){printk("vir_gpio1_io13 ioremap is error \n");return EBUSY;}printk("vir_gpio1_io13 ioremap is ok \n");vir_gpio1_io13_gdir = ioremap(GPIO1_IO13_GDIR, 4);if (vir_gpio1_io13 == NULL){printk("GPIO1_IO13_GDIR ioremap is error \n");return EBUSY;}printk("GPIO1_IO13_GDIR ioremap is ok \n");return 0;
}
static void misc_exit(void)
{//卸载杂项设备misc_deregister(&misc_dev);iounmap(vir_gpio1_io13);iounmap(vir_gpio1_io13_gdir);printk(" misc gooodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

42.4 编写应用程序

编写应用程序如下所示:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd;char buf[64] = {0};//定义buf缓存//打开设备节点fd = open("/dev/hello_misc",O_RDWR);if(fd < 0){//打开设备节点失败perror("open error \n"); return fd;}// atoi()将字符串转为整型,这里将第一个参数转化为整型后,存放在buf[0]中buf[0] = atoi(argv[1]);//把缓冲区数据写入文件中write(fd,buf,sizeof(buf));  printf("buf is %d\n",buf[0]); close(fd);return 0;
}

将app.c文件拷贝到Ubuntu的/home/topeet/imx8mm/04目录下,编译app.c,如下图所示:

42.5 编译驱动及运行测试

我们将42.4章节编写的驱动文件led.c编译成模块,将上次编译file_operation的Makefile文件和build.sh文件拷贝到led.c同级目录下,修改Makefile为:

obj-m += led.o

KDIR:=/home/topeet/linux/linux-imx

PWD?=$(shell pwd)

all:

make -C $(KDIR) M=$(PWD) modules ARCH=arm64

clean:

make -C $(KDIR) M=$(PWD) clean

文件如下图所示:

 

输入命令编译驱动如下图所示: 

驱动编译完,我们进入到共享目录,加载驱动模块如图所示:

insmod beep.ko

 

然后我们输入命令“./app 1”打开LED,LED亮;输入命令“./app 0”关闭LED,LED灭。 

 

 

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

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

相关文章

【python014】Python爬取并解析潮汐天气简报-潮历数据

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 【python014】Python爬取并解析潮汐天气简报-潮历数据&#xff0c;源代码下载【python014】Python爬取并解析潮汐天气简报-潮历数据&#xff0c;源代码下载【python014】Python爬取并解…

EtherNet/IP转Profinet协议转化网关(功能与配置)

如何将EtherNet/IP和Profinet网络连接通讯起来呢?近来几天有几个朋友问到这个问题&#xff0c;那么作者在这里统一说明一下。其实有一个不错的设备产品可以很轻易地解决这个问题&#xff0c;名为捷米特JM-PN-EIP。接下来作者就从该设备的功能及配置详细说明一下。 一&#xf…

Leetcode283. 移动零(java实现)

今天分享的题目是leetcode283移动0&#xff08;成都人狂喜&#xff09; 先来看下题目描述 这道题目我也看了官方的题解感觉有点冗余了。 那么我们就不用官方的题解了&#xff0c;分享下我们自己的解体方式。 解题步骤&#xff1a;1.找到非0元素&#xff0c;如果是非0进行记录2…

CentOS6.0安装telnet-server启用telnet服务

CentOS6.0安装telnet-server启用telnet服务 一步到位 fp"/etc/yum.repos.d" ; cp -a ${fp} ${fp}.$(date %0y%0m%0d%0H%0M%0S).bkup echo [base] nameCentOS-$releasever - Base baseurlhttp://mirrors.163.com/centos-vault/6.0/os/$basearch/http://mirrors.a…

永劫无间游戏辅助攻略:2024阵容搭配攻略大全!云手机辅助!

《永劫无间》是一款备受玩家喜爱的动作类游戏&#xff0c;其丰富的角色选择和多样的技能搭配让玩家在战斗中体验到了极大的乐趣。然而&#xff0c;要在竞争激烈的战场上脱颖而出&#xff0c;仅仅依靠基础的游戏理解是远远不够的。为了帮助广大玩家提升战斗力&#xff0c;本文将…

Ruby、Python、Java 开发者必备:Codigger之软件项目体检

在编程的广阔天地里&#xff0c;Ruby、Python 和 Java 开发者们各自凭借着独特的语言特性&#xff0c;构建着精彩纷呈的应用世界。然而&#xff0c;无论使用哪种语言&#xff0c;确保项目的高质量始终是至关重要的目标。而 Codigger 项目体检则成为了实现这一目标的得力助手&am…

python机器学习8--网络

1.超文本传输协议HTTP GET 在实际开发应用程序时&#xff0c;一定都会利用WiFi网络进行连接&#xff0c;再通过HTTP的方式读入后台的数据&#xff0c;并下载和显示在用户的PC上。这靠的是网络服务的技术&#xff0c;也就是大家提到的Web Service。而与HTTP服务器交换数据有两种…

【React】详解“最新”和“最热”切换与排序

文章目录 一、基本概念和初始化二、切换与排序功能的实现1. 函数定义和参数2. 设置活动 Tab3. 定义新列表变量4. 根据排序类型处理列表4.1 按时间降序排序4.2 按点赞数降序排序 5. 更新评论列表 三、渲染导航 Tab 和评论列表1. map 方法2. key 属性3. className 动态赋值4. onC…

数据结构(二叉树)

树 树的概念与结构 树是⼀种⾮线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;⽽叶朝下的。 • 有⼀个特殊的结点&#xff0c;…

linux 桌面版安装原生微信的方式列举

linux 桌面版安装原生微信的方式列举 方法1&#xff1a;下载deb安装包安装 下载地址&#xff1a;https://github.com/lovechoudoufu/wechat_for_linux/releases 安装示例&#xff1a; sudo dpkg -i linuxqq_3.2.10-25765_amd64.deb方法2&#xff1a;Flatpak安装 安装flatp…

docker dotnet-dump离线部署

1.下载指定dotnet版本的dotnet-dump 示例地址&#xff1a; https://www.nuget.org/packages/dotnet-dump/3.1.141901#dependencies-body-tab 我本地测试的是netcore 3.1 2. 在本地解压 将文件解压出来。看到any目录,能看到我们要用的dotnet-dump文件 3. 将tools/netcoreapp2.…

qt实现智能家居(语言控制+服务器监听)

目录 介绍页面展示环境搭建开发工具开发环境百度token 连接服务器 介绍 qt智能家居&#xff08;语音控制&#xff09; 视频演示地址&#xff1a;进入哔哩哔哩 仓库地址&#xff1a;Gitee 源代码&#xff1a;百度网盘 默认密码是123456 页面展示 登录页面演示图 控制中心页…

SpringBoot运行流程源码分析

run方法核心流程 我们在启动SpringBoot的时候调用的是SpringApplication类的静态run方法。其核心流程如下图所示&#xff1a; 在run方法内完成了SpringApplication的声明周期。&#xff0c;这个过程涉及的几个核心类如下&#xff1a; SpringApplicationRunListeners&#xff…

一起学Java(1)-新建一个Gradle管理的Java项目

一时兴起&#xff0c;也为了便于跟大家同步学习进展和分享样例代码&#xff0c;遂决定创建一个全新的Java项目&#xff0c;并通过Github与大家分享。本文就是记录该项目的创建过程以及其中的一些知识要点&#xff08;如Gradle等&#xff09;。为了紧跟技术潮流和提高操作效率&a…

鱼哥好书分享活动第28期:看完这篇《终端安全运营》终端安全企业基石,为你的终端安全保驾护航!

鱼哥好书分享活动第28期&#xff1a;看完这篇《终端安全运营》终端安全企业基石&#xff0c;为你的终端安全保驾护航&#xff01; 读者对象&#xff1a;主要内容&#xff1a;本书目录&#xff1a;了解更多&#xff1a;赠书抽奖规则: 在当前网络威胁日益复杂化的背景下&#xff…

linux nginx 命令记录,和转发

nginx: 查看配置文件&#xff1a;sudo find / -name nginx.conf 配置文件&#xff1a;/etc/nginx/nginx.conf 检查nginx.conf文件正确性 nginx -t -c /path/to/nginx.conf 或者 有nginx命令执行 nginx -t 查找nginx 可执行文件&#xff1a;which nginx /usr/sbin/nginx 安装Ng…

游戏UI设计大师课:3款游戏 UI 设计模板

很多时候&#xff0c;做设计需要找素材。假如是普通的 UI 界面或者 Banner 等等&#xff0c;在Dribbble、Pinterest、即时设计、Behance 翻看这样的网站&#xff0c;至少可以梳理出一些想法和思路。如果你需要一个更规范的指南&#xff0c;此时&#xff0c;在各种设计规范、官方…

.Net 检验信息采集及管理系统LIS,成熟的医院实验室管理系统源码

检验管理系统LIS实现了检验信息电子化、检验信息管理自动化&#xff0c;具备与医嘱双向沟通、采用条码管理手段、财务自动计费、仪器双向控制等重要功能特点。其工作流程为通过门诊医生和住院工作站提出检验申请&#xff0c;生成相应患者的化验条码标签&#xff0c;在生成化验单…

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 问题描述 根据题意写出完…

从煎饼摊到便利店,如何理解公司的商业价值 - 《进阶吧!投资者4》读后感

投资逻辑永远要遵循最基本的商业逻辑。要搞清楚两个最基本问题&#xff0c;认知一家企业最基本的要素是什么&#xff1b;盈利的本质是什么。 评估公司的商业价值 要评估企业的估值&#xff0c;我们可以在战术上用资产未来可以产生现金流的折现来评估资产的价值&#xff0c;如果…