Linux驱动入门 —— LED点灯驱动程序

目录

IMX6ULL 的 GPIO 操作方法

GPIO 操作相关名词

IMX6ULL 的 GPIO 模块结构

GPIO 模块内部

读 GPIO​编辑

写 GPIO​编辑

LED 点灯驱动程序

字符设备驱动程序框架

编写驱动程序的步骤:

先编写驱动程序代码:

再编写测试程序代码:

Makefile

上机实验


IMX6ULL 的 GPIO 操作方法

GPIO 操作相关名词

IMX6ULL 的 GPIO 模块结构

参考芯片手册

有 5 组 GPIO(GPIO1~GPIO5),每组引脚最多有 32 个,但是可能实际上并有那么多。

  • GPIO1 有 32 个引脚:GPIO1_IO0~GPIO1_IO31;
  • GPIO2 有 22 个引脚:GPIO2_IO0~GPIO2_IO21;
  • GPIO3 有 29 个引脚:GPIO3_IO0~GPIO3_IO28;
  • GPIO4 有 29 个引脚:GPIO4_IO0~GPIO4_IO28;
  • GPIO5 有 12 个引脚:GPIO5_IO0~GPIO5_IO11;
  • GPIO 的控制涉及 4 大模块:CCM、IOMUXC、GPIO 模块本身,框图如图 4.2

1.2 CCM 用于设置是否向 GPIO 模块提供时钟

GPIOx 要用 CCM_CCGRy 寄存器中的 2 位来决定该组 GPIO 是否使能。

哪组 GPIO 用哪个 CCM_CCGR 寄存器来设置,请看上图红框部分。

CCM_CCGR 寄存器中某 2 位的取值含义如下:

  • 00:该 GPIO 模块全程被关闭
  • 01:该 GPIO 模块在 CPU run mode 情况下是使能的;在 WAIT 或 STOP 模式下,关闭
  • 10:保留
  • 11:该 GPIO 模块全程使能

GPIO2 时钟控制:

GPIO1、GPIO5 时钟控制:

GPIO3 时钟控制:

GPIO4 时钟控制:

IOMUXC:引脚的模式(Mode、功能)

对于某个/某组引脚,IOMUXC 中有 2 个寄存器用来设置它:

选择功能:

a) IOMUXC_SW_MUX_CTL_PAD_ <PADNAME>:Mux pad xxx,选择 某个 pad 的功能

b) IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>:Mux grp xxx,选 择某组引脚的功能 某个引脚,或是某组预设的引脚,都有 8 个可选的模式(alternate (ALT) MUX_MODE)。

比如:

设置上下拉电阻等参数:

a) IOMUXC_SW_PAD_CTL_PAD_<PAD_NANE>:pad pad xxx,设置某个 pad 的参数

b) IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>:pad grp xxx,设置某组引脚的参数

比如:

GPIO 模块内部

框图如下:

我们暂时只需要关心3个寄存器

GPIOx_GDIR:设置引脚方向,每位对应一个引脚,1-output,0-input

GPIOx_DR:设置输出引脚的电平,每位对应一个引脚,1-高电平,0-低电平

GPIOx_PSR:读取引脚的电平,每位对应一个引脚,1-高电平,0-低电平

读 GPIO

翻译一下:

  • 设置 CCM_CCGRx 寄存器中某位使能对应的 GPIO 模块 // 默认是使能的,上图省略了
  • 设置 IOMUX 来选择引脚用于 GPIO
  • 设置 GPIOx_GDIR 中某位为 0,把该引脚设置为输入功能
  • 读 GPIOx_DR 或 GPIOx_PSR 得到某位的值(读 GPIOx_DR 返回的是 GPIOx_PSR 的值)

写 GPIO

翻译一下:

  • 设置 CCM_CCGRx 寄存器中某位使能对应的 GPIO 模块 // 默认是使能 的,上图省略了
  • 设置 IOMUX 来选择引脚用于 GPIO
  • 设置 GPIOx_GDIR 中某位为 1,把该引脚设置为输出功能
  • 写 GPIOx_DR 某位的值

