荔枝派zero驱动开发06:GPIO操作(platform框架)

参考:
正点原子Linux第五十四章 platform设备驱动实验
一张图掌握 Linux platform 平台设备驱动框架

上一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统)
下一篇:更新中…

概述

platform是一种分层思想,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动;platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。

  • 初学对具体设计的意图无需完全掌握,大致掌握运行流,在内核驱动中可以找到和读懂相关代码即可,用户编写驱动可以参考模板

  • 参考下图,一图流,完全弄懂这张图就能完全掌握platform框架的思想 😃

在这里插入图片描述
图源:一张图掌握 Linux platform 平台设备驱动框架!

设备树修改

设备树直接使用上一章即可,无需修改

简要分析

在上一章源码基础上修改,添加platform相关的数据结构和匹配表,实现led_probe(即原驱动初始化函数),实现led_remove(原退出函数),并将驱动初始化改为platform_driver_register和platform_driver_unregister

//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver		= {.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table	= led_of_match,     // 设备树匹配表},.probe		= led_probe,.remove		= led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}

如上,设备树与led_of_match的属性值匹配后,即执行led_probe函数,同理:卸载驱动时会执行led_remove函数,卸载操作无需改动

函数原型:int (*probe)(struct platform_device *);

led_probe函数参考实现:

static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green");	// 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);...// 注册字符设备...}

这里直接使用pdev->dev.of_node引用设备节点即可

后续注册字符设备、操作函数ops等无改动,不再赘述

测试

在这里插入图片描述

chardevApp同样使用上一章的测试APP,功能正常实现

源码

platform_led.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>struct platform_led_dev
{dev_t devid;struct cdev cdev;struct class *class;struct device *device;int major;int minor;struct device_node *nd;int led_gpio;
};
struct platform_led_dev platform_led = {.major = 0,
};#define PIN_N 0 // 第0个引脚,PG0,绿色
#define DEV_NAME "platform_led"
#define LED_ON 0 // 上拉,低电平亮
#define LED_OFF 1static int led_gpio_open(struct inode *inode, struct file *file)
{file->private_data = &platform_led;return 0;
}static int led_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{return 0;
}static int led_gpio_release(struct inode *inode, struct file *file)
{return 0;
}static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{int ret = 0;unsigned char databuf;struct platform_led_dev *dev = file->private_data;ret = copy_from_user(&databuf, user_buf, sizeof(databuf));if (ret < 0){pr_err("copy_from_user failed\r\n");return -EFAULT;}if (databuf == 0 || databuf == '0') // LED_OFFgpio_set_value(dev->led_gpio, 1);if (databuf == 1 || databuf == '1') // LED_ONgpio_set_value(dev->led_gpio, 0);return 1;
}static const struct file_operations platform_led_fops = {.open = led_gpio_open,.read = led_gpio_read,.release = led_gpio_release,.write = led_gpio_write,
};static int led_probe(struct platform_device *pdev)
{int ret;const char *str;printk("led driver and device was matched!\r\n");// 获取 LED 灯的 GPIO 号// 进probe说明设备树已匹配,直接使用设备树节点即可platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (platform_led.led_gpio < 0){printk("can't get gpios");return -EINVAL;}printk("gpio num = %d\r\n", platform_led.led_gpio);// 向 gpio 子系统申请使用 GPIOret = gpio_request(platform_led.led_gpio, "green");if (ret){printk(KERN_ERR "Failed to request gpio\n");return ret;}// 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯ret = gpio_direction_output(platform_led.led_gpio, 0);if (ret < 0){printk("can't set gpio!\r\n");}// 注册字符设备if (platform_led.major) // 定义了设备号,静态设备号{platform_led.devid = MKDEV(platform_led.major, 0);ret = register_chrdev_region(platform_led.major, 1, DEV_NAME);if (ret < 0){pr_err("cannot register %s char driver.ret:%d\r\n", DEV_NAME, ret);goto exit;}}else // 没有定义设备号,动态申请设备号{ret = alloc_chrdev_region(&platform_led.devid, 0, 1, DEV_NAME);if (ret < 0){pr_err("cannot alloc_chrdev_region,ret:%d\r\n", ret);goto exit;}platform_led.major = MAJOR(platform_led.devid);platform_led.minor = MINOR(platform_led.devid);}printk("led major=%d,minor=%d\r\n", platform_led.major, platform_led.minor);platform_led.cdev.owner = THIS_MODULE;cdev_init(&platform_led.cdev, &platform_led_fops);ret = cdev_add(&platform_led.cdev, platform_led.devid, 1);if (ret < 0)goto del_unregister;platform_led.class = class_create(THIS_MODULE, DEV_NAME);if (IS_ERR(platform_led.class))goto del_cdev;platform_led.device = device_create(platform_led.class, NULL, platform_led.devid, NULL, DEV_NAME);if (IS_ERR(platform_led.device))goto destroy_class;return 0;// 注意  goto后的标签没有return操作,将顺序执行多个label直至return,这里反向写
destroy_class:class_destroy(platform_led.class);
del_cdev:cdev_del(&platform_led.cdev);
del_unregister:unregister_chrdev_region(platform_led.devid, 1);
exit:printk("init failed\r\n");return -EIO;
}static int led_remove(struct platform_device *pdev)
{if (platform_led.led_gpio){gpio_set_value(platform_led.led_gpio, 1);gpio_free(platform_led.led_gpio);}cdev_del(&platform_led.cdev);unregister_chrdev_region(platform_led.devid, 1);device_destroy(platform_led.class, platform_led.devid);class_destroy(platform_led.class);return 0;
}//platform相关
// 匹配列表
static const struct of_device_id led_of_match[] = {{ .compatible = "user,led" },{ /* Sentinel */ }
};MODULE_DEVICE_TABLE(of, led_of_match);/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver		= {.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 .of_match_table	= led_of_match,     // 设备树匹配表},.probe		= led_probe,.remove		= led_remove,
};static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);
}static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("USER");
MODULE_INFO(intree, "Y");

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

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

