GPIO子系统编写LED灯的驱动、linux内核定时器

一、GPIO子系统

1.概念:

一个芯片厂商生产出芯片后会给linux提供一个当前芯片中gpio外设的驱动,我们当前只需要调用对应的厂商驱动即可完成硬件的控制。而linux内核源码中的gpio厂商驱动有很多,这里linux内核对厂商驱动做了一些封装,提供了一系列的API,我们在自己编写的设备驱动中只需要调用这些API即可访问对应的厂商驱动,进而完成GPIO的控制

2.API

添加LED的设备树节点

myled

{

        led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚 0,表示gpio默认属性

        led2-gpio=<&gpiof 10 0>;

        led3-gpio=<&gpioe 8 0>;

};

或者

myled{ led-gpios=<&gpioe 10 0>,<&gpiof 10 0>,<&gpioe 8 0>; };

执行make dtbs编译设备树,将编译生成的设备树镜像拷贝到~/tftpboot目录下,重启开发板

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

        struct device_node *of_find_node_by_path(const char *path)

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

        #include<linux/of_gpio.h>

         int of_get_named_gpio(struct device_node *np(设备树节点指针),const char *propname(gpio编号信息对应的键名), int index(属性键值对中的索引号))

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

        #include<linux/gpio.h>

        int gpio_request(unsigned gpio, const char *label)

4)将gpio编号对应的gpio管脚设置为输出

        int gpio_direction_output(unsigned gpio, int value)

5)设置gpio编号对应的gpio管脚 输出高低电平

        void gpio_set_value(unsigned gpio, int value)

6)获取gpio编号对应到的GPIO引脚状态值

        int gpio_get_value(unsigned gpio)

7)释放GPIO编号

        void gpio_free(unsigned gpio)

3.新版API

核心不再是GPIO编号,而是GPIO对象

1)在设备树节点中解析出GPIO对象,并向内核申请

struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,

                        const char *propname, int index, enum gpiod_flags dflags, const char *label)

2)int gpiod_direction_output(struct gpio_desc *desc, int value)

     int gpiod_direction_input(struct gpio_desc *desc)

     void gpiod_set_value(struct gpio_desc *desc, int value)

     int gpiod_get_value(const struct gpio_desc *desc)

     void gpiod_put(struct gpio_desc *desc)//释放gpi对象指针

二、linux内核定时器

定时时间到达之后可以执行当前的定时器处理函数

1..jiffies

        内核中用于保存内核节拍数的一个变量。它的值从内核启动开始就不断从0开始增加。

2.内核频率

        内核节拍数一秒钟增加的数量被称为内核的频率,内核的频率在内核顶层目录下的.config文件中被设置

3.定时器启用相关的API

1)分配定时器对象

        struct timer_list mytimer;

2)初始化定时器对象

        void timer_setup(struct timer_list *timer, void (*func)(struct timer_list *), unsigned int flags);

3)注册定时器对象并启用定时器

       void add_timer(struct timer_list *timer) 

4)再次启用定时器

        int mod_timer(struct timer_list *timer, unsigned long expires)

5)注销定时器对象

        int del_timer(struct timer_list *timer)

三、任务

GPIO子系统编写LED灯的驱动(使用新版API)

head.h

#ifndef __HEAD_H__
#define __HEAD_H__ 
//构建LED开关功能码,添加ioctl第三个参数
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)
#endif 

myled.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
#include<linux/io.h>
#include"head.h"
struct cdev* cdev;
unsigned int major=0;
unsigned int minor=0;
dev_t devno;
module_param(major,uint,0664);
struct class* cls;
struct device* dev;
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 mydev_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
{int min=(int)file->private_data;switch(min){case 0:switch(cmd){case LED_ON:  //开灯gpiod_set_value(gpiono1,1);break;case LED_OFF: //关灯gpiod_set_value(gpiono1,0);break;}break;case 1:switch(cmd){case LED_ON:  //开灯gpiod_set_value(gpiono2,1);break;case LED_OFF: //关灯gpiod_set_value(gpiono2,0);break;}break;case 2:switch(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=mydev_ioctl,.release=mycdev_close,
};
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");//向上提交目录信息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");dnode=of_find_node_by_path("/myled");if(dnode==NULL) {printk("解析设备树节点失败\n");return -ENXIO;}printk("解析GPIO信息成功\n");//申请gpio对象gpiono1=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono1)){printk("申请gpio对象失败\n");return -ENXIO;}printk("申请led1-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("申请led2-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("申请led3-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_set_value(gpiono1,0);gpiod_set_value(gpiono2,0);gpiod_set_value(gpiono3,0);//释放gpio编号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);}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

proc.c

#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;
}