需要注意的是,你可以设置该引脚的 loopback 功能,这样就可以从 GPIOx_PSR 中读到引脚的有实电平;你从 GPIOx_DR 中读回的只是上次设置的 值,它并不能反应引脚的真实电平,比如可能因为硬件故障导致该引脚跟地短路了,你通过设置 GPIOx_DR 让它输出高电平并不会起效果

LED 点灯驱动程序

字符设备驱动程序框架

字符设备驱动程序的框架如下:

编写驱动程序的步骤:

  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

驱动怎么操作硬件?

  • 通过 ioremap 映射寄存器的物理地址得到虚拟地址,读写虚拟地址。

驱动怎么和 APP 传输数据?

  • 通过 copy_to_user、copy_from_user 这 2 个函数。

先编写驱动程序代码:

  • 实现 led_open 函数,在里面初始化 LED 引脚。
  • 实现 led_write 函数,在里面根据 APP 传来的值控制 LED。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>

static int major;
static struct class *led_class;

/* registers */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	int ret;
	
	/* copy_from_user : get data from app */
	ret = copy_from_user(&val, buf, 1);

	/* to set gpio register: out 1/0 */
	if (val)
	{
		/* set gpio to let led on */
		*GPIO5_DR &= ~(1<<3);
	}
	else
	{

		/* set gpio to let led off */
		*GPIO5_DR |= (1<<3);
	}
	return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
	/* enable gpio5
	 * configure gpio5_io3 as gpio
	 * configure gpio5_io3 as output 
	 */
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;

	*GPIO5_GDIR |= (1<<3);
	
	return 0;
}

static struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

/* 入口函数 */
static int __init led_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zgl_led", &led_fops);

	/* ioremap */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);
	
	// GPIO5_GDIR 地址:0x020AC004
	GPIO5_GDIR = ioremap(0x020AC004, 4);
	
	//GPIO5_DR 地址:0x020AC000
	GPIO5_DR  = ioremap(0x020AC000, 4);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
	
	return 0;
}

