linux驱动开发笔记--GPIO驱动开发

目录

前言

一、设备树配置

二、驱动编写

三、用户空间测试

总结


前言

        开发平台:全志A133,开发环境:linux4.9+andrio10,开发板:HelperBoard A133_V2.5。

一、设备树配置

        打开板级设备树配置文件,路径:

vim ~/share/linux_source/device/config/chips/a133/configs/c4/board.dts

        添加新节点beep对应GPIO:GPIOB8。

beep: beep@0 {compatible = "murongbai,beep";status = "okay";/* PB8: <&pio PB 8 1 0 3 0> (function=output, pull=none, drive=3, data=0) */gpios = <&pio PB 8 1 0 3 0>;label = "beep_gpio";
};

        重新编译linux源码并下载到开发板中

~/share/linux_source/build.sh

二、驱动编写

        采用模块驱动,未写入内核,创建驱动文件及Makefile。Makefile参考如下

#内核架构
ARCH=arm64
#当前工作路径
PWD = $(shell pwd)
#内核路径
KDIR := /home/murongbai/share/linux_source/kernel/linux-4.9    
#编译器路径
CROSS_COMPILE= /home/murongbai/share/linux_source/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
#模块名称
obj-m += beep_init.o.PHONY : all
all:make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules
.PHONY : clean
clean:make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) clean

        编写驱动代码,使用平台驱动结构体完成初始化配置

//设备树匹配表
static const struct of_device_id beep_of_match[] = {{ .compatible = "murongbai,beep", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, beep_of_match);//平台驱动结构体(驱动加载、驱动卸载、设备匹配等)
static struct platform_driver beep_platform_driver = {.probe  = beep_probe,.remove = beep_remove,.driver = {.name = BEEP_NAME,.of_match_table = beep_of_match,},
};

        根据上方设备树节点中定义的gpios属性获取实际的GPIO编号,再根据GPIO编号注册GPIO设备,并将设备配置为输出模式。然后将设备注册为字符设备并在/dev/beep路径生成设备文件,方便用户空间进行访问。

//设备驱动初始化函数
static int beep_probe(struct platform_device *pdev)
{int ret;//获取设备树中定义的GPIO编号beep_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (!gpio_is_valid(beep_gpio)) {dev_err(&pdev->dev, "Invalid beep gpio\n");return -EINVAL;}//请求GPIOret = gpio_request(beep_gpio, "beep_gpio");if (ret) {dev_err(&pdev->dev, "Failed to request GPIO %d\n", beep_gpio);return ret;}//设置GPIO为输出模式gpio_direction_output(beep_gpio, 0);//注册字符设备beep_major = register_chrdev(0, BEEP_NAME, &beep_fops);if (beep_major < 0) {dev_err(&pdev->dev, "Failed to register chrdev\n");gpio_free(beep_gpio);return beep_major;}//创建设备类beep_class = class_create(THIS_MODULE, BEEP_NAME);if (IS_ERR(beep_class)) {unregister_chrdev(beep_major, BEEP_NAME);gpio_free(beep_gpio);return PTR_ERR(beep_class);}//创建设备节点beep_dev = device_create(beep_class, NULL, MKDEV(beep_major, 0), NULL, BEEP_NAME);//检查设备创建是否成功dev_info(&pdev->dev, "Beep driver init, gpio=%d\n", beep_gpio);return 0;
}//设备驱动卸载函数
static int beep_remove(struct platform_device *pdev)
{device_destroy(beep_class, MKDEV(beep_major, 0));class_destroy(beep_class);unregister_chrdev(beep_major, BEEP_NAME);if (gpio_is_valid(beep_gpio)) {gpio_set_value(beep_gpio, 0);gpio_free(beep_gpio);}pr_info("Beep driver exit\n");return 0;
}

        实现字符设备的open,close,ioctl三个接口。

//打开设备
static int beep_open(struct inode *inode, struct file *file)
{return 0;
}//关闭设备
static int beep_release(struct inode *inode, struct file *file)
{return 0;
}//ioctl控制蜂鸣器开关
static long beep_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int beep_value;//用户空间传递的是指针,需使用copy_from_userswitch(cmd){case BEEP_IOWRITE:if(copy_from_user(&beep_value, (int __user *)arg, sizeof(int)))return -EFAULT;gpio_set_value(beep_gpio, beep_value ? 1 : 0);break;default:return -EINVAL;}return 0;
}//兼容32位用户空间
#ifdef CONFIG_COMPAT
static long beep_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{return beep_ioctl(file, cmd, arg);
}
#endifstatic struct file_operations beep_fops = {.owner          = THIS_MODULE,.open           = beep_open,.release        = beep_release,.unlocked_ioctl = beep_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl   = beep_compat_ioctl,
#endif
};

        完整代码如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>#define BEEP_NAME  "beep"#define DEVICE_TYPE 'B'
