Linux LED驅動(gpio子系統)

0. gpio子系統

gpio子系統是linux內核當中用于管理GPIO資源的一套系統,它提供了很多GPIO相關的API接口,驅動程序中使用GPIO之前需要向gpio子系統申請。
gpio子系統的主要目的就是方便驅動開發者使用gpio,驅動開發者在設備樹中添加gpio相關信息,然后就可以在驅動程序中使用gpio子系統提供的API函數來操作GPIO。
Linux內核向驅動開發者屏蔽掉了GPIO的設置過程,極大的方便了驅動開發者使用GPIO。

  • 設備樹

使用gpio子系統時,需要更改設備樹。
led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;表示led引腳使用的IO屬于gpio0,是gpio0的7號引腳,高電平有效。

led {compatible = "alientek,led";status = "okay";default-state = "on";led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
}
  • GPIO驅動程序

在使用gpio子系統之前,需要向內核gpio子系統注冊這一套操作硬件寄存器的“方法”,也就是GPIO驅動。但對于驅動開發,設置好設備樹以后就可以使用gpio子系統提供的API函數來操作指定的GPIO,gpio子系統向驅動開發人員屏蔽了具體的讀寫寄存器過程。

  • gpio_request()
  • gpio_free()
  • gpio_direction_input()
  • gpio_direction_output()
  • gpio_get_value()
  • gpio_set_value()

1. gpio子系統下的LED驅動

  • 修改設備樹
led {compatible = "alientek,led";status = "okay";default-state = "on";led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
}
  • 編寫gpio子系統下的LED驅動
  1. 定義設備結構體
    設備結構體包含cdev、類、設備、設備號等。
  2. 實現開關讀寫4個系統調用函數
    在文件打開函數中使用filp->private_data設置私有數據;使用copy_from_user()實現用戶空間和內存空間數據交互,并使用gpio_set_value()設置IO值(LED的亮滅)。
  3. 初始化設備操作函數結構體
  4. 實現設備注冊和注銷函數
    使用of_find_node_by_path(“/led”)函數led設備節點,使用of_property_read_string()函數讀取status屬性,使用of_property_read_string()函數獲取compatible屬性值并進行匹配,使用of_get_named_gpio()獲取設備樹中的led-gpio屬性,得到LED所使用的GPIO編號,然后使用gpio_request()向gpio子系統申請使用GPIO,使用gpio_direction_output()將led gpio管腳設置為輸出模式,使用register_chrdev_region()函數、alloc_chrdev_region()函數創建設備號,并使用module_init()函數指定設別注冊函數實現insmod,使用cdev_init()初始化cdev,使用cdev_add()添加cdev,使用class_create()創建類,使用device_create()創建設備;使用device_destroy()注銷設備,使用class_destroy()注銷類,使用cdev_del()刪除cdev,使用unregister_chrdev_region()注銷設備號,使用led_iounmap()取消地址映射,并使用module_exit()函數指定設備注銷函數實現rmmod。
  5. 添加LICENSE和作者
    使用MODULE_LICENSE()函數和MODULE_AUTHOR()函數添加LICENSE和作者。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>#define GPIOLED_CNT		1				/* 設備號個數 */