static void __exit led_exit(void)
{
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	
	unregister_chrdev(major, "zgl_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

再编写测试程序代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


// ledtest /dev/myled on
// ledtest /dev/myled off

int main(int argc, char **argv)
{
	int fd;
	char status = 0;
	
	if (argc != 3)
	{
		printf("Usage: %s <dev> <on|off>\n", argv[0]);
		printf("  eg: %s /dev/myled on\n", argv[0]);
		printf("  eg: %s /dev/myled off\n", argv[0]);
		return -1;
	}
	// open
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("can not open %s\n", argv[0]);
		return -1;
	}

	// write
	if (strcmp(argv[2], "on") == 0)
	{
		status = 1;
	}

	write(fd, &status, 1);
	return 0;	
}

Makefile

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 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 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f ledtest

obj-m	+= led_drv.o

上机实验

执行 make 命令编译驱动程序和测试程序

启动单板后,可以通过 NFS 挂载 Ubuntu 的某个目录,访问该目录中的程序。

具体挂载步骤可以看我之前写过的文章 开发板挂载 Ubuntu 的 NFS 目录-CSDN博客

打开内核打印:echo "7 4 1 7" > /proc/sys/kernel/printk

insmod led_drv.ko // 装载驱动

ls /dev/myled // 驱动程序会生成设备节点

cat /proc/devices,查看当前已经被使用掉的设备号

驱动名字与我们在驱动层使用register_chrdev()函数的第二个参数有关

./ledtest /dev/myled on // 成功点灯

如果驱动使用完了,不要用了

可用 rmmod led_drv.ko 指令卸载驱动

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神经网络是如何工作的? | 京东云技术团队

作为一名程序员&#xff0c;我们习惯于去了解所使用工具、中间件的底层原理&#xff0c;本文则旨在帮助大家了解AI模型的底层机制&#xff0c;让大家在学习或应用各种大模型时更加得心应手&#xff0c;更加适合没有AI基础的小伙伴们。 一、GPT与神经网络的关系 GPT想必大家已…

理解linux中反向映射与应用

反向映射的作用是根据物理页&#xff0c;找到全部相关进程的vma。 主要有两个结构&#xff0c;anon_vma_chain链表&#xff0c;和 anon_vma->rb_root红黑树 打个不恰当的比喻&#xff1a;可以简单认为&#xff0c;红黑树是用来读的&#xff08;遍历找全部映射的vm_area&am…

web服务器之——www服务器的基本配置

目录 一、www简介 1、什么是www 2、www所用的协议 3、WEB服务器 4、主要数据 5、浏览器 二、 网址及HTTP简介 1、HTTP协议请求的工作流程 三、www服务器的类型(静态网站&#xff08;HTML&#xff09;&#xff0c; 动态网站(jsp python,php,perl)) 1、 仅提供…

python:五种算法(GA、OOA、DBO、SSA、PSO)求解23个测试函数(python代码)

一、五种算法简介 1、遗传算法GA 2、鱼鹰优化算法OOA 3、蜣螂优化算法DBO 4、麻雀搜索算法SSA 5、粒子群优化算法PSO 二、5种算法求解23个函数 &#xff08;1&#xff09;23个函数简介 参考文献&#xff1a; [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…

QT QIFW Windows下制作安装包(一)

一、概述 1、QIFW是一款基于QT框架开发的跨平台安装框架。QIFW是QT Installer FrameWork的缩写&#xff0c;支持Windows、Linux和macos等多种平台。QIFW可以帮助开发者创建自己的安装程序&#xff0c;将它们打包到通用的安装包中&#xff0c;并提供可视化的界面进行安装。 2…

『App自动化测试之Appium基础篇』| Desired Capabilities详解与使用

App自动化测试之Appium基础篇』| Desired Capabilities详解与使用 1 关于appium driver2 安装appium driver3 安装Appium Python Client4 安装测试对象5 获取测试对象信息5.1 使用dumpsys5.2 使用AndroidKiller5.3 使用aapt 6 Capabilities详解6.1 Capabilities介绍6.2 automat…

19-数据结构-查找-散列查找

目录 一、散列查找结构思路图 二、哈希函数 三、解决冲突 1.开放地址法 1.1.线性探测法&#xff08;线性探测再散列法&#xff09; 1.2.平方探测法&#xff08;二次探测再散列&#xff09; 1.3.再散列法&#xff08;双散列法&#xff09; 2.拉链法 2.1简介 四、散列查…

飞天使-linux操作的一些技巧与知识点3-http的工作原理

文章目录 http工作原理nginx的正向代理和反向代理的区别一个小技巧dig 命令巧用 http工作原理 http1.0 协议 使用的是短连接&#xff0c;建立一次tcp连接&#xff0c;发起一次http的请求&#xff0c;结束&#xff0c;tcp断开 http1.1 协议使用的是长连接&#xff0c;建立一次tc…

【ARM Trace32(劳特巴赫) 使用介绍 13 -- Trace32 断点 Break 命令篇】

文章目录 1. Break.Set1.1 TRACE32 Break1.1.1 Break命令控制CPU的暂停1.2 Break.Set 设置断点1.2.1 Trace32 程序断点1.2.2 读写断点1.2.2.1 变量被改写为特定值触发halt1.2.2.2 设定非值触发halt1.2.2.4 变量被特定函数改写触发halt1.2.3 使用C/C++语法设置断点条件1.2.4 使用…

深入理解 SVG:开启向量图形的大门(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

AutoCAD输入命令突然显示 未知命令。按 F1查看帮助。

CAD一直好的&#xff0c;突然坏了&#xff0c;不能输入命令了&#xff0c;其他功能正常。输入命令显示“未知命令XXX&#xff0c;按 F1 查看帮助。” 网上说的什么病毒&#xff0c;卸载重装等无效。结果发现输入的字符是全角的&#xff0c;不是半角的&#xff0c;就输入法的问…

C++面试宝典第5题:判断素数

题目 判断一个正整数是否为素数有哪几种方法&#xff0c;每种方法的时间复杂度怎么样。 解析 素数又称质数&#xff0c;是指在大于1的自然数中&#xff0c;除了1和它本身以外&#xff0c;不再有其他因数的自然数。素数只有1和它本身两个正因数&#xff0c;最小的素数是2&#x…

【Vue】router.push用法实现路由跳转

目录 router.push用法 在Login.vue中 在Register.vue中 ​ 上一篇&#xff1a;登录与注册界面的制作 https://blog.csdn.net/m0_67930426/article/details/134895214?spm1001.2014.3001.5502 制作了登录与注册界面&#xff0c;并介绍了相关表单元素即属性的用法 在登录页面…

第三十四周:文献阅读+LSTM学习

目录 摘要 Abstract 文献阅读&#xff1a;综合EMD-LSTM模型在城市排水管网水质预测中的应用 现有问题 提出方法 EMD-LSTM综合模型 研究框架 结论 Long Short-term Memory(长短期记忆) 1. LSTM的结构 2. Multiple-layer LSTM 3.3 LSTM Example 3. GRU LSTM实现PM2…

【K8S 系列】认识k8s、k8s架构

一、什么是k8s? Kubernetes 简称 k8s&#xff0c;是支持云原生部署的一个平台&#xff0c;k8s 本质上就是用来简化微服务的开发和部署的&#xff0c;用于自动化部署、扩展和管理容器化应用的开源容器编排技术。对于传统的docker其实也提供了容器编排的技术docker-compose&…

Rust语言GUI库之gtk安装

文章目录 工具链安装管理软件vcpkgvcpkg介绍安装vcpkg 安装gtk遇到的问题Rust其他依赖package-confg 工具链安装管理软件vcpkg vcpkg介绍 在使用C/C编写项目时, 引用第三方库是很麻烦的事, 需要手动下载源码然后编译最后再添加到项目里&#xff0c;配置头文件、lib、dll&…

PyTorch: 基于【MobileNet V2】处理MNIST数据集的图像分类任务【准确率99%+】

目录 引言1. 安装PyTorch2. 下载并加载MNIST数据集3. 搭建基于MobileNet V2的图像分类模型运行结果&#xff08;重点看网络开头和结束位置即可&#xff09; 4. 设置超参数、损失函数、优化器5. 训练模型6. 测试模型运行结果 完整代码结束语 引言 在深度学习和计算机视觉的世界…

Windows使用selenium操作浏览器爬虫

以前的大部分程序都是操作Chrome&#xff0c;很少有操作Edge&#xff0c;现在以Edge为例。 Selenium本身是无法直接控制浏览器的&#xff0c;不同的浏览器需要不同的驱动程序&#xff0c;Google Chrome需要安装ChromeDriver、Edge需要安装Microsoft Edge WebDriver&#xff0c…

【PostgreSQL】从零开始:(一)初识PostgreSQL

从零开始:&#xff08;一&#xff09;初识PostgreSQL PostgreSQL数据库介绍为什么使用 PostgreSQL&#xff1f;那么多最终用户,云厂商为什么要贡献核心代码&#xff1f;基于PostgreSQL底层开发的好处&#xff1a;为什么要学习PostgreSQL&#xff1f;截止本文发布之日&#xff0…

Web安全之XXE漏洞原理及实践学习

一、原理&#xff1a; XXE漏洞全称即XML外部实体注入漏洞。 攻击者强制XML解析器去访问攻击者指定的资源内容(可能是系统上本地文件亦或是远程系统上的文件)&#xff0c;导致可加载恶意外部文件&#xff0c;利用file协议造成文件读取、命令执行、内网端口扫描、攻击内网网站等…