Linux——字符设备驱动控制LED
目录
驱动模块的加载和卸载
驱动程序Makefile编写
字符设备注册与注销
字符设备驱动模板
应用程序对驱动读写操作
iounmap函数
LED寄存器物理地址映射到虚拟地址
应用程序代码编写
Linux驱动的两种运行方式:
1、将驱动编译进Linux内核中,也就是zImage,当内核启动的说话就会自动运行驱动程序;
2、将驱动编译成模块(Linux下模块扩展名为.ko),在Linux内核启动以后要用insmod命令加载驱动模块,用rmmod命令卸载驱动模块
驱动模块的加载和卸载
模块的加载和卸载的注册函数如下:
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
编译驱动的时候需要用到linux内核源码,因此要解压缩linux内核源码,然后再编译,得到zImage和.dtb。需要使用编译后的zImage和dtb启动系统。
驱动程序Makefile编写
KERNELDIR := /home/zzs/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_gaCURRENT_PATH := $(shell pwd)obj-m := chrdevbase.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
驱动编译完成以后扩展名为.ko,两种命令可以加载驱动模块:insmod和modprobe
在启动Linux内核后,输入如下命令加载驱动:
insmod drv.ko
modprobe命令相比于insmod命令,区别在于modprobe可以解决依赖关系的问题,insmod命令如果要驱动依赖于first.ko模块的模块,就有一个先加载后加载的问题,而modprobe就比较智能一些,会对模块进行依赖关系的分析,然后就所有的依赖模块都加载到内核中。
我们将编译生成的.ko模块拷贝到rootfs中,通过nfs传输到开发板中进行使用。
使用modprobe命令出现下面的错误:
原因是没有加载modules.dep,通过depmod命令进行加载,如果无法使用depmod,要通过busybox重新配置。
显示当前存在的模块命令如下:
lsmod
驱动模块的卸载使用rmmod命令即可,因为另一种卸载驱动模块的命令modprobe -r的使用前提是所卸载模块的依赖模块已经没有被其他模块使用,否则就不能使用该命令卸载驱动模块:
rmmod drv.ko
使用printk打印日志信息验证模块的加载和卸载:
字符设备注册与注销
字符设备的注册函数
static inline register chrdev(unsigned int major,const char *name,const struct file_operations *fops)
字符设备的注销函数
static inline void unregister_chrdev(unsigned int major,const char *name)
major:主设备号,Linux下每个设备都有一个设备号,设备号分为主设备号和次设备号两部分
name:设备名字
fops:结构体file_operations类型指针,指向设备的操作函数集合变量
设备号由主设备号和次设备号组成,主设备号表示一个具体的驱动,次设备号表示使用这个驱动的各个设备。
设备号的类型为dev_t,是一个定义为u32的数据类型,也就是unsigned int,其中,高12位是主设备号,低20位是次设备号,所有主设备号的范围是0~4095,选择主设备号时不要超出这个范围。
查看当前系统所有已经使用了的设备号:
cat /proc/devices
一般字符设备的注册在驱动模块的入口函数xxx_init中进行,字符设备的注销在驱动模块的出口函数xxx_exit中进行。
字符设备驱动模板
/*打开设备*/
static int chrtest_open(struct inode *inode,struct file *filp)
{/*用户实现具体功能*/return 0;
}/*从设备读取*/
static ssize_t chrtest_read(struct file *filp,char __user *buf,size_t cnt,loft_t *offt)
{/*用户实现具体功能*/return 0;
}/*向设备写数据*/
static ssize_t chrtest_write(struct file *filp,const char __user *buf,size_t cnt,loft_t *offt)
{/*用户实现具体功能*/return 0;
}/*关闭/释放设备*/
static int chrtest_release(struct inode *inode,struct file *filp)
{/*用户实现具体功能*/return 0;
}static struct file_operations test_fops={.owner = THIS_MODULE,.open = chrtest_open,.read = chrtest_read,.write = chrtest_write,.release = chrtest_release,
} ;/*驱动入口函数*/
static int __init xxx_init(void)
{/*入口函数具体内容*/int retvalue = 0;/*注册字符设备驱动*/retvalue = register_chrdev(200,"chrtest",&test_fops);if(retvalue<0){/*字符设备注册失败,自行处理*/}return 0;
}/*驱动出口函数*/
static void __exit xxx_exit(void)
{/*注销字符设备驱动*/unregister_chrdev(200,"chrtest");
}/*将上面两个函数指定为驱动的入口和出口函数*/
module_init(xxx_init);
module_exit(xxx_exit);/*添加LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZHANGZHONGSHENG");
应用程序对驱动读写操作
驱动给应用传递数据的时候用到copy_to_user函数(该函数用来完成内核空间的数据到用户空间的复制),函数原型如下:
static inline long copy_to_user(void *to,const void *from,unsigned long n);
参数to表示目的,参数from表示源,参数n表示要复制的数据长度,如果复制成功,返回值为0,如果复制失败返回负数。
因为用户空间的内存不能直接访问内核空间内存,所以使用copy_from_user函数来实现,函数原型如下:
static inline long copy_from_user(void *to,const void *from,unsigned long n);
字符串转换为整型数据:
首先添加<stdlib>库,然后使用atoi函数。
MMU
全称是Memory Manage Unit,内存管理单元,其主要功能如下:
1、完成虚拟空间到物理空间的映射;
2、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性
裸机的时候可以直接对0x01010101这个物理地址进行操作,但是linux不行,因为linux会使能mmu
在Linux里面操作的都是虚拟地址,所以需要得到0x01010101这个物理地址的虚拟地址。
ioremap函数
如果我们没有使能MMU,可以直接向寄存器地址读写数据,但是我们现在启动Linux内核后,会自动使能MMU,此时我们需要将这个寄存器的物理地址转换为虚拟地址,涉及到两个函数,如下:
ioremap函数用于获取指定物理地址空间对应的虚拟地址空间,定义在arch/arm/include/asm/io.h中,本质是一个宏
第一个参数是物理地址起始大小,第二个参数是要转换的字节数量,例如0x01010101开始的10个地址进行转换:
va = ioremap(0x01010101,10)
返回值是转换的起始地
iounmap函数
卸载驱动时使用iounmap函数释放掉ioremap函数所做的映射
iounmap(va);
寄存器物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE (0X0209C000)
#define GPIO1_GDIR_BASE (0X0209C004)
LED寄存器物理地址映射到虚拟地址
(1)地址映射后的虚拟地址指针
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
(2)物理地址映射成虚拟地址供Linux使用(在驱动入口函数中)
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);
(3)取消地址映射(在驱动出口函数中)
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
(4)对虚拟地址进行初始化配置
val = readl(IMX6U_CCM_CCGR1);
val &= ~(3 << 26); /*先清除以前的配置bit26,27*/
val |= 3 << 26; /*bit26,27置1*/
writel(val,IMX6U_CCM_CCGR1);writel(0x5,SW_MUX_GPIO1_IO03); //设置复用
writel(0X10B0,SW_PAD_GPIO1_IO03); //设置电气属性val = readl(GPIO1_GDIR);
val |= 1 << 3; //bit3置1,设置为输出
writel(val,GPIO1_GDIR); val = readl(GPIO1_DR);
val &= ~(1 << 3); //bit3清零,打开LED
writel(val,GPIO1_DR);
应用程序代码编写
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/**argc:应用程序参数个数*argv:具体的参数内容,字符串形式*./ledAPP <filename> <0:1> 0表示关灯 1表示开灯*./ledAPP /dev/led 0 关灯*./ledAPP /dev/led 1 开灯 */#define LEDOFF 0
#define LEDON 1int main(int argc,char *argv[])
{int fd,retvalue; //fd是文件描述符char *filename; //filename是设备名称unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename,O_RDWR); //O_RDWR是用读写模式if(fd < 0){printf("file %s open failed!\r\n",filename);return -1;}databuf[0] = atoi(argv[2]); //将字符数据转换为数字retvalue = write(fd,databuf,sizeof(databuf)); //对设备进行写操作if(retvalue < 0){printf("LED control Failed!\r\n");close(fd);return -1;}close(fd);return 0;
}
创建设备结点命令:
mknod /dev/led c 200 0
/dev/led是设备名称,是传递给应用程序的第二个参数,c表示字符设备,200是主设备号,0表示次设备号
相关文章:

