驱动开发---基于gpio子系统编写LED灯的驱动

一、GPIO子系统相关API

1.解析GPIO相关的设备树节点

struct device_node *of_find_node_by_path(const char *path)
功能:根据设备树节点路径解析设备树节点信息
参数:
path:设备树所在的节点路径   /mynode@0X12345678
返回值:成功返回目标节点首地址,失败返回NULL

2.根据解析的GPIO相关节点信息获取GPIO编号

#include<linux/of_gpio.h>
int of_get_named_gpio(struct device_node *np,const char *propname, int index)
功能:获取GPIO编号
参数:np:设备树节点指针proname:gpio编号信息对应到的键名index:管脚在这个属性键值对中的索引号
返回值:成功返回GPIO编号,失败返回错误码

3.向内核申请要使用的GPIO编号

#include<linux/gpio.h>
int gpio_request(unsigned gpio, const char *label)
功能:申请GPIO编号
参数:gpio:要申请的GPIO编号label:标签,填NULL
返回值:成功返回0,失败返回错误码
int gpio_direction_input(unsigned gpio)
功能:将gpio编号对应的gpio管脚设置为输入
参数:gpio:gpio编号
返回值:成功返回0,失败返回错误码
int gpio_direction_output(unsigned gpio, int value)
功能:将gpio编号对应的gpio管脚设置为是输出
参数:gpio:gpio编号value:默认的输出值  (1)输出高电平  (0)输出低电平
返回值:成功返回0,失败返回错误码
void gpio_set_value(unsigned gpio, int value)
功能:设置gpio编号对应的gpio管脚 输出高低电平
参数:gpio:gpio编号value:默认的输出值  (1)输出高电平  (0)输出低电平
返回值:无 
int gpio_get_value(unsigned gpio)
功能:获取gpio编号对应到的GPIO引脚状态值
参数:
gpio:gpio编号
返回值:1(高电平)  0(低电平状态)
void gpio_free(unsigned gpio)
功能:释放GPIO编号
参数:要释放的gpio编号

二、添加LED的设备树节点信息

在stm32mp157a-fsmp1a.dts文件的根节点中添加如下内容

myled{led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚  0,表示gpio默认属性led2-gpio=<&gpiof 10 0>;led3-gpio=<&gpioe 8 0>;
};

三、基于gpio子系统编写LED灯的驱动

1.头文件

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28//构建LED开关的功能码
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)
#endif 

2.测试文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"
int main(int argc, const char *argv[])
{char buf[128] = "";int a;int fd;while (1){printf("请选择要打开的灯(1,2,3)\n");scanf(" %d", &a);switch (a){case 1:fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("设备文件打开失败\n");exit(-1);}printf("打开文件myled0成功\n");break;case 2:fd = open("/dev/myled1", O_RDWR);if (fd < 0){printf("设备文件打开失败\n");exit(-1);}printf("打开文件myled1成功\n");break;case 3:fd = open("/dev/myled2", O_RDWR);if (fd < 0){printf("设备文件打开失败\n");exit(-1);}printf("打开文件myled2成功\n");break;default:printf("请输入范围内的数\n");}int b;printf("请开灯关灯(0/1)\n");scanf(" %d",&b);switch(b){case 1:ioctl(fd,LED_ON);break;case 0:ioctl(fd,LED_OFF);break;default:printf("请输入'0'或'1'\n");}close(fd);printf("关闭文件\n");		}return 0;
}

3.驱动文件