相关文章

static的用法和作用

从三个方面来讲述static的用法和作用&#xff0c;分别是静态局部变量和静态函数以及静态成员 1. 在一个函数中定义静态局部变量的时候&#xff0c;该静态局部变量的生命周期将贯穿整个程序的运行期&#xff0c;而不会像局部变量一样函数运行结束的时候就被销毁。 #include &…

华为北向网管NCE开发教程(1)闭坑选接口协议

华为北向网管NCE开发教程&#xff08;1&#xff09;闭坑选接口协议 华为北向网管NCE开发教程&#xff08;2&#xff09;REST接口开发 华为北向网管NCE开发教程&#xff08;3&#xff09;CORBA协议开发 华为北向网管NCE开发教程&#xff08;4&#xff09;&#xff08;源代码接口…

网络地址转换协议NAT

网络地址转换协议NAT NAT的定义 NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;是1994年提出的。当在专用网内部的一些主机本来已经分配到了本地IP地址&#xff08;即仅在本专用网内使用的专用地址&#xff09;&#xff0c;但现在又想和因…

如何打造定制化企业内训系统?企培源码开发实战教学

在当今竞争激烈的商业环境中&#xff0c;企业内训系统的建立和定制化已成为提高企业竞争力和员工素质的关键。本文将探讨如何打造定制化企业内训系统&#xff0c;并介绍企培源码开发的实战教学。 第一步&#xff1a;需求分析与规划 建立定制化的企业内训系统之前&#xff0c…

echarts绘制雷达图

<template><div><div>【云端报警风险】</div><div ref"target" class"w-full h-full" stylewidth&#xff1a;200px;height:300px></div></div> </template><script setup> import { ref, onMounte…

【漏洞复现】Salia PLCC cPH2 远程命令执行漏洞(CVE-2023-46359)

0x01 漏洞概述 Salia PLCC cPH2 v1.87.0 及更早版本中存在一个操作系统命令注入漏洞&#xff0c;该漏洞可能允许未经身份验证的远程攻击者通过传递给连接检查功能的特制参数在系统上执行任意命令。 0x02 测绘语句 fofa&#xff1a;"Salia PLCC" 0x03 漏洞复现 ​…

为什么GPU对于人工智能如此重要?

GPU在人工智能中相当于稀土金属&#xff0c;甚至黄金&#xff0c;它们在当今生成式人工智能时代中的作用不可或缺。那么&#xff0c;为什么GPU在人工智能发展中如此重要呢&#xff1f; GPU概述 什么是GPU 图形处理器&#xff08;GPU&#xff09;是一种通常用于进行快速数学计…

学习Java的第六天

