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

news2025/1/12 8:41:21

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

 

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

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

相关文章

渗透测试思路总结

一、说明 《Metasploit 渗透测试魔鬼训练营》等书已经对渗透测试的步骤流程划分得比较合理透彻了&#xff0c;但感觉在多次通读该类书藉之后仍总感觉不得要领----要对一台给定的主机进行渗透还是不懂到底该如何着手。想来主要是存在以下两个问题。 第一个是在渗透操作系统时&…

Yolov8优化:引入Soft-NMS,提升密集遮挡场景检测精度

1.Soft-NMS介绍 论文地址:https://arxiv.org/pdf/1704.04503.pdf NMS需要优化的参数: IoU 的阈值是一个可优化的参数,一般范围为0~0.5,可以使用交叉验证来选择最优的参数。 R-CNN会从一张图片中找出n个可能是物体的矩形框,然后为每个矩形框为做类别分类概率: 就…

maven测试依赖的排除

1、概念 当 A 依赖 B&#xff0c;B 依赖 C 而且 C 可以传递到 A 的时候&#xff0c;A 不想要 C&#xff0c;需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。 所以配置依赖的排除其实就是阻止某些 jar 包的传递。因为这样的 jar 包传递过来会和其他 …

parallelStream与CompletableFuture

1 了解parallelStream parallelStream怎么实现的并行处理呢&#xff1f; 其底层是Fork/Join并行计算框架的默认线程池&#xff0c;默认线程池的数量就是处理器的数量&#xff0c;可以使用系统属性&#xff1a;-Djava.util.concurrent.ForkJoinPool.common.parallelism{N} 调整…

【数据库】MySQL 高级(进阶) SQL 语句

文章目录 前提条件一、常用查询1. SELECT&#xff08;显示查询&#xff09;2. DISTINCT&#xff08;不重复查询&#xff09;3. WHERE&#xff08;有条件查询&#xff09;4. AND/OR&#xff08;且/或&#xff09;5. IN &#xff08;显示已知值的字段&#xff09;6. BETWEEN&…

自驾出游擅自使用对讲机属于违法行为?

周末或节假日大多数人通常都会选择自驾出游&#xff0c;或是叫上自己的好友一起出游&#xff0c;这个时候就可以组成一个车队。为了沟通起来更方便&#xff0c;大家一般都喜欢配个对讲机。 不过据调查显示&#xff0c;大多数人并不认为擅自使用对讲机算违法行为。在多个电商平…

【计算机视觉】OFA:通过一个简单的seq2seq的学习框架来统一架构、任务和模态

文章目录 一、导读二、摘要三、介绍四、OFA4.1 I/O & Architecture4.1.1 I/O4.1.2 Architecture 4.2 Tasks & Modalities4.3 预训练数据集4.4 训练与推理4.5 缩放模型 五、实验结果5.1 跨模态任务的结果5.2 单模态任务的结果5.3 zero-shot学习和任务迁移 六、测试结果七…

测试不为人知的小秘密,你占了几个?

1、线上出现小bug&#xff0c;小本本记下&#xff0c;后面偷偷给开发提个bug。 2、操作服务器时&#xff0c;把数据库玩坏了&#xff0c;系统玩崩了&#xff0c;加班熬夜默默的抢救修复。 3、和开发吵了一架&#xff0c;然后重点照顾了他负责的模块&#xff0c;找了一堆bug&a…

智慧无线灌溉在园林中的应用

智慧无线灌溉技术是解决人们生活用水与园林灌溉之间用水矛盾的有效措施之一&#xff0c;对提升园林灌溉效率和节约水资源有着重要的意义。 智慧无线灌溉系统可以自动感知园林植物周围的环境温度、水分等要素&#xff0c;并对感知到的要素进行详细分析和判断&#xff0c;以确定…

小白到运维工程师自学之路 第四十一集 (shell脚本的基本使用)

一、概述 Shell是一种命令行解释器&#xff0c;它是一种编程语言&#xff0c;用于在操作系统上执行命令和脚本。Shell语言是一种脚本语言&#xff0c;它可以用于自动化任务、批处理、系统管理和编写简单的程序。Shell语言通常用于Unix和Linux操作系统中&#xff0c;但也可以在其…