测试过程:

make arch=arm modname=myled

arm-linux-gnueabihf-gcc proc.c

 cp a.out ~/nfs/rootfs

cp myled.ko ~/nfs/rootfs

insmod myled.ko 

 ./a.out 

 rmmod myled3

测试现象:

linux内核定时器实例代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/timer.h>
struct device_node *dnode;
struct gpio_desc* gpiono;
//分配定时器对象
struct timer_list mytimer;
//设置一个定时器处理函数
void mytimer_func(struct timer_list* timer)
{//LED1一秒亮一秒灭gpiod_set_value(gpiono,!gpiod_get_value(gpiono));//再次启动定时器mod_timer(timer,jiffies+HZ); }
static int __init mycdev_init(void)
{dnode=of_find_node_by_path("/myled");if(dnode==NULL) {printk("解析设备树节点失败\n");return -ENXIO;}printk("解析GPIO信息成功\n");//申请gpio对象gpiono=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);if(IS_ERR(gpiono)){printk("申请gpio对象失败\n");return -ENXIO;}printk("申请gpio信息对象成功\n");//初始化定时器对象timer_setup(&mytimer,mytimer_func,0);mytimer.expires=jiffies+HZ;//注册定时器add_timer(&mytimer);return 0;
}
static void __exit mycdev_exit(void)
{//注销定时器del_timer(&mytimer);//灭灯gpiod_set_value(gpiono,0);//释放gpio编号gpiod_put(gpiono);}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

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

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

相关文章

【管理运筹学】第 8 章 | 动态规划(2,动态规划的基本思想)

文章目录 引言2.2 动态规划的基本思想 写在最后 引言 承接前文&#xff0c;介绍完基本概念后&#xff0c;我们来学习动态规划的基本思想&#xff0c;用上一篇文章的最短路问题来配合说明。 2.2 动态规划的基本思想 最短路问题中的网络如下图所示&#xff0c;从 A 到 E 可以分…

零基础学前端(四)重点讲解 CSS

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…

SpringMVC之自定义注解

一.什么是SpringMVC之自定义注解 二.Java注解简介 Java注解分类 JDK元注解 三.自定义注解简介 自定义注解的分类 四.自定义注解的基本案例 案例一&#xff08;获取类与方法上的注解值&#xff09; 案例二&#xff08;获取类属性上的注解属性值&#xff09; 案例三&a…

MyBatis笔记

Mybatis简介 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c;iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到GithubiBatis一词来源于“intern…

【数据结构与算法】不就是数据结构

前言 嗨喽小伙伴们你们好呀&#xff0c;好久不见了,我已经好久没更新博文了&#xff01;之前因为实习没有时间去写博文&#xff0c;现在已经回归校园了。我看了本学期的课程中有数据结构这门课程&#xff08;这么课程特别重要&#xff09;&#xff0c;因为之前学过一点&#xf…

数据结构与算法(三)——递归

一、递归的概念 递归就是方法自己调用自己&#xff0c;每次调用时传入不同的变量。 递归有助于编程者解决复杂的问题&#xff0c;同时可以让代码变得简洁。 1.1 递归机制 递归调用规则&#xff1a; 1>当程序执行到一个方法时&#xff0c;就会开辟一个独立的空间&#xff0…

vr飞机驾驶舱模拟流程3D仿真演示加大航飞安全法码

众所周知&#xff0c;航空航天飞行是一项耗资大、变量参数很多、非常复杂的系统工程&#xff0c;因此可利用虚拟仿真技术经济、安全及可重复性等特点&#xff0c;进行飞行任务或操作的模拟&#xff0c;以代替某些费时、费力、费钱的真实试验或者真实试验无法开展的场合&#xf…

2023 Google 开发者大会:Web平台新动向

目录 前言一、Open in WordPress playground二、WebGPU三、新的核心 Web 指标INP四、Webview1、Custom Tabs2、JavaScriptEngine 五、Passkeys六、View Transitions API七、Google Chrome开发者工具优化1、覆盖HTTP的响应标头2、改变stack trance 八、Baseline总结 前言 在前不…

攻防世界-WEB-easyupload

1.新建.user.ini文件&#xff0c;内容如下 GIF89a auto_prepend_filea.jpg 2.上传该文件&#xff0c;并用burp抓包&#xff0c;将Content-Type: application/octet-stream修改为 Content-Type: image/jpg 3.放包&#xff0c;结果如下 4. 新建a.txt文件&#xff0c;内容为 GIF89…

插槽指的是什么?插槽的基础用法体验

什么是插槽 插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时&#xff0c;把不确定的、希望由用户指定的部分定义为插槽。 <template><p>这是MyCom1组件的第1个p标签</p><&#xff01;--通过slot标签&#xff0c;为用户预留内容占位符…

蓝牙核心规范(V5.4)10.1-BLE 入门笔记(1)

ble 规范 深入了解蓝牙LE需要熟悉相关的规格。蓝牙LE的架构、程序和协议由一项关键规范完全定义,称为蓝牙核心规范。产品如何使用蓝牙以实现互操作性由两种特殊类型称为配置文件和服务的规范集合所涵盖。图1展示了BLE规范类型及其相互关系。 1.1 蓝牙核心规范 蓝牙核心规范是…

html+js写一个可编辑的元素 支持直接向上粘贴文本或图片

有一说一来讲 CSDN 博客的编辑器还是非常厉害的 能够完美设配图片与文字的粘贴与输入 但其实 如果做个捡漏版的 js也可以完成 但这里 为了方便 我选择了vue2的环境 参考代码如下 <template><div class"editable-div" contenteditable"true" past…

WavJourney:进入音频故事情节生成世界的旅程

推荐&#xff1a;使用 NSDT场景编辑器快速搭建3D应用场景 若要正确查看音频生成的强大功能&#xff0c;请考虑以下方案。我们只需要提供一个简单的指令&#xff0c;描述场景和场景设置&#xff0c;模型就会生成一个扣人心弦的音频脚本&#xff0c;突出与原始指令的最高上下文相…

小米6/6X/米8/米9手机刷入鸿蒙HarmonyOS.4.0系统-刷机包下载-遥遥领先

小米手机除了解锁root权限&#xff0c;刷GSI和第三方ROM也是米粉的一大爱好&#xff0c;这不&#xff0c;在华为发布了HarmonyOS.4.0系统后不久&#xff0c;我们小米用户也成功将自己的手机干山了HarmonyOS.4.0系统。虽然干上去HarmonyOS.4.0系统目前BUG非常多&#xff0c;根本…

数仓主题域和数据域、雪花模型,星型模型和星座模型

数仓模型和领域划分 一、主题域和数据域的差别二、雪花模型&#xff0c;星座模型和星型模型 一、主题域和数据域的差别 明确数据域作为数仓搭建的重要一环&#xff0c;能够让数仓的数据便于管理和应用。 数据域和主题域都是数据仓库中的重要概念&#xff0c;但含义略有不同&am…

【Pinia】Pinia的概念、优势及使用方式

学习公司的项目&#xff0c;发现用到了Pinia&#xff0c;于是上网学习了一下&#xff0c;发现了一篇比较优秀的文章&#xff0c;于是将极少部分放到此记录学习&#xff0c;原文链接在末尾。 是什么 官网解释&#xff1a; Pinia 是 Vue 的存储库&#xff0c;它允许您跨组件/页…

2023年中国场馆产业研究报告

第一章 行业综述 1.1 定义与分类 场馆&#xff0c;作为一个多元化和充满活力的行业&#xff0c;为人们提供了一个为不同目的而聚集的空间。无论是为了活动、表演、展览还是聚会&#xff0c;场馆都在为社区的社会、文化和经济建设做出了不可或缺的贡献。 场馆是一个为举办各类…

VR全景展示的功能有哪些?你了解多少?

VR全景展示作为一种全新的视觉体验技术&#xff0c;能够为人们带来强烈的视觉效果以及沉浸式的观感&#xff0c;在旅游、房地产、车展、博物馆等都有着十分广泛的应用。这种富媒体技术&#xff0c;具有很好的交互性和沉浸感&#xff0c;能够带给大家更好的体验&#xff0c;那么…

uni-app实现web-view图片长按下载

<template><view><web-view :webview-styles"webviewStyles" :src"webUrl"></web-view></view> </template> uniapp的web-view中图片无法长按保存&#xff0c;IOS下是正常的&#xff0c;但是Android下长按无反应 解…

如何统计iOS产品不同渠道的下载量?

一、前言 在开发过程中&#xff0c;Android可能会打出来很多的包&#xff0c;用于标识不同的商店下载量。原来觉得苹果只有一个商店&#xff1a;AppStore&#xff0c;如何做出不同来源的统计呢&#xff1f;本篇文章就是告诉大家如何做不同渠道来源统计。 二、正文 先看一下苹…