#define BEEP_IOWRITE _IOW(DEVICE_TYPE, 0, int)//主设备号
static int beep_major = 0;
//设备类
static struct class *beep_class = NULL;
//蜂鸣器GPIO编号
static int beep_gpio = -1;
//设备结构体
static struct device *beep_dev = NULL;//打开设备
static int beep_open(struct inode *inode, struct file *file)
{return 0;
}//关闭设备
static int beep_release(struct inode *inode, struct file *file)
{return 0;
}//ioctl控制蜂鸣器开关
static long beep_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int beep_value;//用户空间传递的是指针,需使用copy_from_userswitch(cmd){case BEEP_IOWRITE:if(copy_from_user(&beep_value, (int __user *)arg, sizeof(int)))return -EFAULT;gpio_set_value(beep_gpio, beep_value ? 1 : 0);break;default:return -EINVAL;}return 0;
}//兼容32位用户空间
#ifdef CONFIG_COMPAT
static long beep_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{return beep_ioctl(file, cmd, arg);
}
#endifstatic struct file_operations beep_fops = {.owner          = THIS_MODULE,.open           = beep_open,.release        = beep_release,.unlocked_ioctl = beep_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl   = beep_compat_ioctl,
#endif
};//设备驱动初始化函数
static int beep_probe(struct platform_device *pdev)
{int ret;//获取设备树中定义的GPIO编号beep_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);if (!gpio_is_valid(beep_gpio)) {dev_err(&pdev->dev, "Invalid beep gpio\n");return -EINVAL;}//请求GPIOret = gpio_request(beep_gpio, "beep_gpio");if (ret) {dev_err(&pdev->dev, "Failed to request GPIO %d\n", beep_gpio);return ret;}//设置GPIO为输出模式gpio_direction_output(beep_gpio, 0);//注册字符设备beep_major = register_chrdev(0, BEEP_NAME, &beep_fops);if (beep_major < 0) {dev_err(&pdev->dev, "Failed to register chrdev\n");gpio_free(beep_gpio);return beep_major;}//创建设备类beep_class = class_create(THIS_MODULE, BEEP_NAME);if (IS_ERR(beep_class)) {unregister_chrdev(beep_major, BEEP_NAME);gpio_free(beep_gpio);return PTR_ERR(beep_class);}//创建设备节点beep_dev = device_create(beep_class, NULL, MKDEV(beep_major, 0), NULL, BEEP_NAME);//检查设备创建是否成功dev_info(&pdev->dev, "Beep driver init, gpio=%d\n", beep_gpio);return 0;
}//设备驱动卸载函数
static int beep_remove(struct platform_device *pdev)
{device_destroy(beep_class, MKDEV(beep_major, 0));class_destroy(beep_class);unregister_chrdev(beep_major, BEEP_NAME);if (gpio_is_valid(beep_gpio)) {gpio_set_value(beep_gpio, 0);gpio_free(beep_gpio);}pr_info("Beep driver exit\n");return 0;
}//设备树匹配表
static const struct of_device_id beep_of_match[] = {{ .compatible = "murongbai,beep", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, beep_of_match);//平台驱动结构体(驱动加载、驱动卸载、设备匹配等)
static struct platform_driver beep_platform_driver = {.probe  = beep_probe,.remove = beep_remove,.driver = {.name = BEEP_NAME,.of_match_table = beep_of_match,},
};static int __init beep_init(void)
{return platform_driver_register(&beep_platform_driver);
}static void __exit beep_exit(void)
{platform_driver_unregister(&beep_platform_driver);
}module_init(beep_init);
module_exit(beep_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("murongbai");
MODULE_DESCRIPTION("Simple Beep Driver for GPIOB8 (DT version)");

        使用make命令编译生成.ko文件

murongbai@murongbai-B760I-Snow-Dream:~/share/my_drivers/beep_driver$ make
make -C /home/murongbai/share/linux_source/kernel/linux-4.9     ARCH=arm64 CROSS_COMPILE=/home/murongbai/share/linux_source/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- M=/home/murongbai/share/my_drivers/beep_driver modules
make[1]: Entering directory '/home/murongbai/share/linux_source/kernel/linux-4.9'CC [M]  /home/murongbai/share/my_drivers/beep_driver/beep_init.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/murongbai/share/my_drivers/beep_driver/beep_init.mod.oLD [M]  /home/murongbai/share/my_drivers/beep_driver/beep_init.ko
make[1]: Leaving directory '/home/murongbai/share/linux_source/kernel/linux-4.9'#### build completed successfully (1 seconds) ####

三、用户空间测试

        测试开启蜂鸣器设备文件,并控制蜂鸣器每隔5s短鸣一次。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>#define DEVICE_TYPE 'B'
#define BEEP_IOWRITE _IOW(DEVICE_TYPE, 0, int)volatile int beep_on_time = 0;
volatile int running = 1;
int fd = -1;void beep_on(void) {beep_on_time = 100; // 设置蜂鸣器响100ms
}//Ctrl+C信号处理
void handle_sigint(int sig) {running = 0;
}int main() {int beep_value;signal(SIGINT, handle_sigint);fd = open("/dev/beep", O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}//无限循环,直到接收到退出信号while(running){// //如果蜂鸣器在运行时间,且蜂鸣器没有打开// if(beep_on_time && beep_value == 0)// {//     beep_value = 1;//     ioctl(fd, BEEP_IOWRITE, &beep_value);//     beep_on_time -= 10;// }// //如果蜂鸣器不在运行时间,但蜂鸣器打开// else if(beep_value == 1)// {//     beep_value = 0;//     ioctl(fd, BEEP_IOWRITE, &beep_value);// }// usleep(10*1000);beep_value = 1;ioctl(fd, BEEP_IOWRITE, &beep_value);usleep(100*1000);beep_value = 0;ioctl(fd, BEEP_IOWRITE, &beep_value);sleep(5);}close(fd);printf("Exit and release beep device\n");return 0;
}

        通过这条命令编译生成beep_demo.o文件。

armv7a-linux-androideabi21-clang beep_demo.c -o beep_demo.o

        将驱动文件和测试文件都推送到开发板上测试

adb push .\beep\beep_demo.o /data/local/tmp
adb push .\beep_driver\beep_init.ko /data/local/tmp

        加载驱动并查看内核输出

insmod /data/local/tmp/beep_init.ko
dmesg

        看到此输出语句表示驱动加载成功

        最后修改beep_demo.o文件的权限,并运行。即可观察到蜂鸣器每隔5s短鸣一次

chmod 777 /data/local/tmp/beep_demo.o
/data/local/tmp/beep_demo.o

总结

        单片机开发中,写一个GPIO拉高的函数,只需要一条语句即可完成,找到GPIO状态寄存器,然后将对应GPIO设为1即可。但是在linux驱动开发中就需要配置设备树,注册GPIO设备,注册字符设备生成设备文件,完成三个基本控制接口,完成驱动加载卸载函数,再编写一个用户空间的测试代码才能实现。工作量实在是太多了,好处是分工很明确。驱动代码编写交给驱动开发工程师,用户代码编写交给应用工程师,还可以再封装一层交给安卓开发工程师进行APP开发。

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

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

相关文章

腾讯iOA:企业软件合规与安全的免费守护者

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 目录 一、为什么要使用腾讯iOA&#xff1f; 二、中小企业软件合规痛点 三、腾讯iOA解决方案 3.1 核心技…

C#定时任务实战指南:从基础Timer到Hangfire高级应用

高效管理后台作业&#xff0c;让定时任务成为应用的可靠引擎 在C#应用开发中&#xff0c;定时任务是实现数据同步、报表生成、系统维护等后台作业的核心技术。本文将深入探讨C#生态中主流的定时任务解决方案&#xff0c;从基础的内置Timer到强大的Quartz.NET和Hangfire框架&…

软件开发、项目开发基本步骤

• 立项阶段&#xff1a;项目定义、需求收集与分析、可行性分析、风险评估与规划、项目团队组建、制定项目计划、获取批准与支持。• 需求评审与分析&#xff1a;◦ 项目团队&#xff08;包括产品经理、开发人员、测试人员等&#xff09;共同参与&#xff0c;明确项目的目标、功…

慢 SQL接口性能优化实战

在对某电商项目进行接口性能压测时&#xff0c;发现 /product/search 接口响应缓慢&#xff0c;存在明显性能瓶颈。通过慢查询日志排查和 SQL 优化&#xff0c;最终实现了接口响应速度的显著提升。本文完整还原此次优化过程&#xff0c;特别强调操作步骤和问题分析过程&#xf…

【C#】在WinForms中实现控件跨TabPage共享的优雅方案

文章目录一、问题背景二、基本实现方案1. 通过修改Parent属性实现控件移动三、进阶优化方案1. 创建控件共享管理类2. 使用用户控件封装共享内容四、方案对比与选择建议五、最佳实践建议六、完整示例代码一、问题背景 在Windows窗体应用程序开发中&#xff0c;我们经常遇到需要…

Android Camera openCamera

由头 今日调休&#xff0c;终于终于闲下来了&#xff0c;可以写一下博客了&#xff0c;刚好打开自己电脑&#xff0c;就有四年前下的谷歌Android 12源码&#xff0c;不是很旧&#xff0c;刚好够用&#xff0c;不用再另外下载新源码了&#xff0c;不得不感慨这时间过得真快啊~废…

神经网络——池化层

目录 池化层 最大池化层 MaxPool2d 最大池化操作图示 最大池化操作代码演示 综合代码案例 池化层 池化层&#xff08;Pooling Layer&#xff09; 核心作用&#xff1a;通过降采样减少特征图尺寸&#xff0c;降低计算量&#xff0c;增强特征鲁棒性。 1. 常见类型 …

Android 默认图库播放视频没有自动循环功能,如何添加2

Android 默认图库播放视频没有自动循环功能, 如何添加 按如下方式修改可以添加 开发云 - 一站式云服务平台 --- a/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java +++ b/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java…

数字孪生赋能智慧能源电力传输管理新模式

在“双碳”战略和能源数字化转型的双重驱动下&#xff0c;智慧能源系统亟需更高效、精细和智能的管理手段。数字孪生技术作为融合物理世界与数字空间的桥梁&#xff0c;为电力传输系统的全生命周期管理提供了强有力的技术支撑。本文聚焦数字孪生在智慧能源电力传输中的应用&…

Jmeter的元件使用介绍:(二)线程组详解

Jmeter线程组默认包含三种&#xff1a;线程组、setUp线程组、tearDown线程组。线程组之间的执行顺序为&#xff1a;setUp线程组->线程组->tearDown线程组。多数情况都是选用线程组&#xff0c;setUp线程组用于做一些脚本的前置准备&#xff0c;比如&#xff1a;跨线程组设…

AI替代人工:浪潮中的沉浮与觉醒

当AlphaGo以4:1的比分战胜围棋大师李世石之时&#xff0c;人机博弈的疆界被重新划定&#xff1b;当工厂车间里机械臂以惊人精度与不知疲倦的姿态取代了工人重复的手势&#xff1b;当客服电话那头响起的不再是温存人声&#xff0c;而成了准确但缺乏温度的AI语音&#xff1b;当算…

数学建模--matplot.pyplot(结尾附线条样式表格)

matplotlib.pyplot绘图接口 1. 用法 导入模块 import matplotlib.pyplot as plt import numpy as np # 用于生成示例数据绘制简单图表 # 生成数据 x np.linspace(0, 10, 100) y np.sin(x)# 创建图形和坐标轴 plt.figure(figsize(8, 4)) # 设置图表大小 plt.plot(x, y, …

NumPy 实现三维旋转变换

在三维空间中,物体的旋转变换是计算机图形学、机器人学以及三维建模等领域中一个至关重要的操作。这种变换可以通过构造特定的旋转矩阵并将其应用于三维点或向量来实现。本文将深入探讨如何利用 NumPy 这一强大的 Python 科学计算库来实现三维旋转变换,从基本的数学原理到具体…

基于Springboot的中药商城管理系统/基于javaweb的中药材销售系统

管理员&#xff1a;登录&#xff0c;个人中心&#xff0c;用户管理&#xff0c;药材分类管理&#xff0c;药材信息管理&#xff0c;药材入库管理&#xff0c; 药材出库管理&#xff0c;订单管理&#xff0c;云端药馆&#xff0c;系统设置用户&#xff1a;注册&#xff0c;登录&…

试用SAP BTP 02A:试用SAP HANA Cloud

进入SAP BTP主控室 -> 子账 -> 服务市场&#xff0c;选择【数据和分析】-> 点击SAP HANA Cloud点击创建选择服务、计划、运行时环境、空间&#xff0c;输入实例名称&#xff0c;点击下一步在JSON文件中配置HANA管理员密码&#xff0c;点击下一步审核hana 实例信息&…

纯CPU场景下C++的分布式模型训练框架设计思路

0. 参数分配 稠密参数 → MPI 集合通信&#xff08;All-Reduce / Broadcast / Reduce-Scatter&#xff09;。稀疏参数 → brpc Parameter Server 异步推拉。 完全去掉 NCCL/GPU 相关部分。1. 整体拓扑 ┌----------------┐ ┌----------------┐ │ Worker-0 │…

训练日志7.21

conda环境&#xff0c;服务器原因无法使用&#xff0c;需重新搭建 学习一下预训练和微调相关内容&#xff0c;对于预训练整体的流程&#xff0c;还不太清楚&#xff0c;自己估计是训练不动&#xff0c;只能微调

Java 高频算法

Java高频算法面试题 以下是Java面试中常见的高频算法题目&#xff0c;涵盖了数据结构、算法思想和实际应用场景。 一、数组与字符串 1. 两数之和 public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map new HashMap<>();for (int i 0; i <…

汽车控制系统——CAPL脚本

CAPL (Communication Access Programming Language) 是一种专门用于嵌入式系统和汽车电子测试领域的编程语言&#xff0c;特别是在 CAN (Controller Area Network) 总线和汽车网络通信系统中被广泛使用。它由 Vector 公司开发&#xff0c;主要用于编写与汽车控制单元 (ECU) 进行…

深入解析Hive SQL转MapReduce的编译原理:从AST抽象语法树到Operator执行树

Hadoop与Hive SQL简介Hadoop生态系统的核心架构作为大数据处理领域的基石&#xff0c;Hadoop生态系统采用分布式架构设计&#xff0c;其核心组件构成了一套完整的解决方案框架。HDFS&#xff08;Hadoop Distributed File System&#xff09;作为底层存储系统&#xff0c;采用主…