Linux——字符设备驱动控制LED
目录 驱动模块的加载和卸载 驱动程序Makefile编写 字符设备注册与注销 字符设备驱动模板 应用程序对驱动读写操作 iounmap函数 LED寄存器物理地址映射到虚拟地址 应用程序代码编写 Linux驱动的两种运行方式: 1、将驱动编译进Linux内核中,也就是zIm…...

云边端架构的优势是什么?面临哪些挑战?
一、云边端架构的优势 降低网络延迟:在传统集中式架构中,数据需传输到云计算中心处理,导致网络延迟较高。而云边端架构将计算和存储推向边缘设备,可在离用户更近的地方处理数据,大大降低了网络延迟,提升了用…...

Effective C++ 条款 01:视 C++ 为一个语言联邦
文章目录 条款 01:视 C 为一个语言联邦关键点 条款 01:视 C 为一个语言联邦 C 是一个复杂而多样化的编程语言,可以将其视为由多个子语言(sublanguage)组成的联邦。为了更好地理解和使用 C,需要认识它的四个…...

Redis分布式锁释放锁是否必须用lua脚本?
无lua脚本释放锁: public void unlock(String key, String uniqueValue) {String value redisDao.getString(key);if (value ! null && value.equals(uniqueValue))redisDao.delete(key); }使用lua脚本释放锁: // LUA脚本 -> 分布式锁解锁原…...

