驱动开发(2)|鲁班猫rk3568简单GPIO波形操控

   上篇文章写了如何下载内核源码、编译源码的详细步骤,以及一个简单的官方demo编译,今天分享一下如何根据板子的引脚写自己控制GPIO进行高低电平反转。
想要控制GPIO之前要学会看自己的引脚分布图,我用的是鲁班猫RK3568,引脚分布图如下所示:
在这里插入图片描述
具体板子的引脚示意图可以在这里看:教程官网

1 通过shell命令进行GPIO控制

1.1 使用GPIO sysfs接口控制IO

#以下所有操作均需要打开管理者权限使用
#使能引脚GPIO1_A0
echo 32 > /sys/class/gpio/export#设置引脚为输入模式
echo in > /sys/class/gpio/gpio32/direction
#读取引脚的值
cat /sys/class/gpio/gpio32/value#设置引脚为输出模式
echo out > /sys/class/gpio/gpio32/direction
#设置引脚为低电平
echo 0 > /sys/class/gpio/gpio32/value
#设置引脚为高电平
echo 1 > /sys/class/gpio/gpio32/value#复位引脚
echo 32 > /sys/class/gpio/unexport

这里需要注意的是,设置引脚模式为输出模式之后才能对引脚高低电平进行设置,其他的没什么好主意的,多敲几遍啥都懂了。至于官方给的计算引脚的位置,我根本没看,因为图中都给你算好了,文章开头的引脚分布图中的编号一列就为引脚具体的值。

1.2 使用libgpiod控制IO

首先要下载libgpio:

sudo apt install gpiod

具体使用一共就这几个接口:
在这里插入图片描述

设置GPIO1_A1为高电平:

gpioset 1 1=1

设置GPIO1_A1为低电平:

gpioset 1 1=0

这里的gpioset后面第一个1是GPIO的组号,因为是GPIO1所以为1,我想换个板子一共五组(GPIO0-GPIO4),所以范围是0-4。第二个是索引号,具体计算方式可以参照下图:
在这里插入图片描述

2 通过代码来操控GPIO接口

2.1 通过GPIO 子系统设置引脚

直接使用gpio_set_value,这种方式与1.1类似,很简单,也是直接看引脚途中的编号就可以:

//设置GPIO1_A0为高电平
gpio_set_value(32,1);
//设置GPIO1_A0为低电平
gpio_set_value(32,0);

这种方式操作GPIO,就算直接拉高在拉低操作延时都会在500ns左右。
这是我写的部分代码,感兴趣的兄弟可以看一下:
pin_ctl.c:

