泰山派GPIO子系统驱动---亮灯
本人
linux
驱动小白,文章基于B站up主 李Sir______ 视频内容记录,做笔记用。如有错误欢迎指正。本文将以开发板第40
引脚GPIO3_B4
作为LED
灯珠的控制引脚,高电平灯亮,低电平灯灭。
杂话
在linux
内核中,芯片厂商已经把所有控制器的设备树编写好了。硬件层与子系统的API
也都适配好无需使用者关心。我们编写驱动只需要将自己所需要的硬件资源与厂商定义好的设备树匹配,在驱动程序中调用相应子系统API
函数即可写出一份自己的GPIO
驱动程序。相比传统无官方库支持的单片机需操作寄存器来操作底层,程序可移植性大大提高。
内核子系统三层概念
user 逻辑代码—————————————————————————————————————————————设备驱动层(今天的驱动在这一层) 通过各个子系统 完成对硬件的操作 并向上层提供接口 驱动工程师______________________________________________________________________________
kernel核心层(内核维护者) 屏蔽底层细节 向上层提供接口 各类子系统api 内核工程师———————————————————————————————————————厂商驱动层(瑞芯微) 各个厂商针对不同硬件的操作 包括内存映射 厂商—————————————————————————————————————————————
HARDWAREmotor led lcd ...
厂商的设备树文件
厂商已经把所有的控制器设备树信息都提供了,下面是与gpio
相关的一些文件所在位置。
path:kenel\arch\arm64\boot\dts\rockchip\rk3568.dtsi
...
gpio3: gpio@fe760000 {//节点名compatible = "rockchip,gpio-bank"; //厂商名 设备名reg = <0x0 0xfe760000 0x0 0x100>; //地址 0x0 0xfe760000控制器地址 0x0 0x100地址范围interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;//中断clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;//时钟gpio-controller;//标识#gpio-cells = <2>;//描述子节点个数gpio-ranges = <&pinctrl 0 96 32>; //引脚范围 &pinctrl 引用pinctrl节点 0 96 GPIO起始序号 32引脚范围interrupt-controller;#interrupt-cells = <2>;//描述子节点个数
};
...
//一些宏厂商也已经做出定义
path:kernel\include\dt-binding\pinctrl\rockchip.h
path:kernel\include\dt-binding\pinctrl\pinctrl-amd.h
path:kernel\include\dt-binding\gpio\gpio.h
gpio设备树书写规则
[names]-gpios=<控制器地址 引脚编号 高/低电平>;
设备树中添加我们的设备
仿照设备树中的节点,添加一个我们自己的节点,位置在根节点处
path:kenel\arch\arm64\boot\dts\tspi-rk3566-user-v10-linux.dts
..............
/ {model = "lckfb tspi V10 Board";compatible = "lckfb,tspi-v10", "rockchip,rk3566";//自己新建的led灯的设备树 image_led{compatible = "image,led";status = "okay";led_gpio=<&gpio3 RK_PB4 GPIO_ACTIVE_LOW>;}; rk_headset: rk-headset {compatible = "rockchip_headset";headset_gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;pinctrl-names = ............
编译内核,将boot.img文件烧录到开发板
./build.sh kernel
查看添加的设备树节点是否存在
root@RK356X:/tmp# ls /proc/device-tree...........iep@fdef0000 serial@fe6c0000image_led serial@fe6d0000interrupt-controller@fd400000 sfc@fe300000..........
GPIO相关的API
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)
功能:获取gpio编号
参数:np 结构体指针propname 属性名index 编号
返回值:成功 返回gpio编号失败 返回错误码static inline int gpio_request(unsigned gpio, const char *label)
功能:申请gpio
参数:gpio GPIO编号label 标签 NULL
返回值:成功 返回0失败 返回错误码
static inline int gpio_direction_input(unsigned gpio)
功能:设置gpio为输入
参数:gpio GPIO编号
返回值:成功 返回0失败 返回错误码static inline int gpio_direction_output(unsigned gpio, int value)
功能:设置gpio为输出
参数:gpio GPIO编号value 输出电平 1高电平 0是低电平
返回值:成功 返回0失败 返回错误码static inline void gpio_set_value(unsigned int gpio, int value)
功能:设置gpio输出电平
参数:gpio GPIO编号value 输出电平 1高电平 0是低电平
返回值:成功 返回0失败 返回错误码static inline int gpio_get_value(unsigned int gpio)
功能:获取gpio电平状态
参数:gpio GPIO编号
返回值:1 是高电平0 是低电平static inline void gpio_free(unsigned gpio)
功能:释放gpio
参数:gpio GPIO编号
返回值:无
解析设备树相关API
device_node 结构体讲解
struct device_node {const char *name; //节点名#const char *type; //节点类型#phandle phandle; //唯一标识const char *full_name; //节点全名#struct fwnode_handle fwnode; //用于支持不同设备struct property *properties; //设备数属性键值对的链表#struct property *deadprops; /* removed properties */ 链表头struct device_node *parent; //父节点struct device_node *child; //子节点struct device_node *sibling; //兄弟节点
#if defined(CONFIG_OF_KOBJ)#struct kobject kobj;//内核对象
#endif#unsigned long _flags;//状态标志#void *data;//节点相关的私有数据
#if defined(CONFIG_SPARC)#const char *path_component_name;//表示设备节点路径的一部分#unsigned int unique_id;//唯一ID#struct of_irq_controller *irq_trans;//中断控制器结构体指针
#endif
};property结构体
struct property {char *name;//键的名字int length; //值长度void *value; //值 注意 强转struct property *next; //下一个键值对的property结构体指针
};static inline struct device_node *of_find_node_by_path(const char *path)
功能:通过路径获取节点
参数:path 路径 "/image_led"
返回值:成功 返回结构体首地址失败 返回NULLstatic inline struct device_node *of_find_node_by_name(struct device_node *from,const char *name)功能:通过节点名获取节点
参数:from 填NULL 从根节点搜索name 节点名
返回值:成功 返回结构体首地址失败 返回NULLstatic inline struct device_node *of_find_compatible_node(struct device_node *fromconst char *type,const char *compat)
功能:通过compatible获取节点
参数:from 填NULL 从根节点搜索type 填NULcompat 厂商名 设备名
返回值:成功 返回结构体首地址失败 返回NULL获取属性相关的API
static inline struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)
功能:获取键值对
参数:np device_node结构体首地址name 键的名字lenp 值的长度 单位字节
返回值:成功 返回结构体首地址失败 返回NULLstatic inline int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
功能:获取单字节数据数组
参数:np device_node结构体首地址propname 键的名字out_values 获取到的数据sz 值的个数
返回值:成功 返回0失败 返回错误码static inline int of_property_read_u32_index(const struct device_node *np,const char *propname, u32 index, u32 *out_value)
功能:获取32位数值
参数:np device_node结构体首地址propname 键的名字index 索引号 下标out_values 获取到的数据
返回值:成功 返回0失败 返回错误码static inline int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string)
功能:获取字符串
参数:np device_node结构体首地址propname 键的名字index 索引号 下标out_string 获取到的字符串
返回值:成功 返回0失败 返回错误码
编写的驱动文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>//字符设备名 sys/class 描述名称
#define CDEV_NAME "image_led"
//设备树中 自定义led节点路径
#define LED_NODE_PATH "/image_led"
//自定义设备树节点中 led引脚名称
#define LED_NODE_PROPERTY "led_gpio"int major = -1;
int gpionum = -1;
struct class *cls = NULL;
struct device *dev = NULL;
struct device_node *led_node = NULL; //设备树节点结构体
char kbuf[128] = {0};int my_led_open(struct inode *inode, struct file *file)
{printk("device:image_led is open\r\n");return 0;
}ssize_t my_led_read (struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{char level = 0;if(size > 1){size = 1;}level = gpio_get_value(gpionum);if(copy_to_user(ubuf, &level, size)){printk("copy data from user failed!\n");return -EINVAL;}printk("device:image_led cur level is %d\r\n",level);return size;
}
ssize_t my_led_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{size = size > sizeof(kbuf) ? sizeof(kbuf) : size;if(copy_from_user(kbuf, ubuf, size)){printk("copy data from user failed!\n");return -EINVAL;}if(kbuf[0] == '1'){gpio_set_value(gpionum,1); //设置高电平printk("device:image_led set level is %d\r\n",kbuf[0]);}else{gpio_set_value(gpionum,0); //设置低电平printk("device:image_led set level is %d\r\n",kbuf[0]);}return size;
}
int my_led_close (struct inode *inode, struct file *file)
{printk("device:image_led is close\r\n");return 0;
}struct file_operations fops = {.open = my_led_open,.read = my_led_read,.write = my_led_write,.release = my_led_close
};int led_gpio_init(void)
{//读取设备树上节点路径获取节点数据led_node = of_find_node_by_path(LED_NODE_PATH);if(led_node == NULL){printk("find node %s failed!\n",LED_NODE_PATH );return -ENODEV;}//通过节点数据中的名称获取gpio编号gpionum = of_get_named_gpio(led_node, LED_NODE_PROPERTY, 0);if(gpionum < 0){printk("get property %s failed!\n",LED_NODE_PROPERTY );return -EINVAL;}//申请GPIO 防止占用冲突if(gpio_request(gpionum,NULL)){printk("request gpio failed!\n");return -EINVAL;}//设置GPIO为输出if(gpio_direction_output(gpionum, 0)){printk("set gpio direction failed!\n");return -EINVAL;}return 0;
}
void led_gpio_deinit(void)
{gpio_set_value(gpionum,0);gpio_free(gpionum);
}
static int __init image_led_init(void)
{//注册字符设备major = register_chrdev(0,CDEV_NAME,&fops);if(major < 0){printk("register chardev failed! \n");return -1;}//自动创建设备节点//提交目录信息cls = class_create(THIS_MODULE,CDEV_NAME);if(IS_ERR(cls)){printk("auto create node failed! \n");goto del_cdev; //}//提交设备信息dev = device_create(cls,NULL,MKDEV(major,0),NULL,"image_led0");if(IS_ERR(dev)){printk("device create failed! \n");goto destroy_class;}if(led_gpio_init()<0){goto free_gpio;}return 0;free_gpio:led_gpio_deinit();
destroy_class:class_destroy(cls);
del_cdev:unregister_chrdev(major,CDEV_NAME);
return -EIO;
}static void __exit image_led_exit(void)
{led_gpio_deinit();device_destroy(cls, MKDEV(major,0));class_destroy(cls);unregister_chrdev(major,CDEV_NAME);
}module_init(image_led_init);
module_exit(image_led_exit);
MODULE_LICENSE("GPL");
APP测试文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define MY_CHRDEV_NAME "/dev/image_led0"int fd = 0;
int main(void)
{char light_cmd = '1';char off_cmd = '0';char count = 0;char read_buf[1] = {0};fd = open(MY_CHRDEV_NAME,O_RDWR);if(fd < 0){printf("open %s is failed fd:%d\r\n",MY_CHRDEV_NAME,fd);return -1;}for(count=0;count<10;count++){write(fd,&light_cmd,1);printf("cmd:%c\r\n",light_cmd);usleep(500*1000);read(fd,read_buf,1);printf("read gpio level is %d\r\n",read_buf[0]);write(fd,&off_cmd,1);printf("cmd:%c\r\n",off_cmd);usleep(500*1000);read(fd,read_buf,1);printf("read gpio level is %d\r\n",read_buf[0]);}close(fd);return 0;
}
Makefile文件
PWD ?= $(shell pwd)
KERNELDIR:=/home/image/work/tspi/sdk/kernel
CROSS_COMPILE ?= /home/image/work/tspi/sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
obj-m += demo.o
CC := $(CROSS_COMPILE)gcc
module:make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 modules$(CC) test_app.c -o test_app
clean:make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 cleanrm test_app
APP执行效果
root@RK356X:/tmp# ./test_app
cmd:1
read gpio level is 1
cmd:0
read gpio level is 0
cmd:1
read gpio level is 1
cmd:0
read gpio level is 0
cmd:1
read gpio level is 1
cmd:0
read gpio level is 0
cmd:1
..............
开发板打印调试信息
root@RK356X:/tmp# dmesg
[ 6757.176165] device:image_led is open
[ 6757.176265] device:image_led set level is 49
[ 6757.676878] device:image_led cur level is 1
[ 6757.677111] device:image_led set level is 48
[ 6758.177583] device:image_led cur level is 0
[ 6758.177804] device:image_led set level is 49
[ 6758.678220] device:image_led cur level is 1
[ 6758.678423] device:image_led set level is 48
[ 6759.178801] device:image_led cur level is 0
[ 6759.178988] device:image_led set level is 49
[ 6759.679358] device:image_led cur level is 1
[ 6759.679582] device:image_led set level is 48
[ 6760.179943] device:image_led cur level is 0
[ 6760.180143] device:image_led set level is 49
[ 6760.680495] device:image_led cur level is 1
[ 6760.680716] device:image_led set level is 48
[ 6761.181149] device:image_led cur level is 0
[ 6761.181349] device:image_led set level is 49
[ 6761.681747] device:image_led cur level
..................
//48 49为ASCII码的0 和 1这里打印没处理好
开发板运行效果
开发板运行效果
相关文章:
泰山派GPIO子系统驱动---亮灯
本人linux驱动小白,文章基于B站up主 李Sir______ 视频内容记录,做笔记用。如有错误欢迎指正。本文将以开发板第40引脚GPIO3_B4作为LED灯珠的控制引脚,高电平灯亮,低电平灯灭。 杂话 在linux内核中,芯片厂商已经把所有…...

【C#特性整理】C#特性及语法基础
1. C#特性 1.1 统一的类型系统 C#中, 所有类型都共享一个公共的基类型. 例如,任何类型的实例都可以通过调用ToString方法将自身转换为一个字符串 1.2 类和接口 接口: 用于将标准与实现隔离, 仅仅定义行为,不做实现. 1.3 属性、方法、事件 属性: 封装了一部分对…...
Presence:Colyseus用于管理实时分布式数据的工具
Colyseus Presence 详细介绍 Presence 是 Colyseus 中用于管理实时分布式数据的一种工具。它主要用于在多房间、多服务器或分布式部署中实现玩家的实时在线状态、数据共享和通信。Presence 提供了一套简单的 API 来处理诸如在线玩家跟踪、分布式数据存储和发布/订阅模式等功能…...

Ubuntu 搭建SVN服务
目录 1、安装SVN服务端 2、创建SVN版本库 3、修改SVN配置svnserve.conf 3.1 配置文件介绍 3.2 svnserve.conf配置 3.3 authz配置设置用户读写权限 3.4 passwd配置 用户名密码 4、启动SVN服务 4.1 配置开机启动 1、安装SVN服务端 sudo apt-get install subversion…...
HTML速查
HTML 基本文档 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>文档标题</title></head><body>可见文本...</body> </html>基本标签(Basic Tags) <h1>最大的…...

day-102 二进制矩阵中的最短路径
思路 BFS 解题过程 从起点依次向八个方向尝试(之后也一样),如果某个位置在矩阵内且值为0且没有访问过,将其添加到一个队列中,依次类推,直到到达出口 Code class Solution {public int shortestPathBinar…...
SQL Server大批量数据插入
数据库连接及相关操作 public class DataBase {/*** 驱动*/private static final String DRIVER PropertiesUtil.getString("spring.datasource.driver-class-name");/*** 数据库地址*/private static final String URL PropertiesUtil.getString("spring.da…...
在 Ubuntu 下通过 Docker 部署 Caddy 服务器
嘿,伙伴们!今天我们来聊聊如何在 Ubuntu 系统下通过 Docker 部署 Caddy 服务器。Caddy 是一个现代的 Web 服务器,支持自动 HTTPS,简单易用,特别适合快速搭建网站。而 Docker 则是一个让你可以隔离和管理应用的神器。结…...

ZooKeeper注册中心实现
具体步骤 安装ZooKeeper(启动端口占用,2181:客户端,8080:管理端)引入客户端依赖实现注册中心接口SPI补充ZooKeeper注册中心 引入依赖 <!-- zookeeper --> <dependency><groupId>org.a…...
数仓建模:如何进行实体建模?
目录 1 如何进行实体建模? 业务建模 领域建模 逻辑建模 2 实体建模具体步骤 需求分析...

Python编程技术
设计目的 该项目框架Scrapy可以让我们平时所学的技术整合旨在帮助学习者提高Python编程技能并熟悉基本概念: 1. 学习基本概念:介绍Python的基本概念,如变量、数据类型、条件语句、循环等。 2. 掌握基本编程技巧:教授学生如何使…...
「Mac玩转仓颉内测版55」应用篇2 - 使用函数实现更复杂的计算
本篇教程基于仓颉编程语言扩展了计算器功能,支持加减乘除的基础运算,以及幂运算和开平方等高级功能。代码经过简化后,移除了对输入的复杂校验,提升了程序的可维护性和交互效率。 关键词 仓颉编程语言函数封装高级运算 一、功能说…...
map参数详解
const items new Array(15).fill(null).map((_, index) > ({key: index 1,label: nav ${index 1}, })); $.map() 函数用于使用指定函数处理数组中的每个元素(或对象的每个属性),并将处理结果封装为新的数组返回。 注意:1. 在jQuery 1.6 之前&#…...

OSI 七层模型 | TCP/IP 四层模型
注:本文为 “OSI 七层模型 | TCP/IP 四层模型” 相关文章合辑。 未整理去重。 OSI 参考模型(七层模型) BeretSEC 于 2020-04-02 15:54:37 发布 OSI 的概念 七层模型,亦称 OSI(Open System Interconnection…...
高转速风扇|无刷暴力风扇方案设计
在当今科技高速发展的时代,电子设备的性能不断提升,散热问题也日益成为关注的焦点。而 13w 高转速暴力风扇方案的出现,为解决各种设备的散热难题提供了强大的技术支持。 一、高转速暴力风扇的重要性 随着电子设备的不断升级,其功率…...

GPU 进阶笔记(三):华为 NPU/GPU 演进
大家读完觉得有意义记得关注和点赞!!! 1 术语 1.1 CPU1.2 GPU1.3 NPU / TPU1.4 小结2 华为 DaVinci 架构:一种方案覆盖所有算力场景 2.1 场景、算力需求和解决方案2.2 Ascend NPU 设计3 路线一:NPU 用在手机芯片&…...

计算机网络 (13)信道复用技术
前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号,从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用(Multiplexing)就是在一条传输媒体上同时传输…...

数据库约束和查询
一 约束意义 这个后面的字段是什么意思呢? 先前说数据类型是一种约束,约束我们只能放该类型的数据,还有其它的约束来保证数据的合法性,下面的字段就和约束有关。 编译器的编译就是一个约束,保证我们的代码一定是语法合格的。我们…...
网工日记:FTP两种工作模式的区别
FTP 的主动模式和被动模式在连接建立的发起方、数据传输端口以及对网络环境的适应性等方面存在明显区别: 1. 连接发起方 主动模式:数据连接由服务器主动发起。在控制连接建立后,客户端通过 PORT 命令告知服务器自己用于接收数据的临时端口号…...

NLP模型工程化部署
文章目录 一、理论-微服务、测试与GPU1)微服务架构2)代码测试3)GPU使用 二、实践-封装微服务,编写测试用例和脚本,并观察GPU1)微服务封装(RestFul和RPC)2)测试编写(unit_test\api_test\load_tes…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...