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
英文地址,請注明出處:http://en.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;采用主…