#include "pin_ctl.h"void set_ce_high()
{gpio_set_value(GPIO_A1, data & 0x01);
}
static void set_pinA_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_A1, data & 0x01);}else{//不是上电时序,是选择喷头gpio_set_value(GPIO_A1, data & 0x01);gpio_set_value(GPIO_A2, (data >> 1) & 0x01);gpio_set_value(GPIO_A3, (data >> 2) & 0x01);gpio_set_value(GPIO_A4, (data >> 3) & 0x01);gpio_set_value(GPIO_A5, (data >> 4) & 0x01);gpio_set_value(GPIO_A6, (data >> 5) & 0x01);gpio_set_value(GPIO_A7, (data >> 6) & 0x01);}
}
static void set_pinD_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_D1, data & 0x01);}else{//不是上电时序gpio_set_value(GPIO_D1, data & 0x01);gpio_set_value(GPIO_D2, (data >> 1) & 0x01);gpio_set_value(GPIO_D3, (data >> 2) & 0x01);gpio_set_value(GPIO_D4, (data >> 3) & 0x01);}}
static void set_pinS_value(u8 data, int signal)
{if(signal == 0){//如果是上电时序和下电时序0gpio_set_value(GPIO_S1, data & 0x01);}else{//不是上电时序gpio_set_value(GPIO_S1, data & 0x01);gpio_set_value(GPIO_S2, (data >> 1) & 0x01);gpio_set_value(GPIO_S3, (data >> 2) & 0x01);gpio_set_value(GPIO_S4, (data >> 3) & 0x01);}}// 上电函数
static void power_on_sequence(void)
{gpio_set_value(GPIO_VL, 1);msleep(TPO_MS);gpio_set_value(GPIO_VPK, 1);gpio_set_value(GPIO_VPC, 1);// 参考书上最小0.5us//udelay(TW_US);udelay(TW_US);gpio_set_value(GPIO_CE, 1);// 6. 等待tn时间(5us),参考书上最小5usudelay(TN_US);gpio_set_value(GPIO_CK, 1);//1111->0x0Fset_pinD_value(0x0F, 0);gpio_set_value(GPIO_SH, 1); //1111111->0x7Fset_pinA_value(0x7F, 0);//1111->0x0Fset_pinS_value(0x0F, 0);printk(KERN_INFO "Full power sequence completed\n");
}
//下电函数
static void power_off_sequence(void)
{//下电顺序S->A->SH->D->CH->5us->CE->0.5/1us->VPC->VPK->1ms->VLset_pinS_value(0x00, 0);set_pinA_value(0x00, 0);gpio_set_value(GPIO_SH, 0);set_pinD_value(0x00, 0);gpio_set_value(GPIO_CK, 0);udelay(TN_US);gpio_set_value(GPIO_CE, 0);udelay(TW_US);gpio_set_value(GPIO_VPK, 0);gpio_set_value(GPIO_VPC, 0);msleep(TPO_MS);gpio_set_value(GPIO_VL, 0);}
void SetPinDValue(u8 data)
{(data & 0x01) == 1 ? set_hig(5):set_low(5);((data >> 1) & 0x01) == 1 ? set_hig(6):set_low(6);((data >> 2) & 0x01) == 1 ? set_hig(7):set_low(7);((data >> 3) & 0x01) == 1 ? set_hig(8):set_low(8);}// 打印时序
static void print_sequence(void)
{//上电顺序//CE->CK->D1-D4->SH->A1-A7->S1-S4//选择第一个喷头(A1-A7)set_pinA_value(0x07, 1);gpio_set_value(GPIO_CK, 0);ndelay(50);gpio_set_value(GPIO_CE, 1);ndelay(50);gpio_set_value(GPIO_CK, 1);ndelay(10);set_pinD_value(0x0F, 1);ndelay(40);}static int ck_thread_func(void *data)
{while (!kthread_should_stop()) {//ck信号测试//set_ck_one_cycle();}printk(KERN_INFO "Power CK thread is stopping...\n");return 0;
}MODULE_LICENSE("GPL");
MODULE_AUTHOR("limingzhao");
MODULE_DESCRIPTION("inkjet enable pro");
//上电时序信号
EXPORT_SYMBOL(power_on_sequence);
//下电时序信号
EXPORT_SYMBOL(power_off_sequence);
//设置引脚
EXPORT_SYMBOL(set_pinA_value);
EXPORT_SYMBOL(set_pinD_value);
EXPORT_SYMBOL(set_pinS_value);
//测试程序pin_s_test
EXPORT_SYMBOL(pin_s_test);
EXPORT_SYMBOL(pin_s_test1);
EXPORT_SYMBOL(pin_s_test2);

2.2 直接操作硬件寄存器

这是官方给的示例代码,功能是点亮板子上的一个led灯:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>#define DEV_NAME            "led_chrdev"
#define DEV_CNT                 (1)#define GPIO0_BASE (0xfdd60000)
//每组GPIO,有2个寄存器,对应32个引脚,每个寄存器负责16个引脚;
//一个寄存器32位,其中高16位都是使能位,低16位对应16个引脚,每个引脚占用1比特位
#define GPIO0_DR_L (GPIO0_BASE + 0x0000)
#define GPIO0_DR_H (GPIO0_BASE + 0x0004)
#define GPIO0_DDR_L (GPIO0_BASE + 0x0008)
#define GPIO0_DDR_H (GPIO0_BASE + 0x000C)static dev_t devno;
struct class *led_chrdev_class;struct led_chrdev {struct cdev dev;unsigned int __iomem *va_dr; 	// 数据寄存器,设置输出的电压unsigned int __iomem *va_ddr; 	// 数据方向寄存器,设置输入或者输出unsigned int led_pin; // 偏移
};static int led_chrdev_open(struct inode *inode, struct file *filp)
{	unsigned int val = 0;struct led_chrdev *led_cdev = (struct led_chrdev *)container_of(inode->i_cdev, struct led_chrdev,dev);filp->private_data = container_of(inode->i_cdev, struct led_chrdev, dev);printk("open\n");//设置输出模式val = ioread32(led_cdev->va_ddr);val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0X1 << (led_cdev->led_pin));iowrite32(val,led_cdev->va_ddr);//输出高电平val = ioread32(led_cdev->va_dr);val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0x1 << (led_cdev->led_pin));iowrite32(val, led_cdev->va_dr);return 0;
}static int led_chrdev_release(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t led_chrdev_write(struct file *filp, const char __user * buf,size_t count, loff_t * ppos)
{unsigned long val = 0;char ret = 0;struct led_chrdev *led_cdev = (struct led_chrdev *)filp->private_data;printk("write \n");get_user(ret, buf);val = ioread32(led_cdev->va_dr);printk("val = %lx\n", val);if (ret == '0'){val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val &= ~((unsigned int)0x01 << (led_cdev->led_pin));   /*设置GPIO引脚输出低电平*/}else{val |= ((unsigned int)0x1 << (led_cdev->led_pin+16));val |= ((unsigned int)0x01 << (led_cdev->led_pin));    /*设置GPIO引脚输出高电平*/}iowrite32(val, led_cdev->va_dr);printk("val = %lx\n", val);return count;
}static struct file_operations led_chrdev_fops = {.owner = THIS_MODULE,.open = led_chrdev_open,.release = led_chrdev_release,.write = led_chrdev_write,
};static struct led_chrdev led_cdev[DEV_CNT] = {{.led_pin = 7}, 	//偏移,高16引脚,GPIO0_C7
};static __init int led_chrdev_init(void)
{int i = 0;dev_t cur_dev;printk("led_chrdev init (lubancat2  GPIO0_C7)\n");led_cdev[0].va_dr   = ioremap(GPIO0_DR_H, 4);	 //led_cdev[0].va_ddr  = ioremap(GPIO0_DDR_H, 4);	 // alloc_chrdev_region(&devno, 0, DEV_CNT, DEV_NAME);led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");for (; i < DEV_CNT; i++) {cdev_init(&led_cdev[i].dev, &led_chrdev_fops);led_cdev[i].dev.owner = THIS_MODULE;cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);cdev_add(&led_cdev[i].dev, cur_dev, 1);device_create(led_chrdev_class, NULL, cur_dev, NULL,DEV_NAME "%d", i);}return 0;
}module_init(led_chrdev_init);static __exit void led_chrdev_exit(void)
{int i;dev_t cur_dev;printk("led chrdev exit (lubancat2  GPIO0_C7)\n");for (i = 0; i < DEV_CNT; i++) {iounmap(led_cdev[i].va_dr); 		// 释放模式寄存器虚拟地址iounmap(led_cdev[i].va_ddr); 	// 释放输出类型寄存器虚拟地址}for (i = 0; i < DEV_CNT; i++) {cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i);device_destroy(led_chrdev_class, cur_dev);cdev_del(&led_cdev[i].dev);}unregister_chrdev_region(devno, DEV_CNT);class_destroy(led_chrdev_class);}module_exit(led_chrdev_exit);MODULE_AUTHOR("embedfire");
MODULE_LICENSE("GPL");

具体源代码位置:led_cdev.c
这种方式操作GPIO,直接拉高在拉低操作延时在160ns左右。
这里如果兄弟们想封装自己的GPIO接口,可以通过官方文档去查找:引脚说明
简单来说就是将寄存器分为了高位寄存器和低位寄存器,其中A、B两组为地位寄存器,C和D为高位寄存器,查看Rockchip_RK3568_TRM_Part1_V1.3-20220930P.PDF可以知道GPIO0的基地址为:
在这里插入图片描述
GPIO1、GPIO2、GPIO3、GPIO4的基地址为:
在这里插入图片描述
所以可以知道上述代码中地址宏定义:

//GPIO0
#define GPIO0_BASE (0xfdd60000)
//AB用这俩
#define GPIO0_DR_L (GPIO0_BASE + 0x0000)
#define GPIO0_DDR_L (GPIO0_BASE + 0x0008)
//CD用这俩
#define GPIO0_DR_H (GPIO0_BASE + 0x0004)
#define GPIO0_DDR_H (GPIO0_BASE + 0x000C)

的出处,明显可以看到这是使用的GPIO0,至于引脚设置,可以看这里:
在这里插入图片描述
具体出处及来源,看这里5.4.2.1. 定义GPIO寄存器物理地址

总结

  本文主要分享了GPIO控制的四种方式,shell两种控制方式,和使用代码控制的两种方式,重点要说的是如果你追求极致性能和对硬件有较高的控制要求,ioread32/iowrite32 可能更合适。反之,若追求易用性和代码的可维护性,gpio_set_value 更为推荐。

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

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

相关文章

ArcGIS Pro 3.4 二次开发 - 布局

环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 布局1 布局工程项1.1 引用布局工程项及其关联的布局1.2 在新视图中打开布局工程项1.3 激活已打开的布局视图1.4 引用活动布局视图1.5 将 pagx 导入工程1.6 移除布局工程项1.7 创建并打开一个新的基本布局1.8 使用修改后的CIM创建新…

OpenCV 图像像素的算术操作

一、知识点 1、operator (1)、MatExpr operator (const Mat & a, const Mat & b); a、a和b的行数、列数、通道数得相同。 b、a和b的每个像素的每个通道值分别相加。 (2)、MatExpr operator (const Mat & a, const Scalar & s); a、若a…

音视频中的复用器

&#x1f3ac; 什么是复用器&#xff08;Muxer&#xff09;&#xff1f; 复用器&#xff08;muxer&#xff09;是负责把音频、视频、字幕等多个媒体流打包&#xff08;封装&#xff09;成一个单一的文件格式的组件。 &#x1f4a1; 举个形象的例子&#xff1a; 假设你有两样东…

数据库安全性

一、计算机安全性概论 &#xff08;一&#xff09;核心概念 数据库安全性&#xff1a;保护数据库免受非法使用导致的数据泄露、更改或破坏&#xff0c;是衡量数据库系统的关键指标之一&#xff0c;与计算机系统安全性相互关联。计算机系统安全性&#xff1a;通过各类安全保护…

【Linux网络编程】网络层IP协议

目录 IP协议的协议头格式 网段划分 特殊的IP地址 IP地址的数量限制 私有IP地址和公网IP地址 路由 IP协议的协议头格式 4位版本号 &#xff1a;指定IP协议的版本&#xff0c;对于IPv4&#xff0c;版本号就是4。 4位首部长度&#xff1a;表名IP协议报头的长度&#xff0c;单…

“候选对话链”(Candidate Dialogue Chain)概念

目录 一、定义与形式 二、生成过程详解 1. 语言模型生成&#xff08;LLM-Based Generation&#xff09; 2. 知识图谱支持&#xff08;KG-Augmented Generation&#xff09; 3. 策略调控&#xff08;Policy-Driven Planning&#xff09; 三、候选对话链的属性 四、候选对…

Unity中的JsonManager

1.具体代码 先贴代码 using LitJson; using System.IO; using UnityEngine;/// <summary> /// 序列化和反序列化Json时 使用的是哪种方案 有两种 JsonUtility 不能直接序列化字典 ligJson可以序列化字典 /// </summary> public enum JsonType {JsonUtilit…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Split Landing Page(拆分展示页)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— SplitLandingPage 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 在这篇文章中&#xff0c;我们将实现一个交互式的左右面板…

机器学习-ROC曲线​​ 和 ​​AUC指标

1. 什么是ROC曲线&#xff1f;​​ ROC&#xff08;Receiver Operating Characteristic&#xff0c;受试者工作特征曲线&#xff09;是用来评估​​分类模型性能​​的一种方法&#xff0c;特别是针对​​二分类问题​​&#xff08;比如“患病”或“健康”&#xff09;。 ​…

Docker容器创建Redis主从集群

利用虚拟机中的三个Docker容器创建主从集群&#xff0c;容器信息&#xff1a; 容器名角色IP映射端口r1master192.168.150.1017001r2slave192.168.150.1017002r3slave192.168.150.1017003 启动多个redis实例 新建一个docker-compose文件来构建主从集群&#xff1a; 文件内容&…

手写ArrayList和LinkedList

项目仓库&#xff1a;https://gitee.com/bossDuy/hand-tear-collection-series 基于b站up生生大佬&#xff1a;https://www.bilibili.com/video/BV1Kp5tzGEc5/?spm_id_from333.788.videopod.sections&vd_source4cda4baec795c32b16ddd661bb9ce865 LinkedList package com…

每日c/c++题 备战蓝桥杯(Cantor 表)

Cantor 表的探究与实现 在数学中&#xff0c;有理数的可枚举性是一个令人惊叹的结论。今天&#xff0c;就让我们一起深入探讨这个经典问题&#xff0c;并分享一段精心编写的代码&#xff0c;揭开这一数学奥秘的神秘面纱。 问题背景 在 19 世纪末&#xff0c;伟大的数学家康托…

解决idea与springboot版本问题

遇到以下问题&#xff1a; 1、springboot3.2.0与jdk1.8 提示这个包org.springframework.web.bind.annotation不存在&#xff0c;但是pom已经引入了spring-boot-starter-web 2、Error:Cannot determine path to tools.jar library for 17 (D:/jdk17) 3、Error:(3, 28) java: …

Notepad++找回自动暂存的文件

场景&#xff1a; 当你没有保存就退出Notepad&#xff0c;下次进来Notepad会自动把你上次编辑的内容显示出来&#xff0c;以便你继续编辑。除非你手动关掉当前页面&#xff0c;这样Notepad就会删除掉自动保存的内容。 问题&#xff1a; Notepad会将自动保存的文件地址,打开Note…

yolov12毕设前置知识准备 1

1 什么是目标检测呢&#xff1f; 目标检测&#xff08;Object Detection&#xff09;主要用于识别图像或视频中特定类型物体的位置&#xff0c;并标注其类别。 简单来说&#xff0c;就是让计算机像人类一样 “看懂” 图像内容&#xff0c;不仅能识别出物体&#xff08;如人、…

unix/linux source 命令,其内部结构机制

要理解 source (或 .) 命令的内部结构机制,我们需要戴上“操作系统”和“解释器设计”的眼镜,深入到 Shell 如何管理其状态以及如何执行命令的层面。 虽然我们无法直接看到 Shell 内部的 C 代码(除非我们去阅读 Bash 或 Zsh 的源码),但我们可以基于其行为和操作系统的原理…

计算机网络学习20250528

地址解析协议ARP 实现IP地址和Mac地址的转换 ARP工作原理&#xff1a; 每台主机或路由器都有一个ARP表&#xff0c;表项&#xff1a;<IP地址&#xff0c;Mac地址&#xff0c;TTL>&#xff08;TTL一般为20分钟&#xff09; 主机产生ARP查询分组&#xff0c;包含源目的IP地…

【Rust】Rust获取命令行参数以及IO操作

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

微服务中引入公共拦截器

本文使用的微服务版本为springcloudAlbaba :2021.0.4.0 微服务工程&#xff0c;一般公共的东西都放入一个工程&#xff0c;别的微服务都会引入这个工程&#xff0c;比如common-service,那么就可以在这个工程编写一个拦截器&#xff1a;&#xff0c;比如&#xff1a; public cla…

Linux SLES 系统的/var/log/下的常见文件及其作用

在 SUSE Linux Enterprise Server&#xff08;SLES&#xff09; 系统中&#xff0c;/var/log/ 目录是系统日志的集中地&#xff0c;存储了各种服务、内核、系统消息的日志。以下是一些在 /var/log/ 下常见的日志文件及其功能&#xff1a; &#x1f4c2; 常见日志文件及功能 文…