JVM系列(十三) -常用调优工具介绍
最近对 JVM 技术知识进行了重新整理,再次献上 JVM系列文章合集索引,感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…...

数据中心虚拟化与高可用性架构实施指南
数据中心虚拟化与高可用性架构实施指南 项目背景 随着业务的不断扩展和技术的迭代更新,公司决定采用虚拟化技术和构建高可用性架构来提高数据中心的资源利用率和业务连续性。本项目旨在详细描述运维人员在实施数据中心虚拟化和高可用性架构过程中的关键步骤和任务…...

对文件内的文件名生成目录,方便查阅
import os import re# 定义要查找的目录路径 path r"J:\...\顺序目录" # 要遍历的主目录路径# 定义输出的目录文件路径 output_file r"J:\...\目录_中文文件.txt" # 保存结果的文件路径# 判断文件名是否包含中文字符 def contains_chinese(text):retur…...

leetcode hot100 轮转数组
189. 轮转数组 已解答 中等 相关标签 相关企业 提示 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮…...

定位方式:css
使用相对路径 div ul #div下的所有ul,空格表示相对路径(这个实际中用的多一些) 绝对路径-一般不用绝对路径 html>head>div,“>”表示根路径 使用class名称定位 使用.表示 使用id定位 使用#表示 使用属性定位 [属性名…...

谷歌浏览器的网络连接问题解决方案
在数字化时代,网络浏览器已成为日常工作和生活中不可或缺的工具。谷歌浏览器以其快速、稳定和丰富的功能深受用户喜爱。然而,就像其他软件一样,谷歌浏览器也可能遇到网络连接问题,这可能由多种因素引起。本文将为您提供一系列解决…...

保护模式基本概念
CPU 架构 RISC(Reduced Instruction Set Computer) 中文即"精简指令集计算机”。RISC构架的指令格式和长度通常是固定的(如ARM是32位的指令)、且指令和寻址方式少而简单、大多数指令在一个周期内就可以执行完毕 CISC&…...

Linux程序设计(第四版)| 学习笔记
上次学习Linux相关内容还是上学的时候为了应付考试,最近有项目涉及Linux,重新学习以下。 很多年前关于Linux的总结 一、入门 1.概念 (1) UNIX 1)定义:指的是一种遵循特定规范的计算机操作系统。 2)特点:简单性、集中性、可重用…...

【Python-中级】Python中的线程池:ThreadPoolExecutor
Python中的线程池:from concurrent.futures import ThreadPoolExecutor 在Python中,实现多线程编程的方法有很多,而ThreadPoolExecutor 是一个简单且高效的线程池工具。它提供了高层次的接口,用于并发地运行任务,同时隐藏了许多复杂的底层细节,非常适合日常的多线程任务…...

医疗信息系统有哪些
医疗信息系统(Health Information Systems, HIS)是用于管理和存储患者健康数据、医疗记录、医院运营数据等信息的技术平台。这些系统通过自动化、集成和分析数据,提高医疗服务的效率、质量和安全性。以下是一些主要的医疗信息系统及其功能&am…...

JVM系列(十二) -常用调优命令汇总
最近对 JVM 技术知识进行了重新整理,再次献上 JVM系列文章合集索引,感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…...