#define GPIOLED_NAME	"gpioled"		/* 名字 */// 0. **定義設備結構體**
/* dtsled設備結構體 */
struct gpioled_dev {dev_t devid;			/* 設備號 */struct cdev cdev;		/* cdev */struct class *class;	/* 類 */struct device *device;	/* 設備 */int major;				/* 主設備號 */int minor;				/* 次設備號 */struct device_node *nd;	/* 設備節點 */int led_gpio;			/* LED所使用的GPIO編號 */
};static struct gpioled_dev gpioled;	/* led設備 */// 1. **實現開關讀寫4個系統調用函數**
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &gpioled;	/* 設置私有數據 */return 0;
}static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{return 0;
}static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{int ret;char kern_buf[1];ret = copy_from_user(kern_buf, buf, cnt);	// 得到應用層傳遞過來的數據if(0 > ret) {printk(KERN_ERR "kernel write failed!\r\n");return -EFAULT;}if (0 == kern_buf[0])gpio_set_value(gpioled.led_gpio, 0);	// 如果傳遞過來的數據是0則關閉ledelse if (1 == kern_buf[0])gpio_set_value(gpioled.led_gpio, 1);	// 如果傳遞過來的數據是1則點亮ledreturn 0;
}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}// 2. **初始化設備操作函數結構體**
static struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= led_open,.read		= led_read,.write		= led_write,.release	= led_release,
};// 3. **實現設備注冊和注銷函數**
static int __init led_init(void)
{const char *str;int ret;/* 1.獲取led設備節點 */gpioled.nd = of_find_node_by_path("/led");if(NULL == gpioled.nd) {printk(KERN_ERR "gpioled: Failed to get /led node\n");return -EINVAL;}/* 2.讀取status屬性 */ret = of_property_read_string(gpioled.nd, "status", &str);if(!ret) {if (strcmp(str, "okay"))return -EINVAL;}/* 2、獲取compatible屬性值并進行匹配 */ret = of_property_read_string(gpioled.nd, "compatible", &str);if(0 > ret) {printk(KERN_ERR "gpioled: Failed to get compatible property\n");return ret;}if (strcmp(str, "alientek,led")) {printk(KERN_ERR "gpioled: Compatible match failed\n");return -EINVAL;}printk(KERN_INFO "gpioled: device matching successful!\r\n");/* 4.獲取設備樹中的led-gpio屬性,得到LED所使用的GPIO編號 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(!gpio_is_valid(gpioled.led_gpio)) {printk(KERN_ERR "gpioled: Failed to get led-gpio\n");return -EINVAL;}printk(KERN_INFO "gpioled: led-gpio num = %d\r\n", gpioled.led_gpio);/* 5.向gpio子系統申請使用GPIO */ret = gpio_request(gpioled.led_gpio, "LED-GPIO");if (ret) {printk(KERN_ERR "gpioled: Failed to request led-gpio\n");return ret;}/* 6.將led gpio管腳設置為輸出模式 */gpio_direction_output(gpioled.led_gpio, 0);/* 7.初始化LED的默認狀態 */ret = of_property_read_string(gpioled.nd, "default-state", &str);if(!ret) {if (!strcmp(str, "on"))gpio_set_value(gpioled.led_gpio, 1);elsegpio_set_value(gpioled.led_gpio, 0);} elsegpio_set_value(gpioled.led_gpio, 0);/* 8.注冊字符設備驅動 *//* 創建設備號 */if (gpioled.major) {gpioled.devid = MKDEV(gpioled.major, 0);ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);if (ret)goto out1;} else {ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);if (ret)goto out1;gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}printk("gpioled: major=%d,minor=%d\r\n",gpioled.major, gpioled.minor); /* 初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 添加一個cdev */ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);if (ret)goto out2;/* 創建類 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {ret = PTR_ERR(gpioled.class);goto out3;}/* 創建設備 */gpioled.device = device_create(gpioled.class, NULL,gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {ret = PTR_ERR(gpioled.device);goto out4;}return 0;out4:class_destroy(gpioled.class);out3:cdev_del(&gpioled.cdev);out2:unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);out1:gpio_free(gpioled.led_gpio);return ret;
}static void __exit led_exit(void)
{/* 注銷設備 */device_destroy(gpioled.class, gpioled.devid);/* 注銷類 */class_destroy(gpioled.class);/* 刪除cdev */cdev_del(&gpioled.cdev);/* 注銷設備號 */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);/* 釋放GPIO */gpio_free(gpioled.led_gpio);
}/* 驅動模塊入口和出口函數注冊 */
module_init(led_init);
module_exit(led_exit);// 4. **添加LICENSE和作者**
MODULE_AUTHOR("DengTao <773904075@qq.com>");
MODULE_DESCRIPTION("Alientek ZYNQ GPIO LED Driver");
MODULE_LICENSE("GPL");

應用程序:

  1. 打開文件
  2. 從文件讀取數據
  3. 將數據寫入文件
  4. 關閉文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>/** @description		: main主程序* @param - argc	: argv數組元素個數* @param - argv	: 具體參數* @return			: 0 成功;其他 失敗*/