MySQL优化--索引创建原则,索引什么时候会失效

目录 索引创建原则 面试回答 索引什么时候会失效 面试回答 索引创建原则 1). 针对于数据量较大&#xff0c;且查询比较频繁的表建立索引。 2). 针对于常作为查询条件&#xff08;where&#xff09;、排序&#xff08;order by&#xff09;、分组&#xff08;group by&…

链路追踪SkyWalking整合项目以及数据持久化

1. 微服务整合SkyWalking 1.1 通过jar包方式整合 首先我们将一个简单的springboot服务打成jar包。 将其上传到Linux服务器中。 准备一个启动脚本&#xff0c;脚本内容如下&#xff1a; #!/bin/sh # SkyWalking Agent配置 export SW_AGENT_NAMEskywalking‐test #Agent名字,一…

QVariant类api及相关用法

目录 1、QVariant对象概述 2、QVariant对象的使用 3.使用QVariant对QT中标准数据类型进行处理 4.使用QVariant对QT中自定义数据类型进行处理 setValue fromValue canConvert 1、QVariant对象概述 在Qt中&#xff0c;QVariant是一个通用的值容器&#xff0c;它可…

【kubernetes】Docker与获取转证书工具安装、kubernetes软件安装

前言:二进制部署kubernetes集群在企业应用中扮演着非常重要的角色。无论是集群升级,还是证书设置有效期都非常方便,也是从事云原生相关工作从入门到精通不得不迈过的坎。通过本系列文章,你将从虚拟机准备开始,到使用二进制方式从零到一搭建起安全稳定的高可用kubernetes集…

没有“中间商赚差价”, OpenVINO™ 直接支持 PyTorch 模型对象

点击蓝字 关注我们,让开发变得更有趣 作者 | 杨亦诚 排版 | 李擎 没有“中间商赚差价”&#xff0c; OpenVINO™ 直接支持 PyTorch 模型对象 背景 作为最热门的开源深度学习框架之一&#xff0c;PyTorch 的易用性和灵活性使其深受学术和研究界的喜爱。之前 OpenVINO™ 对于 Py…

人脸检测——基于机器学习2】Haar特性

1.什么是Haar特征 Haar特征是一种反映人脸灰度差异的矩形特征,因其与Haar小波类似而得名。 2.什么是Haar小波 Haar小波: 定义 其波形图为 由图2.1可以看出,Haar小波可以看作二值 分类问题,如同Haar矩阵特征非黑即白,故Haar矩阵特征又称为类Haar特征。 3.Haar特征 H…

React项目引入Arco Design,以及Arco Design Pro 架构

创建项目 创建 react-arco 项目 pnpm create vite my-vue-app --template react安装 arco-design/web-react 安装 react 版的 arco-design 基础使用 添加一个按钮&#xff0c;App.tsx import "./App.css"; import { Button } from "arco-design/web-react…

基本类型与包装类型区别

知识点概括&#xff1a; 简介&#xff1a; Java有八种基本类型&#xff0c;byte&#xff0c; short&#xff0c; int&#xff0c; long&#xff0c; float&#xff0c; double&#xff0c; char&#xff0c; boolean。 对应八种包装类&#xff0c;Byte&#xff0c; Short&…

【Python架构】在 Python 中使用架构模式管理复杂性

你的源代码是不是感觉像一个大泥球&#xff1f;依赖项是否在您的代码库中交织在一起&#xff0c;以至于改变感觉很危险或不可能&#xff1f; 随着业务的增长和领域模型&#xff08;您在应用程序中解决的业务问题&#xff09;变得更加复杂&#xff0c;我们如何在不从头开始重新编…

Android初体验

文章目录 前言一、安卓架构1.1 安卓架构1.2 信息安全1.3 兼容性测试 二、使用WSL编译安卓低版本&#xff08;10&#xff09;总结 前言 Android 是一个适用于移动设备的开源操作系统&#xff0c;也是由 Google 主导的对应开源项目。 最初&#xff0c;Android只在移动设备中流行…