通过GPIO子系统编写LED驱动,应用程序控制LED灯亮灭

1、在内核设备树中添加设备信息:

LED1的设备树编写需要参考内核的帮助文档:
linux-5.10.61/Documentation/devicetree/bindings/gpio

在根节点内部添加led灯设备树节点

:~/linux-5.10.61/arch/arm/boot/dts $ vi stm32mp157a-fsmp1a.dts

 

myled.c

#include "mydev.h"
int gpiono, gpiono1, gpiono2;
struct device_node *dnode;
struct timer_list mytimer;
struct class *cls;
struct device *dev;
// 定义cdev的结构体指针变量
struct cdev *cdev;
unsigned int major = 0;
unsigned int minor = 0;
int ret, i;
char kbuf[128] = {0};
// 定时器时间到达的处理函数
void timer_led_function(struct timer_list *timer)
{
    gpio_set_value(gpiono, !gpio_get_value(gpiono)); // 让灯取反
    mod_timer(timer, jiffies + 5 * HZ);              // 再次启动定时器
}
/****************************************************************/
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
/**************************************************************/
ssize_t mycdev_read(struct file *file, char __user *ubuf,
                    size_t size, loff_t *offs)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy data to user error\n");
        return -EIO;
    }
    return size;
}
/**************************************************************/
ssize_t mycdev_write(struct file *file,
                     const char __user *ubuf, size_t size, loff_t *off)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy data from user error\n");
        return -EIO;
    }
    switch (kbuf[0])
    {
    case '1': // led1灯亮
              // 开灯
        gpio_set_value(gpiono, 1);
        break;
    case '2': // led2灯亮
        gpio_set_value(gpiono1, 1);
        break;
    case '3': // led3灯亮
        gpio_set_value(gpiono2, 1);
        break;
    case '4': // led1灯灭
        gpio_set_value(gpiono, 0);
        break;
    case '5': // led2灯灭
        gpio_set_value(gpiono1, 0);
        break;
    case '6': // led3灯灭
        gpio_set_value(gpiono2, 0);
        break;
    }
    return size;
}
/**************************************************************/
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
/**************************************************************/
const struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};
/**************************************************************/
static int __init mycdev_init(void)
{
    dev_t devno;
    // 1.分配对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev alloc memory error\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    printk("分配对象成功\n");
    // 2.对象的初始化
    cdev_init(cdev, &fops);
    // 3.申请设备号
    ret = alloc_chrdev_region(&devno, minor, 1, "myled");
    if (ret)
    {
        printk("dynamic:alloc device number error\n");
        goto ERR2;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);
    printk("动态申请设备号成功\n");
    // 4.字符设备驱动的注册
    ret = cdev_add(cdev, MKDEV(major, minor), 1);
    if (ret)
    {
        printk("cdev register error\n");
        goto ERR3;
    }

    printk("字符驱动注册成功\n");
    // 5.自动创建设备节点
    cls = class_create(THIS_MODULE, "myled"); // 向上提交设备目录
    if (IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    printk("向上提交设备目录成功\n");
    dev = device_create(cls, NULL, MKDEV(major, minor), NULL, "myled"); // 向上提交设备节点
    if (IS_ERR(dev))
    {
        printk("device create error\n");
        ret = PTR_ERR(dev);
        goto ERR5;
    }
    printk("向上提交设备节点成功\n");
    printk("字符设备驱动的注册成功\n");
    // 1、分配一个定时器对象
    // 2、对象初始化
    mytimer.expires = jiffies + HZ; // 定时器设置时间为1s
    timer_setup(&mytimer, timer_led_function, 0);

    // 3、启动定时器
    add_timer(&mytimer);
    // 解析设备树,获得设备节点
    dnode = of_find_node_by_name(NULL, "myleds");
    if (dnode == NULL)
    {
        printk("解析设备树失败\n");
        return -1;
    }
    printk("解析设备树成功\n");
    /**************************led1************************************/
    // 根据gpio的节点解析到gpio的编号
    gpiono = of_get_named_gpio(dnode, "led1", 0);
    if (gpiono < 0)
    {
        printk("解析到gpio的编号失败\n");
        return -1;
    }
    printk("解析到gpio的编号成功\n");
    // 申请gpio编号
    if (gpio_request(gpiono, NULL))
    {
        printk("申请gpio编号失败\n");
        return -1;
    }
    printk("申请gpio编号成功\n");
    // 设置管脚为输出
    gpio_direction_output(gpiono, 0);
    /****************************led2**********************************/
    // 根据gpio的节点解析到gpio的编号
    gpiono1 = of_get_named_gpio(dnode, "led2", 0);
    if (gpiono1 < 0)
    {
        printk("解析到gpio_led2的编号失败\n");
        return -1;
    }
    printk("解析到gpio_led2的编号成功\n");
    // 申请gpio编号
    if (gpio_request(gpiono1, NULL))
    {
        printk("申请gpio_led2编号失败\n");
        return -1;
    }
    printk("申请gpio_led2编号成功\n");
    // 设置管脚为输出
    gpio_direction_output(gpiono1, 0);
    /*****************************led3*********************************/
    // 根据gpio的节点解析到gpio的编号
    gpiono2 = of_get_named_gpio(dnode, "led3", 0);
    if (gpiono2 < 0)
    {
        printk("解析到gpio_led3的编号失败\n");
        return -1;
    }
    printk("解析到gpio_led3的编号成功\n");
    // 申请gpio编号
    if (gpio_request(gpiono2, NULL))
    {
        printk("申请gpio_led3编号失败\n");
        return -1;
    }
    printk("申请gpio_led3编号成功\n");
    // 设置管脚为输出
    gpio_direction_output(gpiono2, 0);
    return 0;
    /******************goto执行跳转语句*****************************/
ERR5:
    device_destroy(cls, MKDEV(major, minor));
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 1);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    // 1.销毁设备节点
    device_destroy(cls, MKDEV(major, minor));
    class_destroy(cls);
    // 2.销毁字符设备驱动
    cdev_del(cdev);
    // 3.销毁设备号
    unregister_chrdev_region(MKDEV(major, minor), 1);
    // 4.释放动态申请的cdev内存
    kfree(cdev);
    // 删除定时器
    del_timer(&mytimer);
    // 灭灯
    gpio_set_value(gpiono, 0);
    gpio_set_value(gpiono1, 0);
    gpio_set_value(gpiono2, 0);
    // 释放gpio编号
    gpio_free(gpiono);
    gpio_free(gpiono1);
    gpio_free(gpiono2);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

test.c

#include "c.h"
int count = 5;
int main(int argc, const char *argv[])
{
    char buf[128] = {0};
    int fd2, ret, i;
    fd2 = open("/dev/myled", O_RDWR);
    if (fd2 < 0)
    {
        printf("打开mycdev设备失败\n");
        exit(-1);
    }
    while (1)
    {
        memset(buf,0,sizeof(buf));//清空buf
        printf("1(1亮灯)2(2亮灯)3(3亮灯)4(1亮灭)5(2亮灭)6(3亮灭)\n");
        printf("请输入控制码:>>");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        write(fd2,buf,sizeof(buf));
    }

    close(fd2);
    return 0;
}

 

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

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

相关文章

选择排序 - C语言实现

目录 &#x1f970;前言 ✅选择排序 &#x1f95d;基本思想 &#x1f95d;实现逻辑 &#x1f95d;动图演示 复杂度分析 &#x1f60d;代码实现 &#x1f6a9;优化改进-->二元选择排序 &#x1f60d; 改进代码 前言 &#x1f970;在学数据结构的第一节课就知道了数据结…

React 通过一个输入内容加入列表案例熟悉 Hook 基本使用

我们创建一个react项目 在src下创建components文件夹 在下面创建一个index.jsx index.jsx 参考代码如下 import React, { useState } from "react";const useInputValue (initialValue) > {const [value,setValue] useState(initialValue);return {value,onCha…

19-递归的理解、场景

一、递归 &#x1f32d;&#x1f32d;&#x1f32d;在函数内部&#xff0c;可以调用其他函数。如果一个函数在内部调用自身本身&#xff0c;这个函数就是递归函数 核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解 一般来说&#xff0c;递归…

算法刷题-字符串-左旋转字符串

反转个字符串还有这么多用处&#xff1f; 题目&#xff1a;剑指Offer58-II.左旋转字符串 力扣题目链接 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如&#xff0c;输入字符串"abcdefg"和数字2…

generator和promise和async的异同

一、generator(生成器)是ES6标准引入的新数据类型,他和promise一样都是异步事件的解决方案 //generator函数生成斐波那契// generator(生成器)是ES6标准引入的新数据类型,async就是 Generator 函数的语法糖//本质&#xff1a;用来处理异步事件的对象/包含异步操作的容器functio…

校园外卖平台怎么做

校园外卖小程序是一款基于智能手机的移动应用&#xff0c;提供订餐、支付、配送等服务。它能为顾客提供丰富的美食选择&#xff0c;为商家提供进一步发展业务的机会&#xff0c;同时骑手也有机会赚取额外的收入。 一、 用户端功能介绍 1. 地图定位&#xff1a;用户可以利用小…

网络安全学术顶会——CCS '22 议题清单、摘要与总结(中)

注意&#xff1a;本文由GPT4与Claude联合生成。 81、HammerScope: Observing DRAM Power Consumption Using Rowhammer 内存单元尺寸的不断缩小使得内存密度提高&#xff0c;功耗降低&#xff0c;但同时也影响了其可靠性。Rowhammer攻击利用这种降低的可靠性在内存中引发比特翻…

计算机网络基础学习指南

前言 计算机网络基础是研发/运维工程师都需掌握的知识&#xff0c;但往往会被忽略。 今天&#xff0c;我将对计算机网络基础学习进行详细阐述&#xff0c;涵盖 TCP / UDP协议、Http协议、Socket等&#xff0c;希望你们会喜欢。 1、计算机网络体系结构 1.1 简介 定义 计算机…

数据库的事务处理

文章目录 前言一、事务的概念二、事务的特性三、隔离级别四、并发控制五、总结 前言 在现代信息化时代&#xff0c;大量的数据不断地被创建、修改、删除和查询。 为了保证数据的准确性和一致性&#xff0c;数据库的事务处理成为了必不可少的一个重要组成部分。 本文将针对数据…

nginx学习使用

一、Nginx是什么&#xff1f; Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;第…

线性代数:线性方程求解、矩阵的逆、线性组合、线性独立

本文参考www.deeplearningbook.org一书第二章2.3 Identity and Inverse Matrices 2.4 Linear Dependence and Span 本文围绕线性方程求解依次介绍矩阵的逆、线性组合、线性独立等线性代数的基础知识点。 一、线性方程 本文主要围绕求解线性方程展开&#xff0c;我们先把线性…

软件工程——第2章可行性研究知识点整理

本专栏是博主个人笔记&#xff0c;主要目的是利用碎片化的时间来记忆软工知识点&#xff0c;特此声明&#xff01; 文章目录 1.可行性研究的目的&#xff1f; 2.可行性研究的实质&#xff1f; 3.从哪些方面研究逻辑模型的解法可行性&#xff1f; 4.可行性研究最根本的任务是…

【MySQL】数据库基础 ②

✍LIKE 子句 说明&#xff1a; 使用 SELECT 来查询数据&#xff0c; 同时我们可以在 SELECT 语句中使用 WHERE 子句来获取指定的记录。 WHERE 子句中可以使用等号 来设定获取数据的条件&#xff0c;如 "字段(text_title) 值()"。 但是有时候我们需要获取 text_…

Dump寄存器使用、解析

前人种树&#xff0c;后人乘凉&#xff1b;创造不易&#xff0c;请勿迁移~ author daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主daisy.skye擅长嵌入式,Qt,Linux,等方面的知识https://blog.csdn.net/qq_40715266?t…

Ubuntu18.04离线安装Nginx

因需要安装nginx的服务器无法连接互联网&#xff0c;所以需要离线安装。首先需要下载nginx的安装包&#xff0c;之后进行安装&#xff0c;在安装之前需要保证gcc&#xff0c;g&#xff0c;make等依赖包已经安装。 因为是需要离线安装&#xff0c;所以在之前是用的一台互联网下载…

selenium 要点击的元素被其他元素遮挡 or 无法找到非可视范围内的元素

selenium 无法找到非可视范围内的元素 org.openqa.selenium.StaleElementReferenceException: The element reference of is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed se…

Java实训日志06

文章目录 八、项目开发实现步骤&#xff08;八&#xff09;创建服务接口1、创建学校服务接口2、创建状态服务接口3、创建学生服务接口4、创建用户服务接口 &#xff08;九&#xff09;创建服务接口实现类1、创建学校服务接口实现类2、创建状态服务接口实现类3、创建学生服务接口…

【C++】4.工具:读取ini配置信息

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍读取ini配置信息。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&…

百度沈抖:大模型 产业智能化时代的新引擎

6月9日&#xff0c;2023 NAVIGATE领航者峰会在杭州举办&#xff0c;聚焦数字经济新政策、新技术、新业态带来的蓬勃机遇&#xff0c;探讨ICT行业在AIGC时代将要面临的全新挑战与应对策略。百度集团执行副总裁、百度智能云事业群总裁沈抖出席大会并作题为《大模型 产业智能化时代…

Elastic 8.8 版引入了全新的 Learned Sparse Encoder 模型,并宣布正式推出合成监测

作者&#xff1a;Brian Bergholm 2023年5月25日 今天&#xff0c;我们非常高兴地宣布 Elastic 8.8 版正式发布。 新增功能 Elastic 企业搜索可帮助开发人员利用 Elasticsearch 实现强大的现代搜索和发现体验。 请在 “Elastic 企业搜索亮点” 博文或 8.8 版发行说明中&#…