驅動開發(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
繁體地址,請注明出處:http://hk.pswp.cn/web/82227.shtml
英文地址,請注明出處:http://en.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; 常見日志文件及功能 文…