目录 一、变量 1、变量的定义 2、变量的声明格式 3、变量的注意事项 4、变量的作用域 二、常量 三、命名规范 Java 语言支持如下运算符&#xff1a; 1、算术运算符 解析图&#xff1a; 示例&#xff1a; 2、赋值运算符 解析图&#xff1a; 示例&#xff1a; 3、关…

Midjourney绘图欣赏系列(十)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

pip 和conda 更换镜像源介绍

1、前言 很多深度学习的项目免不了安装库文件、配置环境等等&#xff0c;如果利用官方提供的连接&#xff0c;网速很慢&#xff0c;而且很容易download掉。 所以配置好了虚拟环境&#xff0c;将pip换源属实重要 常见的国内镜像源有清华、中科大、阿里等等... 这里建议用中科…

使用Amazon Bedrock托管的Claude3 学习中国历史

最近被Amazon Bedrock托管的Claude3 刷屏了&#xff0c;那么先简单介绍下什么是Claude 3。 Claude 3是Anthropic 推出了下一代 Claude模型&#xff0c;针对不同用例进行优化的三种先进模型&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 和 Claude 3 Opus&#xff0c;使用户能够…

Django入门 整体流程跑通

Django学习笔记 一、Django整体流程跑通 1.1安装 pip install django //安装 import django //在python环境中导入django django.get_version() //获取版本号&#xff0c;如果能获取到&#xff0c;说明安装成功Django目录结构 Python310-Scripts\django-admi…

nginx代理参数proxy_pass

proxy_pass参数用于配置反向代理&#xff0c;指定客户端请求被转发到后端服务器&#xff0c;后端地址可以是域名、ip端口URI 代理后端报错提示本地找不到CSS文件、JavaScript文件或图片 例如&#xff1a; nginx &#xff1a;10.1.74.109 后端服务&#xff1a;http://10.1.74.…

华为北向网管NCE开发教程(3)CORBA协议开发

华为北向网管NCE开发教程&#xff08;1&#xff09;闭坑选接口协议 华为北向网管NCE开发教程&#xff08;2&#xff09;REST接口开发 华为北向网管NCE开发教程&#xff08;3&#xff09;CORBA协议开发 华为北向网管NCE开发教程&#xff08;4&#xff09;&#xff08;源代码接口…

嵌入式学习第二十六天!(网络传输:TCP编程、HTTP协议)

TCP通信&#xff1a; 1. TCP发端&#xff1a; socket -> connect -> send -> recv -> close 2. TCP收端&#xff1a; socket -> bind -> listen -> accept -> recv -> send -> close 3. TCP需要用到的函数&#xff1a; 1. co…

【脚本开发】脚本的启动与暂停

文章目录 需求简单实现测试代码 更新&#xff1a;添加两个方法 需求 基于pynput库开发一个脚本。 要能够用按键控制它启动&#xff0c;暂停。 简单实现 key参数&#xff0c;代表了用什么键控制。 state属性&#xff0c;代表了当前的开关状态。 listener属性&#xff0c;是…

3.基础算法之搜索与图论

1.深度优先搜索 深度优先搜索&#xff08;DFS&#xff0c;Depth First Search&#xff09;是一种用于遍历或搜索树或图的算法。它将当前状态按照一定的规则顺序&#xff0c;先拓展一步得到一个新状态&#xff0c;再对这个新状态递归拓展下去。如果无法拓展&#xff0c;则退回…

STM32 通过Modbus协议更改内部Flash(模仿EEPROM)的运行参数

main.c测试 uint8_t uart1RxBuf[64]{0};uint8_t Adc1ConvEnd0; uint8_t Adc2ConvEnd0;int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initial…

【C语言刷题】——初识位操作符

【C语言刷题】——初识位操作符 位操作符介绍题一、 不创建临时变量&#xff08;第三个变量&#xff09;&#xff0c;实现两个数的交换&#xff08;1&#xff09;法一&#xff08;2&#xff09;法二 题二、 求一个数存储在内存中的二进制中“一”的个数&#xff08;1&#xff0…

【算法】Hash存储——开放寻址法

模拟散列表 维护一个集合&#xff0c;支持如下几种操作&#xff1a; I x&#xff0c;插入一个整数 x&#xff1b; Q x&#xff0c;询问整数 x是否在集合中出现过&#xff1b; 现在要进行 N次操作&#xff0c;对于每个询问操作输出对应的结果。 输入格式 第一行包含整数 N&am…