#include <linux/init.h>
#include <linux/module.h>
#include<linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "head.h"struct cdev *cdev;
char kbuf[128]={0};
unsigned int major=0;
unsigned int minor=0;
dev_t devno;
module_param(major,uint,0664);//方便在命令行传递major的值
struct class*cls;
struct device *dev;gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct device_node *dnode;
struct gpio_desc *gpiono1;
struct gpio_desc *gpiono2; 
struct gpio_desc *gpiono3;  int mycdev_open(struct inode *inode, struct file *file)
{int min=MINOR(inode->i_rdev);//根据打开的文件对应的设备号获取次设备号file->private_data=(void *)min;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int min=(int)file->private_data;switch(min){case 0://控制LED1switch (cmd){case LED_ON: // 开灯gpiod_set_value(gpiono1,1);break;case LED_OFF://关灯gpiod_set_value(gpiono1,0);break;}break;case 1://控制LED2switch (cmd){case LED_ON: // 开灯gpiod_set_value(gpiono2,1);break;case LED_OFF://关灯gpiod_set_value(gpiono2,0);break;}break;case 2://控制LED3switch (cmd){case LED_ON: // 开灯gpiod_set_value(gpiono3,1);break;case LED_OFF://关灯gpiod_set_value(gpiono3,0);break;}break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};int all_led_init(void)
{// 寄存器地址的映射vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));if (vir_led1 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));if (vir_led2 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led3 = vir_led1;vir_rcc = ioremap(PHY_RCC_ADDR, 4);if (vir_rcc == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}printk("物理地址映射成功\n");// 寄存器的初始化// rcc(*vir_rcc) |= (0X3 << 4);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_led3->MODER &= (~(3 << 16));vir_led1->MODER |= (1 << 16);vir_led1->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{int ret;//为字符设备驱动对象申请空间cdev=cdev_alloc();if(cdev==NULL){printk("字符设备驱动对象申请空间失败\n");ret=-EFAULT;goto out1;}printk("申请对象空间成功\n");//初始化字符设备驱动对象cdev_init(cdev,&fops);//申请设备号if(major>0)//静态指定设备号{ret=register_chrdev_region(MKDEV(major,minor),3,"myled");if(ret){printk("静态申请设备号失败\n");goto out2;}}else if(major==0)//动态申请设备号{ret=alloc_chrdev_region(&devno,minor,3,"myled");if(ret){printk("动态申请设备号失败\n");goto out2;}major=MAJOR(devno);//获取主设备号minor=MINOR(devno);//获取次设备号}printk("申请设备号成功\n");//注册字符设备驱动对象ret=cdev_add(cdev,MKDEV(major,minor),3);if(ret){printk("注册字符设备驱动对象失败\n");goto out3;}printk("注册字符设备驱动对象成功\n");//寄存器映射及初始化all_led_init();//向上提交目录信息cls=class_create(THIS_MODULE,"myled");if(IS_ERR(cls)){printk("向上提交目录失败\n");ret=-PTR_ERR(cls);goto out4;}printk("向上提交目录成功\n");//向上提交设备节点信息int i;for(i=0;i<3;i++){dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);if(IS_ERR(dev)){printk("向上提交设备节点信息失败\n");ret=-PTR_ERR(dev);goto out5;}}printk("向上提交设备信息成功\n");//解析LED的设备树节点dnode=of_find_node_by_path("/myled");if(dnode==NULL){printk("解析设备树节点失败\n");return -ENXIO;}printk("解析GPIO信息成功\n");gpiono1=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono1)){printk("申请gpio对象失败\n");return -ENXIO;}printk("申请gpio信息对象成功\n");gpiono2=gpiod_get_from_of_node(dnode,"led2-gpio",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono2)){printk("申请gpio对象失败\n");return -ENXIO;}printk("申请gpio信息对象成功\n");gpiono3=gpiod_get_from_of_node(dnode,"led3-gpio",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono3)){printk("申请gpio对象失败\n");return -ENXIO;}printk("申请gpio信息对象成功\n");return 0;
out5://释放前一次提交成功的设备信息for(--i;i>=0;i--){device_destroy(cls,MKDEV(major,i));}class_destroy(cls);//释放目录
out4:cdev_del(cdev);
out3:unregister_chrdev_region(MKDEV(major,minor),3);
out2:kfree(cdev);
out1:return ret;
}
static void __exit mycdev_exit(void)
{//释放对象指针gpiod_put(gpiono1);gpiod_put(gpiono2);gpiod_put(gpiono3);/*销毁设备节点信息*/int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录信息class_destroy(cls);//注销驱动对象cdev_del(cdev);//释放设备号unregister_chrdev_region(MKDEV(major,minor),3);//释放对象空间kfree(cdev);// 取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

4.Makefile

modname?=demo  #给定一个默认的模块名
arch?=arm #给定一个架构的模块名
ifeq ($(arch),arm)#判断要编译的架构进而使用不同的Makefile规则进行编译
#定义变量存放内核源码顶层路径
KERNELDIR:= /home/ubuntu/FSMP1A/linux-stm32mp-5.10.61-stm32mp-r2-r0/linux-5.10.61 #用于编译生成ARM架构的模块
else
KERNELDIR := /lib/modules/$(shell uname -r)/build  #用于生成x86架构的模块
endif#定义变量保存模块化编译的文件的路径
PWD:= $(shell pwd)
all:
#make modules是模块化编译的命令
#make -C $(KERNELDIR)表示读取KERNELDIR路径下的Makefile进行make编译
#M=$(PWD)指定模块化编译的路径make -C $(KERNELDIR) M=$(PWD) modules
clean: #编译清除make -C $(KERNELDIR) M=$(PWD) cleanobj-m:=$(modname).o  #指定将demo.o独立链接生成内核模块文件

5.测试

1.make arch=arm modname=myled

2.arm-linux-gnueabihf-gcc main.c

3.cp a.out ~/nfs/rootfs

4.cp myled.ko ~/nfs/rootfs

 6.测试结果

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

JS的基本组成

JavaScript的实现包括以下3个部分&#xff1a; 模块功能ECMAScript(核心)描述了JS的语法和基本对象。文档对象模型 &#xff08;DOM&#xff09;处理网页内容的方法和接口浏览器对象模型&#xff08;BOM&#xff09;与浏览器交互的方法和接口 javascript 有三部分构成&#…

腾讯mini项目-【指标监控服务重构-会议记录】2023-07-26

2023-07-26组长会议纪要 A组 项目对齐和问题 分配需求&#xff0c;SLI指标上报&#xff0c;暂时没有实际效果 每个人负责一条指标&#xff0c;同步代码&#xff0c;时间问题还是难题跟B组同学请教&#xff0c;答疑 问题&#xff1a;启动 Tracer 【已解决】 环境问题&#xf…

Python读取Excel每一行为列表—大PK(openpyxl\pandas\xlwings\xlrd)看谁用时少?

目录 背景使用—openpyxl&#xff08;耗时89秒输出&#xff09;使用—pandas&#xff08;耗时44秒输出&#xff09;使用—xlwings&#xff08;耗时15秒输出&#xff09;使用—xlrd&#xff08;耗时47秒输出&#xff09;总结 背景 我们在平常办公的时候&#xff0c;尤其是财务人…

asisctf 2023 web hello wp

hello 开题&#xff0c;直接给了源码。 <?php /* Read /next.txt Hint for beginners: read curls manpage. */ highlight_file(__FILE__); $url file:///hi.txt; if(array_key_exists(x, $_GET) &&!str_contains(strtolower($_GET[x]),file) && !str_c…

ElementUI之动态树+数据表格+分页

目录 前言 一.ElementUI之动态树 1.前端模板演示 2.数据绑定 2.1 通过链接获取后台数据 2.2 对链接进行绑定 2.3添加动态路由 2.4 配置路由 3.效果演示 二.数据表格动态分页 1.前端模板 2.通过JS交互获取后端数据 3 效果演示 前言 Element UI 是一个基于 Vue.js 的开…

64位Ubuntu20.04.5 LTS系统安装32位运行库

背景&#xff1a; 在ubutu&#xff08;版本为20.04.5 LTS&#xff09;中运行./arm-none-linux-gnueabi-gcc -v 后提示“no such device”。 经多方查证&#xff0c;是ubutu的版本是64位的&#xff0c;而需要运行的编译工具链是32位的&#xff0c;因此会不兼容。 解决方法就是在…

反射学习笔记

反射学习笔记 一、反射入门案例 在反射中&#xff0c;万物皆对象&#xff0c;方法也是对象。反射可以在不修改源码的情况下&#xff0c;只需修改配置文件&#xff0c;就能实现功能的改变。 实体类 /*** 动物猫类*/ public class Cat {private String name;public void hi()…

系统安全测试要怎么做?

进行系统安全测试时&#xff0c;可以按照以下详细的步骤进行&#xff1a; 1、信息收集和分析&#xff1a; 收集系统的相关信息&#xff0c;包括架构、部署环境、使用的框架和技术等。 分析系统的安全需求、威胁模型和安全策略等文档。 2、威胁建模和风险评估&#xff1a; …

gitee-快速设置

快速设置— 如果你知道该怎么操作&#xff0c;直接使用下面的地址 HTTPS SSH: gitgitee.com:liuzl33078235/esp-idf.git 我们强烈建议所有的git仓库都有一个README, LICENSE, .gitignore文件 初始化 readme 文件 Git入门&#xff1f;查看 帮助 , Visual Studio / TortoiseG…

el-table 指定层级展开

先来看看页面默认全部展开时页面的显示效果&#xff1a;所有节点被展开&#xff0c;一眼望去杂乱无章&#xff01; 那么如何实现只展开指定的节点呢&#xff1f;最终效果如下&#xff1a;一眼看去很舒爽。 干货上代码&#xff1a; <el-table border v-if"refreshTabl…

软件测试-测试用例

软件测试-测试用例 1.什么是测试用例 为了实施测试而向被测系统提供的一组集合。这组集合包括测试环境、操作步骤、测试数据、预期结果等要素。 举例&#xff1a;对一个垃圾桶设计测试用例 2.设计测试用例的万能公式 设计测试用例的万能公式&#xff1a;功能测试性能测试界…

卤制品配送经营商城小程序的用处是什么

卤制品也是食品领域重要的分支&#xff0c;尤其对年轻人来说&#xff0c;只要干净卫生好吃价格合理&#xff0c;那复购率宣传性自是不用说&#xff0c;而随着互联网发展&#xff0c;传统线下门店也须要通过线上破解难题或进一步扩大生意。 而商城小程序无疑是商家通过线上私域…

【漏洞复现】企望制造 ERP命令执行

漏洞描述 由于企望制造 ERP comboxstore.action接口权限设置不当&#xff0c;默认的配置可执行任意SQL语句&#xff0c;利用xp_cmdshell函数可远程执行命令&#xff0c;未经认证的攻击者可通过该漏洞获取服务器权限。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织…

Maven项目package为jar包后在window运行报A JNI error has occurred

原因&#xff1a;本地java版本与项目结构中使用的java版本不一致&#xff08;之前因为别的需求把idea的java版本改为了18&#xff09; 解决方法 打开项目结构&#xff0c;将idea的java版本改为与本地一致 再修改项目中的pom.xml 重新编译&#xff0c;package即可

Spark集成ClickHouse(笔记)

目录 前言&#xff1a; 一.配置环境 1.安装clickhouse驱动 2.配置clickhouse环境 二.spark 集成clickhouse 直接上代码&#xff0c;里面有一些注释哦&#xff01; 前言&#xff1a; 在大数据处理和分析领域&#xff0c;Spark 是一个非常强大且广泛使用的开源分布式计算框架…

uni-app:实现页面效果1

效果 代码 <template><view><view class"add"><image :src"add_icon" mode""></image></view><view class"container_position"><view class"container_info"><view c…

【SQL server】数据库入门基本操作教学

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 前言 数据库是计算机系统中用于存储和管理数据的一种软件系统。它通常由一个或多个数据集合、管理系统和应用程序组成&#xff0c;被广泛应用于企业、政府和个人等各种领域。目前常用的数…

服务器搭建(TCP套接字)-epoll版(服务端)

epoll 是一种在 Linux 系统上用于高效事件驱动编程的 I/O 多路复用机制。它相比于传统的 select 和 poll 函数具有更好的性能和扩展性。 epoll 的主要特点和原理&#xff1a; 1、事件驱动&#xff1a;epoll 是基于事件驱动的模型&#xff0c;它通过监听事件来触发相应的回调函…

爬楼梯Java(斐波那契数列)

题目:有n阶楼梯,一次只能爬一层或者两层,请问有多少种方法? 这类题目其实都可以用斐波那契数列来解决,比如: 一阶楼梯只有一种方法 二阶楼梯有(11,2)两种方法 三阶楼梯有(111,12,21)三种方法 四阶楼梯有(1111,121,112,22,211)五种方式 五阶楼梯有(11111,1112,122,1211,1…

Servlet执行流程生命周期方法介绍体系结构、Request和Response的功能详解

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Servlet 一、 Servlet执行流程二、Servlet生…