int main(int argc, char *argv[])
{int fd, ret;unsigned char buf[1];if(3 != argc) {printf("Usage:\n""\t./ledApp /dev/led 1		@ close LED\n""\t./ledApp /dev/led 0		@ open LED\n");return -1;}/* 打開設備 */fd = open(argv[1], O_RDWR);if(0 > fd) {printf("file %s open failed!\r\n", argv[1]);return -1;}/* 將字符串轉換為int型數據 */buf[0] = atoi(argv[2]);/* 向驅動寫入數據 */ret = write(fd, buf, sizeof(buf));if(0 > ret){printf("LED Control Failed!\r\n");close(fd);return -1;}/* 關閉設備 */close(fd);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/75799.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75799.shtml
英文地址,請注明出處:http://en.pswp.cn/web/75799.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

go中new和make有什么異同?

相同點&#xff1a;都是給變量分配內存 不同點&#xff1a; 作用類型不同。new通常給int、string、數組類型的變量分配內存&#xff0c;而make通常給slice、map、channel分配內存。返回值類型不同。new返回指向變量的指針&#xff0c;make返回的是變量本身new分配內存空間后&…

C/C++基礎知識點

隨著工作中瑣事越來越多&#xff0c;靜下來好好敲代碼的時間越來越少&#xff0c;基礎知識雖然簡單&#xff0c;但常看常新&#xff0c;并記錄下來共勉。 一、基礎知識點 1. 內存區域中數據管理 在C和C中&#xff0c;內存分為多個區域&#xff0c;每個區域負責存儲不同類型的…

消息中間件kafka,rabbitMQ

在分布式系統中,消息中間件是實現不同組件之間異步通信的關鍵技術。Kafka 和 RabbitMQ 是兩個非常流行的消息中間件系統,它們各自有著不同的特點和應用場景。下面將分別介紹 Kafka 和 RabbitMQ,并討論它們在消息隊列中的使用。 一、Kafka (Apache Kafka) 主要特點: 高吞吐…

2k1000LA , 調試串口改成通信串口, uart.

客戶的問題解決了&#xff0c;但是 調試串口 改成通信串口的問題&#xff0c;并沒有解決&#xff0c;我走的其他的路徑。 先準備一些資料。 以備以后使用。 網上的資料。 總結&#xff1a; 實際上 有幾種思路了。 1 就是更改 設備樹的 chosen 節點&#xff0c; 瑞芯微又單獨…

springboot集成spring-cloud-context手動刷新并讀取更新后的配置文件

背景 springboot單體項目在運行過程需要刷新springboot配置文件值&#xff0c;比如某個接口限流閾值&#xff0c;新增某個賬戶等場景。分布式設計的可以直接引入一些持久化中間件比如redis等&#xff0c;也可以用相關配置中心中間件如nacos等。處于成本等場景單體項目可以考慮①…

proteus8.17 環境配置

Proteus介紹 Proteus 8.17 是一款功能強大的電子設計自動化&#xff08;EDA&#xff09;軟件&#xff0c;廣泛應用于電子電路設計、仿真和分析。以下是其主要特點和新功能&#xff1a; ### 主要功能 - **電路仿真**&#xff1a;支持數字和模擬電路的仿真&#xff0c;包括靜態…

手機端可部署的開源大模型; 通義千問2.5訓練和推理需要的內存和外存

手機端可部署的開源大模型 目錄 手機端可部署的開源大模型Qwen2.5 0.5B 7b 推理采用手機內存需要多少Qwen2.5 0.5B不同量化精度下的內存需求Qwen2.5 7B不同量化精度下的內存需求通義千問2.5訓練和推理需要的內存和外存推理階段1. Qwen2.5 - 7B2. Qwen2.5 - 14B3. Qwen2.5 - 72B…

【uniapp-兼容性處理】swiper在iOS上偶發出現后幾張圖片白屏情況

【日期】2025-04-14 【問題】 swiper在iOS上偶發出現后幾張圖片白屏情況 swiper內部的幾個swiper-item垂直排列&#xff0c;各自進行滾動&#xff0c;樣式方面兼容性出現問題 【原因】&#xff1a; 原代碼&#xff1a;&#xff08;不應在swiper-item添加style屬性&#xf…

SpringBoot連接MQTT客戶端

引入依賴 <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.2</version> </dependency> 啟動類 SpringBootApplication public class AxiosDemoApplic…

HTML:網頁的骨架 — 入門詳解教程

HTML&#xff1a;網頁的骨架 — 入門詳解教程 HTML&#xff08;HyperText Markup Language&#xff0c;超文本標記語言&#xff09;是構建網頁的基礎語言&#xff0c;負責定義網頁的結構和內容。無論是簡單的個人博客&#xff0c;還是復雜的企業網站&#xff0c;HTML都是不可或…

212、【圖論】字符串接龍(Python)

題目描述 題目鏈接&#xff1a;110. 字符串接龍 代碼實現 import collectionsn int(input()) beginStr, endStr input().split() strList [input() for _ in range(n)]deque collections.deque() # 使用隊列遍歷結點 deque.append([beginStr, 1]) # 存儲當前字符串和遍…

操作系統導論——第19章 分頁:快速地址轉換(TLB)

使用分頁作為核心機制來實現虛擬內存&#xff0c;可能會帶來較高的性能開銷。使用分頁&#xff0c;就要將內存地址空間切分成大量固定大小的單元&#xff08;頁&#xff09;&#xff0c;并且需要記錄這些單元的地址映射信息。因為這些映射信息一般存儲在物理內存中&#xff0c;…

使用Apache POI(Java)創建docx文檔和表格

1、引入poi 依賴組件 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.0.0</version> </dependency> <dependency><groupId>org.apache.poi</groupId>&…

python cv2 安裝

在Python中安裝opencv-python&#xff08;即OpenCV庫&#xff09;&#xff0c;通常有兩種方法&#xff1a;使用pip命令或通過conda&#xff08;如果你使用的是Anaconda或Miniconda&#xff09;。以下是詳細的步驟&#xff1a; 方法1&#xff1a;使用pip 打開你的命令行界面&am…

讀者、寫者問題優化

#include <stdio.h> #include <time.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define NUM_READERS 5 #define NUM_WRITERS 5 // 定義信號量和全局變量 sem_t sdata, srcount; int rea…

如何通過前端表格控件實現自動化報表?1

背景 最近伙伴客戶的項目經理遇見一個問題&#xff0c;他們在給甲方做自動化報表工具&#xff0c;項目已經基本做好了&#xff0c;但拿給最終甲方&#xff0c;業務人員不太買賬&#xff0c;項目經理為此也是天天抓狂&#xff0c;沒有想到合適的應對方案。 現階段主要面臨的問…

RabbitMQ 優先級隊列詳解

本文是博主在記錄使用 RabbitMQ 在執行業務時遇到的問題和解決辦法&#xff0c;因此查閱了相關資料并做了以下記載&#xff0c;記錄了優先級隊列的機制和使用要點。 本文為長文&#xff0c;詳細介紹了相關的知識&#xff0c;可作為學習資料看。 文章目錄 一、優先級隊列介紹1、…

代理模式簡述

目錄 一、主要角色 二、類型劃分 三、靜態代理 示例 缺點 四、動態代理 JDK動態代理 示例 缺點 CGLib動態代理 導入依賴 示例 五、Spring AOP 代理模式是一種結構型設計模式&#xff0c;通過代理對象控制對目標對象的訪問&#xff0c;可在不改變目標對象情況下增強…

每日一題——云服務計費問題

云服務計費問題&#xff08;哈希表 排序&#xff09;| 附詳細 C源碼解析 一、題目描述二、輸入描述三、輸出描述四、樣例輸入輸出輸入示例&#xff1a;輸出示例&#xff1a;說明&#xff1a; 五、解題思路分析六、C實現源碼詳解&#xff08;完整&#xff09;七、復雜度分析 一…

【JVM】運行時數據區域

文章目錄 1. 程序計數器補充 2. 虛擬機棧2.1 棧幀1. 局部變量表2. 操作數棧3. 動態鏈接4. 方法返回地址補充 3. 本地方法棧4. 堆5. 方法區靜態常量池&#xff08;Class常量池&#xff09;運行時常量池字符串常量池&#xff08;1&#xff09;位置變化&#xff08;2&#xff09;放…