修改采购订单BAPI学习研究-BAPI_PO_CHANGE
这里是修改采购订单BAPI,修改订单数量的简单应用 代码 *&---------------------------------------------------------------------* *& Report Z_BAPI_PO_CHANGE *&---------------------------------------------------------------------* *& C…...

使用uniapp开发微信小程序-框架搭建
最近要开发一个小程序,为了降低学习成本,最后还是选择使用uniapp来开发,但是由于电脑性能不行,实在不想多跑一个应用程序,就简单搭建了一个可以用vscode跑的uniapp项目。 项目整体技术栈:vue3 pinia type…...

雅思真题短语(九)
真题短语收录在合辑 141华夫饼 waffles 142防腐剂 no preservatives 143精制 refined sugar 144水分充足 plenty of moisture 145五角星 five-pointed star 146树皮 tree bark 147汁液 liquid sap 148敲击 several taps 149蒸发 water evaporates 150干旱的山谷 arid valleys …...

系统压力测试助手——stress-ng
1、背景 在系统性能测试和压力测试中,stress-ng 是一个非常强大的工具,广泛应用于对 Linux 系统进行各种硬件和软件方面的负载测试。它能够模拟多种极端负载情况,帮助开发人员和运维人员检查系统在高负载下的表现,以便发现潜在的…...

java如何使用poi-tl在word模板里渲染多张图片
1、poi-tl官网地址 http://deepoove.com/poi-tl/ 2、引入poi-tl的依赖 <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.1</version></dependency>3、定义word模板 释义…...

UE5 UHT GENERATED_BODY() GENERATED_USTRUCT_BODY()
你在 .h 文件中写的 GENERATED_BODY() 宏本身并不是 GetPrivateStaticClass() 函数的声明。 实际上,GENERATED_BODY() 是一个预处理器宏,它会被 Unreal Header Tool (UHT) 处理。 UHT 会读取你的 .h 文件,识别 UCLASS() 和 GENERATED_BODY(…...

内网穿透ubuntu20 docker coplar
sudo apt-get install curl curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash ubuntu-base报错 /sbin/init:No such file or directory解决办法 apt install systemd 命令安装即可 cpolar version 1.3 token认证 登录cpolar官网后台…...

windows C++ TCP客户端
demo有一下功能 1、心跳包 2、断开重连 3、非阻塞 4、接受数据单独线程处理 #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <string> #include <process.h> // 用于Windows下的线程相…...

Linux xargs 命令使用教程
简介 xargs 是一个功能强大的 Linux 命令,用于从标准输入构建和执行命令。它接受一个命令的输出,并将其作为参数提供给另一个命令。它在处理大量输入时特别有用,其含义可以解释为:extended arguments,使用 xargs 允许…...

什么是异步处理
什么是异步处理 if ( conditionA && conditionB )mqSendService.sendMessageAsync(MqTopicConstant.YOUR_TOPIC, ID,JSONObject.toJSONString(CommonMsg.builder().data(ID).msgType(TypeCode).build()));}sendMessageAsync 发送消息的过程不会阻塞当前的执…...

【解决问题】Java2DRenderer生成图片时中文乱码 Linux安装字体
一,问题 在使用Java2DRenderer框架将html生成图片时,html中的中文文本在图片上显示框框,即出现了中文乱码。在确认使用正确的字符编码utf-8之后,并且确认了修改成unicode也同样乱码的情况下,找到了真正的原因…...

WPF 依赖属性和附加属性
除了普通的 CLR 属性, WPF 还有一套自己的属性系统。这个系统中的属性称为依赖属性。 1. 依赖属性 为啥叫依赖属性?不叫阿猫阿狗属性? 通常我们定义一个普通 CLR 属性,其实就是获取和设置一个私有字段的值。假设声明了 100 个 …...

leetcode hot100 删除链表的第n个节点
19. 删除链表的倒数第 N 个结点 已解答 中等 相关标签 相关企业 提示 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNon…...

MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
1.1创建ThreadLocal工具类(作为业务逻辑结果存放类) package org.springblade.sample.utils;public class QueryContext {private static final ThreadLocal<Long> totalInThreadLocal new ThreadLocal<>();public static void setTotalIn…...

记一MySQL连接速度慢的问题
某一个程序启动速度超级慢,查看日志得知是是在Init DruidDataSource ~ {dataSource-1} inited 这一段耗时最长,这一段是Druid 数据源初始化,进行连接的创建等,使用mysql命令行连接发现连接超级慢,可见是在创建连接的时…...