泰山派学习14--pinctr、gpio子系统控制设备树LED

news/2024/11/17 14:27:40/文章来源:https://www.cnblogs.com/zblblog/p/18304167

1、在泰山派设备树的/根节点上添加zbl_led子节点(路径:Z:\sdk\linux\kernel\arch\arm64\boot\dts\rockchip)

  打开tspi-rk3566-user-v10-linux.dts设备树源文件

       

  在根目录下添加gpioled子节点

       

 

2、在SDK上编译kernel(./build.sh kernel)

  执行内核编译:./build.sh kernel

       

  内核编译成功输出:

       

  查看boot.img是否生成最新的

       

 

3、烧录boot.img镜像(前提已经烧录了buildroot的uboot,且正常运行的)

  仅勾选boot.img,进行烧录

      

4、查看设备树上节点是否添加成功(cd /proc/device-tree)

  ls 

  cd gpioled

  ls 

    

    

   查看对应的属性是否与设置的一致

  cat name

      

 

 5、编写设备树的字符驱动对应函数

/*
** gpioled.c
** 复用型引脚分为5组(GPIO0~4),每组里面都有32个复用型引脚,而且又分为4个小组(A、B、C、D),每个小组8个引脚(0~7)
** GPIO3_B4
** 在GPIO3大组,第二的B小组,第4个引脚,

group = 1; //GPIO3_B4 => 1, group ∈ {(A=0), (B=1), (C=2), (D=3)}

bank = 3; //GPIO3_B4 => 3, bank ∈ [0,4]

x = 4; //GPIO3_B4 => 4, X ∈ [0,7]

number = group * 8 + X = 1 * 8 + 4 = 12

pin = bank*32 + number= 3 * 32 + 28 = 108;

*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>


//设备名称
#define DEV_NAME "gpioled"
#define DEV_CNT 1
#define LED_ON 1
#define LED_OFF 0

char kbuf[128] = {0};


//定义新的一个结构体 struct chr_dev
struct gpioled_dev{
dev_t dev_id; //设备编号
struct cdev cdev; //内核字符设备
struct class *class; //设备类
struct device *device; //设备
int major; //主设备
int minor; //从设备
struct device_node *nd; //设备节点
int led_gpio;
};

static struct gpioled_dev gpioled;


int led_open(struct inode *inode, struct file *file)
{

//把文件的私有数据 private_data 指向设备结构体 dtsled
file->private_data = &gpioled;

printk(KERN_ALERT "[ KERN_ALERT ] gpioled open ...\n");

return 0;
}

ssize_t led_read(struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
printk(KERN_ALERT "[ KERN_ALERT ] gpioled read!\n");
return 0;
}

ssize_t led_write(struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
unsigned char ret;
struct gpioled_dev *dev = file->private_data;

if(size > sizeof(kbuf)){
size = sizeof(kbuf);

}
if(copy_from_user(kbuf, ubuf, size)){
printk(KERN_ALERT "[ KERN_ALERT ] copy data form user fail!\n");
return -EIO;
}

ret = kbuf[0];

if(ret == LED_ON){
gpio_set_value(dev->led_gpio, LED_ON);
}
else if(ret == LED_OFF){
gpio_set_value(dev->led_gpio, LED_OFF);
}

printk(KERN_ALERT "[ KERN_ALERT ] gpioled write!\n");
return size;

}

int led_close(struct inode *inode, struct file *file)
{
printk(KERN_ALERT "[ KERN_ALERT ] gpioled close!\n");
return 0;
}

struct file_operations gpioled_fops= {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_close
};


static int __init ledcdev_init(void)
{
int ret = 0;
const char *str;


//读取设备树节点的属性数据
//获取设备节点:gpioled
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL){
printk(KERN_ALERT "[ KERN_ALERT ] gpioled node find failed.\n");
return -EINVAL;
}else{
printk(KERN_ALERT "[ KERN_ALERT ] gpioled node find ok.\n");
}

//获取 status 属性内容
ret = of_property_read_string(gpioled.nd, "status", &str);
if(ret < 0){
printk(KERN_ALERT "[ KERN_ALERT ] status read failed!\n");
return -EINVAL;

}else{
printk(KERN_ALERT "[ KERN_ALERT ] status = %s\n",str);
}

if(strcmp(str, "okay")){
return -EINVAL;
}

//获取 compatible 属性内容
ret = of_property_read_string(gpioled.nd, "compatible", &str);
if(ret < 0){
printk(KERN_ALERT "[ KERN_ALERT ] compatible read failed!\n");
return -EINVAL;

}else{
printk(KERN_ALERT "[ KERN_ALERT ] compatible = %s\n",str);
}

if(strcmp(str, "zbl,led")){
printk(KERN_ALERT "[ KERN_ALERT ] gpioled: Compatible match failed\n");
return -EINVAL;
}


//获取设备树中的 gpio 属性
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0 ){
printk(KERN_ALERT "[ KERN_ALERT ] led-gpio find failed.\n");
return -EINVAL;
}else{
printk(KERN_ALERT "[ KERN_ALERT ] led-gpio num=%d\n", gpioled.led_gpio);
}


//向gpio子系统申请GPIO
ret = gpio_request(gpioled.led_gpio, "LED-GPIO");
if(ret){
printk(KERN_ALERT "[ KERN_ALERT ] gpioled failed to request led-gpio\n");
return ret;
}

ret = gpio_direction_output(gpioled.led_gpio, 0);
if(ret < 0){
printk(KERN_ALERT "[ KERN_ALERT ] can't set gpio output mode.\n");
}

//注册字符设备驱动
//创建设备号
if(gpioled.major){
gpioled.dev_id = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.dev_id, DEV_CNT, DEV_NAME);
if(ret < 0){
printk(KERN_ALERT "[ KERN_ALERT ] can't regsiter %s char driver [ret=%d]\n", DEV_CNT, DEV_NAME);
goto free_gpio;
}
}else{
ret = alloc_chrdev_region(&gpioled.dev_id, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk(KERN_ALERT "[ KERN_ALERT ] %s can't alloc chrdev region, ret=%d.\n", DEV_NAME, ret);
goto free_gpio;
}
gpioled.major = MAJOR(gpioled.dev_id);
gpioled.minor = MINOR(gpioled.dev_id);
}

printk(KERN_ALERT "[ KERN_ALERT ] dtsled major=%d, minor=%d.\n", gpioled.major, gpioled.minor);

//关联结构体
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);

ret = cdev_add(&gpioled.cdev, gpioled.dev_id, DEV_CNT);
if(ret < 0){
goto del_unregister;
}

gpioled.class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(gpioled.class)){
goto del_cdev;
}


gpioled.device = device_create(gpioled.class, NULL, gpioled.dev_id, NULL, DEV_NAME);
if(IS_ERR(gpioled.device)){
goto destroy_class;
}


printk(KERN_ALERT "[ KERN_ALERT ] gpioled init ...\n");
return 0;

destroy_class:
class_destroy(gpioled.class);
del_cdev:
cdev_del(&gpioled.cdev);
del_unregister:
unregister_chrdev_region(gpioled.dev_id, DEV_CNT);
free_gpio:
gpio_free(gpioled.led_gpio);
return -EIO;

}


static void __exit ledcdev_exit(void)
{

printk(KERN_ALERT "[ KERN_ALERT ] gpioled exit ...\n");

cdev_del(&gpioled.cdev);

unregister_chrdev_region(gpioled.dev_id, DEV_CNT);

device_destroy(gpioled.class, gpioled.dev_id);

class_destroy(gpioled.class);

gpio_free(gpioled.led_gpio);

}

module_init(ledcdev_init);

module_exit(ledcdev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zbl");

 

 

6、编写应用函数

/*
** dtsledapp.c
**
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>


#define LED_ON 1
#define LED_OFF 0


int main(int argc, char *argv[])
{

int fd;
int ret;
unsigned char databuf[1] = {0};

if(argc != 2){
printf("Error Usage!\n");
return -1;
}

fd = open("/dev/gpioled", O_RDWR);
if(fd == -1)
{
printf("open %s failed.\n", "/dev/gpioled");
return -1;
}

databuf[0] = atoi(argv[1]);
ret = write(fd, databuf, sizeof(databuf));
if(ret < 0){
printf("LED control failed.\n");
close(fd);
return -1;
}

ret = close(fd);
if(ret < 0){
printf("close %s failed.\n", "/dev/gpioled");
return -1;
}

return 0;
}

7、编写makefile (修改对应文件名称)

     

PWD ?= $(shell pwd)

KERNELDIR := /home/zbl/tspi-rk3566/sdk/linux/kernel
CROSS_COMPILE ?= /home/zbl/tspi-rk3566/sdk/linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
CC := $(CROSS_COMPILE)gcc


obj-m += gpioled.o

module:
make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 modules
@# -C 表示从当前目录切换到内核源码目录下,借助内核源码makefile进行make编译
@# M=$(PWD) 表示只编译当前目录下的驱动
@# ARCH=arm64 指定编译架构

$(CC) gpioledapp.c -o app
@# 交叉编译应用程序

.PHONE:clean

clean:
make -C $(KERNELDIR) M=$(PWD) ARCH=arm64 clean
rm app

 

8、拷贝到开发板(adb push xxx xxx),并对文件进行赋权(chmod 777 app gpioled.ko)

     

9、执行测试验证

  查看现有内核模块(lsmod), 并加载LED模块(insmod gpioled.ko)

     

   查看设备驱动(cat  /proc/devices)

      

  执行应用程序进行LED测试

      

   卸载驱动模块

      

 

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

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

相关文章

JavaScript全解析——本地存储✔(localStorage~sessionStorage~cookie)

●就是浏览器给我们提供的可以让我们在浏览器上保存一些数据●常用的本地存储(localStorage~sessionStorage~cookie)1-localStorage => 特点:-> 长期存储,除非手动删除否则会一直保存在浏览器中 清除缓存或者卸载浏览器也就没有了-> 可以跨页面通讯, 也就是说在一个…

三种交换方式的比较

一、互联网的核心部分 网络核心部分是互联网中最复杂的部分。 网络中的核心部分要向周围网络边缘的大量主机提供连通性,使边缘部分任何一个主机都能够向其他主机通信。 在网络核心部分起特殊作用的是路由器。 路由器是实现分组交换的关键构件,其任务是转发收到的分组,这是网…

gateway 使用细节

spring:cloud:gateway:routes:- id: test_my_provider # 自定义,全局唯一即可uri: http://localhost:8081 # 实际调用的地址predicates:- Path=/test/** # 请求匹配规则- id: test_my_consumeruri: http://localhost:8082predicates:- Path=/feign/**上一篇文章 gateway 快速入…

长链剖分笔记

与轻重链剖分相似. dfs1:高度 \(h\)、\(son\);dfs2:\(top\). 性质 1:到根最多 \(O(\sqrt n)\) 条轻边. (证明:长链长度最坏情况:1, 2, 3...) 性质 2:\(x\) 的 \(k\) 级祖先 \(y\) 所在的长链长度 \(\ge k\).(证明:若非,则 \(y-x\) 是一条更长的链,矛盾.) 树上 \(…

【漏洞分析】DoughFina 攻击事件分析:不做任何参数检查的去杠杆合约

背景介绍 2024 年 7 月 12 日,DoughFina 协议遭受了黑客攻击,造成本次攻击的主要原因是 ConnectorDeleverageParaswap 合约没有对输入参数进行检查,且该合约为 DSA 合约的 owner。攻击者可以构造恶意参数窃取 DSA 合约的资金。 攻击交易 https://app.blocksec.com/explorer/…

SpringCloud启动报错Did you forget to include spring-cloud-starter-loadbalancer?

当我在gateway项目中添加了feign依赖时,报错:   原因:因为在springcloudFeign在Hoxton.M2 RELEASED版本之后就不再使用Ribbon而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错解决办法:引入依赖即可1 <dependency> 2 …

圆方树

一些概念 割点:无向图中,若删除点x及其连边,连通块变多,那么x为割点。 点双连通:若点对x和y,删除任意非x和非y节点后,x和y仍然联通,称x和y点双连通。 点双联通子图:无向图中的一个子图G,G中任意两点都是点双连通的,那么G为原图的一个点双连通子图。 点双联通分量:无…

链表引用——约瑟夫问题

约瑟夫问题Josephu问题为:设编号为1,2,...n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。提示:用一个不带头结点的循环…

LogRotate 切割 Nginx 日志

发布于 2023-12-04 10:20:327140举报文章被收录于专栏:码农UP2U一直以来做日志切割都是使用 shell + crontab 来搞,shell 脚本可以在网上找到各种版本的,改改就用了,懒省事。这样的做法很传统,却忽略了系统的给我们提供的优秀的工具 —— logrotate。 一、Logrotate 是什么…

SQL Server中Upsert的三种方式(转载)

本文介绍了SQL Server中Upsert的三种常见写法以及他们的性能比较。SQL Server并不支持原生的Upsert语句,通常使用组合语句实现upsert功能。假设有表table_A,各字段如下所示:int型Id为主键。方法1:先查询,根据查询结果判断使用insert或者updateIF EXISTS (SELECT 1 FROM ta…

2024/7/15 模拟赛 记录

noip NOI plus!几乎全员爆蛋( 本来能拿T1 20pts 暴力分的,但是居然CE了!!! max里两个参数,一个int一个longlong dev居然没报!!!光荣爆蛋(我估计是全场唯一一个没过编的:( 题解已存至网盘 https://fzoishare.xndxfz.com:7123/

[考试记录] 2024.7.15 csp-s模拟赛4

2024.7.15 csp-s模拟赛4 T1 传送带 题面翻译 有一个长度为 \(n\) 的一维网格。网格的第 \(i\) 个单元格包含字符 \(s_i\) ,是“<”或“>”。当弹球放在其中一个格子上时,它会按照以下规则移动: 如果弹球位于第 \(i\) 个格子上且 \(s_i\) 为 <,则弹球在下一秒会向左…