STM32MP157A單片機移植Linux驅動深入版

需求整理

在Linux設備樹中新增leds節點,其有3個gpio屬性,分別表示PE10對應led1,PF10對應led2,PE8對應led3,設備樹鍵值對如下:

?? ?leds {
?? ??? ?led1-gpio = <&gpioe 10 0>;
?? ??? ?led2-gpio = <&gpiof 10 0>;
?? ??? ?led3-gpio = <&gpioe 8 0>;
?? ?};

內核驅動實現對燈控模塊的初始化函數、模塊退出函數、燈控模塊各回調函數(open/release/unlocked_ioctl/read/write)。

應用程序實現對燈控模塊的控制,通過ioctl函數控制led亮滅。

驅動開發邏輯分析

1.驅動初始化

注冊字符設備 --> register_chrdev

申請一個struct class結構體,保存當前設備類的信息 -->?class_create

申請一個struct device結構體,保存當前設備節點的信息 -->?device_create

通過名稱查找設備節點 -->?of_find_node_by_name

2.初始化GPIO

獲取GPIO編號 -->?of_get_named_gpio

請求GPIO -->?gpio_request

設置GPIO方向為輸出 -->?gpio_direction_output

3.ioctl回調函數

獲取應用程序發送的值 -->?copy_from_user

處理應用程序發送的功能碼 --> 回調函數中的第二個參數的值(一般有應用程序通過ioctl命令發送)

4.驅動退出

釋放GPIO -->?gpio_free

注銷字符設備文件 -->?device_destroy

注銷字符設備類 -->?class_destroy

注銷字符設備 -->?unregister_chrdev

5.指定模塊

指定模塊初始化函數 -->?module_init

指定模塊注銷函數 -->?module_exit

應用程序開發邏輯分析

1.打開字符設備文件 --> open

2.發送功能碼和值給驅動 --> ioctl

3.功能碼 --> _IO() / _IOW / _IOR / _IOWR ...

詳細代碼

驅動程序 --> leds.c
#include <linux/init.h>       // 包含內核初始化相關的頭文件
#include <linux/module.h>     // 包含內核模塊相關的頭文件
#include <linux/of.h>         // 包含設備樹操作相關的頭文件
#include <linux/gpio.h>       // 包含 GPIO 操作相關的頭文件
#include <linux/of_gpio.h>    // 包含設備樹 GPIO 相關的頭文件
#include <linux/fs.h>         // 包含文件操作相關的頭文件
#include <linux/uaccess.h>    // 包含用戶空間訪問內核空間相關的頭文件
#include <linux/device.h>     // 包含設備相關的頭文件
#include "leds.h"            // 包含自定義頭文件/* 設備樹節點定義leds {led1-gpio = <&gpioe 10 0>;led2-gpio = <&gpiof 10 0>;led3-gpio = <&gpioe 8 0>;};
*/
static struct class *led_class;
static struct device *led_device;
static struct device_node *leds_node;  // 定義設備節點指針
static char kernel_buf[100];  // 定義緩沖區
int led1_id,led2_id,led3_id;                     // 定義 GPIO 編號
int led_major;  // 定義主設備號static int led_open(struct inode *inode, struct file *file);
static int led_close(struct inode *inode, struct file *file);
static int led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);
static int led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg);struct file_operations fops = {.open = led_open,.release = led_close,.unlocked_ioctl = led_ioctl,.read = led_read,.write = led_write,
};static int led_open(struct inode *inode, struct file *file)
{printk("led_open\n");return 0;
}static int led_close(struct inode *inode, struct file *file)
{printk("led_close\n");return 0;
}static int led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{uint32_t n = copy_to_user(buf, kernel_buf, count);if(n){printk("copy_to_user failed\n");return -EFAULT;}printk("led_read\n");return 0;
}static int led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{uint32_t n = copy_from_user(kernel_buf, buf, count);if(n){printk("copy_from_user failed\n");return -EFAULT;}printk("led_write\n");return 0;
}static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{//獲取arg的值unsigned int value;if(copy_from_user(&value, (unsigned int *)arg, sizeof(unsigned int))){printk("copy_from_user failed\n");return -EFAULT;}switch(cmd){case LED_ON:switch(value){case 1:gpio_set_value(led1_id, 1);break;case 2:gpio_set_value(led2_id, 1);break;case 3:gpio_set_value(led3_id, 1);break;default:    printk("cmd error\n");return -EINVAL;}break;case LED_OFF:switch(value){case 1:gpio_set_value(led1_id, 0);break;case 2:gpio_set_value(led2_id, 0);break;case 3:gpio_set_value(led3_id, 0);break;default:    printk("cmd error\n");return -EINVAL;}break;default:printk("cmd error\n");return -EINVAL;}printk("led_ioctl\n");return 0;
}//通過設備樹的屬性名查找gpio,并初始化gpio
static int mygpio_init(struct device_node *np ,const char *name)
{int id;printk("name=%s\n", name);  // 打印屬性名id = of_get_named_gpio(np, name, 0);  // 獲取 GPIO 編號if (id < 0)  // 如果獲取 GPIO 編號失敗{printk("get gpio number failed\n");  // 打印獲取 GPIO 編號失敗的消息return -ENODEV;  // 返回錯誤碼}printk("get gpio number success\n");  // 打印獲取 GPIO 編號成功的消息if(gpio_request(id, NULL))  // 請求 GPIO{printk("request gpio failed\n");  // 打印請求 GPIO 失敗的消息return -ENODEV;  // 返回錯誤碼}   printk("request gpio success\n");  // 打印請求 GPIO 成功的消息if(gpio_direction_output(id, 0))  // 設置 GPIO 方向為輸出{printk("set gpio direction failed\n");  // 打印設置 GPIO 方向失敗的消息return -ENODEV;  // 返回錯誤碼}printk("set gpio direction success\n");  // 打印設置 GPIO 方向成功的消息return id;  // 返回 GPIO 編號
}static int __init leds_init(void)  // 模塊初始化函數
{//字符設備注冊led_major = register_chrdev(0, "leds_control", &fops);if(led_major < 0){printk("register_chrdev failed\n");return -ENODEV;}printk("register_chrdev success:led_major = %d\n", led_major);//申請一個struct class結構體,保存當前設備類的信息led_class = class_create(THIS_MODULE, "leds_control");if(IS_ERR(led_class)){printk("class_create failed\n");unregister_chrdev(led_major, "leds_control");   // 注銷字符設備return -ENODEV;}printk("class_create success\n");//申請一個struct device結構體,保存當前設備節點的信息led_device = device_create(led_class, NULL, MKDEV(led_major, 0), NULL, "leds_control");if(IS_ERR(led_device)){printk("device_create failed\n");class_destroy(led_class);  // 注銷類unregister_chrdev(led_major, "leds_control");   // 注銷字符設備return -ENODEV;}printk("device_create success\n");leds_node = of_find_node_by_name(NULL, "leds");  // 通過名稱查找設備節點//leds_node = of_find_compatible_node(NULL, NULL, "sjh,mynode");  // 通過兼容字符串查找設備節點if (!leds_node)  // 如果未找到設備節點{printk("mynode node not found\n");  // 打印未找到節點的消息device_destroy(led_class, MKDEV(led_major, 0));  // 注銷設備class_destroy(led_class);  // 注銷類unregister_chrdev(led_major, "leds_control");   // 注銷字符設備return -ENODEV;  // 返回錯誤碼}printk("mynode node found\n");  // 打印找到節點的消息led1_id = mygpio_init(leds_node, "led1_gpio");  // 控制 GPIOled2_id = mygpio_init(leds_node, "led2_gpio");  // 控制 GPIOled3_id = mygpio_init(leds_node, "led3_gpio");  // 控制 GPIO/*    printk("name=%s,value=%s\n", leds_node->properties->name, (char *)leds_node->properties->value);  // 打印第一個屬性的名稱和值printk("name=%s,value=%s\n", leds_node->properties->next->name, (char *)leds_node->properties->next->value);  // 打印第二個屬性的名稱和值printk("name=%s,value=%x %x\n", leds_node->properties->next->next->name, __be32_to_cpup((uint32_t *)leds_node->properties->next->next->value), __be32_to_cpup((uint32_t *)leds_node->properties->next->next->value + 1));  // 打印第三個屬性的名稱和值(無符號整數)// 解析設備樹的屬性binarry = of_find_property(leds_node, "binarry", &len);  // 查找名為 "binarry" 的屬性if (!binarry)  // 如果未找到屬性{printk("binarry property not found\n");  // 打印未找到屬性的消息return -ENODEV;  // 返回錯誤碼}for (i = 0; i < len; i++)  // 遍歷屬性值{printk("%02x ", *((unsigned char *)binarry->value + i));  // 打印屬性值的每個字節} */return 0;  // 返回成功
}static void __exit leds_exit(void)  // 模塊退出函數
{// 退出時執行的清理操作(當前為空)gpio_free(led1_id);  // 釋放 GPIOgpio_free(led2_id);  // 釋放 GPIOgpio_free(led3_id);  // 釋放 GPIO//字符設備注銷device_destroy(led_class, MKDEV(led_major, 0));  // 注銷設備class_destroy(led_class);  // 注銷類unregister_chrdev(led_major, "leds_control");   // 注銷字符設備printk("exit\n");  // 打印退出消息
}module_init(leds_init);  // 指定模塊初始化函數
module_exit(leds_exit);  // 指定模塊退出函數
MODULE_LICENSE("GPL");  // 指定模塊許可證為 GPL
MODULE_AUTHOR("Johnson");  // 指定模塊作者
MODULE_DESCRIPTION("leds driver");  // 指定模塊描述
MODULE_VERSION("V1.0");  // 指定模塊版本
應用程序 --> test_app.c

實現按1s間隔控制三個led燈亮滅

#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include<sys/ioctl.h>
#include "leds.h"            // 包含自定義頭文件int main(int argc,const char * argv[])
{int fd;int ret[3] = {1,2,3};    // 定義返回值int value;  // 定義變量fd = open("/dev/leds_control", O_RDWR);  // 打開設備文件if(fd < 0)  // 如果打開設備文件失敗{perror("open");  // 打印錯誤信息return -1;  // 返回錯誤碼}printf("open success\n");  // 打印打開設備文件成功的消息while(1){ioctl(fd, LED_ON, ret);  // 打開 LED1ioctl(fd, LED_ON, ret+1);  // 打開 LED2ioctl(fd, LED_ON, ret+2);  // 打開 LED3sleep(1);  // 等待 1 秒ioctl(fd, LED_OFF, ret);    // 關閉 LED1ioctl(fd, LED_OFF, ret+1);  // 關閉 LED2ioctl(fd, LED_OFF, ret+2);  // 關閉 LED3sleep(1);  // 等待 1 秒}return 0;
}
頭文件 --> leds.h
#ifndef __LEDS_H__
#define __LEDS_H__#define LED_ON _IOW('l', 1, int)
#define LED_OFF _IOW('l', 0, int)#endif
結果
加載內核模塊 --> insmod leds.ko

?查看自動生成的字符設備文件 --> ls /dev/

?執行應用程序 --> ./a.out

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

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

相關文章

本地DeepSeek模型GGUF文件轉換為PyTorch格式

接前文,我們在本地Windows系統上,基于GGUF文件部署了DeepSeek模型(DeepSeek-R1-Distill-Qwen-1.5B.gguf版本),但是GGUF是已經量化的版本,我們除了對其進行微調之外,無法對其訓練,那么還有沒有其他辦法對本地的GGUF部署的DeepSeek模型進行訓練呢?今天我們就反其道而行之…

http代理IP怎么實現?如何解決代理IP訪問不了問題?

HTTP代理是一種網絡服務&#xff0c;它充當客戶端和目標服務器之間的中介。當客戶端發送請求時&#xff0c;請求首先發送到代理服務器&#xff0c;然后由代理服務器轉發到目標服務器。同樣&#xff0c;目標服務器的響應也會先發送到代理服務器&#xff0c;再由代理服務器返回給…

人工智能之數學基礎:施密特正交化

本文重點 在前面的課程中,我們學習了線性空間的基,其中有一個標準正交基的概念,假設現在有一個線性向量空間,然后已經確定了該線性空間的一組基,那么如何將其轉變為標準正交基。本文將學習如何通過施密特正交化完成這個任務。 施密特正交化 施密特正交化(Schmidt Orth…

Spark(2)linux和簡單命令

&#xff08;一&#xff09;Linux的文件系統 文件系統&#xff1a;操作系統中負責管理和存儲文件信息的軟件結構稱為文件管理系統。 文件系統的結構通常叫做目錄樹結構&#xff0c;從斜桿/根目錄開始; Linux號稱萬物皆文件&#xff0c;意味著針對Linux的操作&#xff0c;大多…

Grok 3.0 Beta 版大語言模型評測

2025年2月17日至18日&#xff0c;全球首富埃隆馬斯克&#xff08;Elon Musk&#xff09;攜手其人工智能公司xAI&#xff0c;在美國重磅發布了Grok 3.0 Beta版。這款被譽為“迄今為止世界上最智能的語言模型”的AI&#xff0c;不僅集成了先進的“DeepSearch”搜索功能&#xff0…

基于COSTAR模型的內容創作:如何用框架提升寫作質量

目錄 前言1. Context&#xff08;上下文&#xff09;&#xff1a;理解背景&#xff0c;奠定寫作基礎1.1 何為上下文1.2 上下文的作用1.3 案例解析 2. Objective&#xff08;目標&#xff09;&#xff1a;明確寫作方向&#xff0c;避免跑題2.1 確立目標2.2 如何設定目標2.3 案例…

Springboot應用開發工具類整理

目錄 一、編寫目的 二、映射工具類 2.1 依賴 2.2 代碼 三、日期格式 3.1 依賴 3.2 代碼 四、加密 4.1 代碼 五、Http請求 5.1 依賴 5.2 代碼 六、金額 6.1?代碼 七、二維碼 7.1 依賴 7.2 代碼 八、坐標轉換 8.1 代碼 九、樹結構 9.1?代碼 9.1.1 節點 …

【Research Proposal】基于提示詞方法的智能體工具調用研究——研究問題

博客主頁&#xff1a; [小????????] 本文專欄: AIGC | ChatGPT 文章目錄 &#x1f4af;前言&#x1f4af;研究問題1. 如何優化提示詞方法以提高智能體的工具調用能力&#xff1f;2. 如何解決提示詞方法在多模態任務中的挑戰&#xff1f;3. 如何通過提示詞優化智能體…

Java 大視界 -- 國際競爭與合作:Java 大數據在全球市場的機遇與挑戰(94)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

25旅游管理研究生復試面試問題匯總 旅游管理專業知識問題很全! 旅游管理復試全流程攻略 旅游管理考研復試真題匯總

旅游管理復試很難&#xff1f;&#xff01; 別怕&#xff01;經驗超豐富的老學姐來給你們出謀劃策啦&#xff01; 最近是不是被旅游管理考研復試折磨得夠嗆&#xff1f;莫慌&#xff01;我這有著豐富復試指導經驗的老學姐來幫你們排雷&#xff0c;助力大家順利上岸&#xff01…

美的樓宇科技基于阿里云 EMR Serverless Spark 構建 LakeHouse 湖倉數據平臺

作者&#xff1a;美的樓宇科技事業部 先行研究中心智能技術部 美的樓宇科技 IoT 數據平臺建設背景 美的樓宇科技事業部&#xff08;以下簡稱樓宇科技&#xff09;是美的集團旗下五大板塊之一&#xff0c;產品覆蓋多聯機組、大型冷水機組、單元機、機房空調、扶梯、直梯、貨梯…

Html5學習教程,從入門到精通,HTML5 元素語法知識點及案例代碼(2)

HTML5 元素語法知識點及案例代碼 一、HTML5 元素概述 HTML5 元素是構成網頁的基本單位&#xff0c;每個元素都有特定的語義和功能。HTML5 元素由開始標簽、內容和結束標簽組成&#xff0c;例如&#xff1a; <p>這是一個段落。</p><p> 是開始標簽這是一個段…

23種設計模式 - 備忘錄模式

模式定義 備忘錄模式&#xff08;Memento Pattern&#xff09;是一種行為型設計模式&#xff0c;其核心是在不破壞對象封裝性的前提下&#xff0c;捕獲并保存對象的內部狀態&#xff0c;以便后續恢復。該模式特別適用于需要實現撤銷/重做、狀態回滾等功能的系統&#xff0c;如…

2025asp.net全棧技術開發學習路線圖

2025年技術亮點?&#xff1a; Blazor已全面支持WebAssembly 2.0標準 .NET 8版本原生集成AI模型部署能力 Azure Kubernetes服務實現智能自動擴縮容 EF Core新增向量數據庫支持特性 ?ASP.NET 全棧開發關鍵技術說明&#xff08;2025年視角&#xff09;? 以下技術分類基于現…

Linux設備驅動-練習

練習要求&#xff1a; 一、設備樹 1、配置設備樹信息&#xff1a;將3個led燈和1個風扇使用到的設備信息配置到設備樹中 二、設備驅動層 1、通過of_find_node_by_name、of_get_named_gpion等內核核心層統一的api接口調用外設&#xff1b; 2、通過udev設備管理器自動注冊并創建設…

Python應用算法之貪心算法理解和實踐

一、什么是貪心算法&#xff1f; 貪心算法&#xff08;Greedy Algorithm&#xff09;是一種簡單而高效的算法設計思想&#xff0c;其核心思想是&#xff1a;在每一步選擇中&#xff0c;都采取當前狀態下最優的選擇&#xff08;即“局部最優解”&#xff09;&#xff0c;希望通…

競爭與冒險問題【數電速通】

時序邏輯電路&#xff1a; 組合邏輯電路中的競爭與冒險問題&#xff1a; 在組合邏輯電路中&#xff0c;競爭和冒險是兩種常見的時序問題&#xff0c;它們通常由電路的延時特性和不完美的設計引起。下面是這兩種現象的詳細解釋&#xff1a; 1. 競爭&#xff08;Race Condition&…

nasm - BasicWindow_64

文章目錄 nasm - BasicWindow_64概述筆記nasm_main.asmmy_build.batEND nasm - BasicWindow_64 概述 學個demo, 這個demo最主要學到了: 不用在調用每個API前都準備陰影區&#xff0c;在API調用后棧平衡。 可以在函數入口處考慮到所用的棧尺寸最大值(16字節對齊&#xff0c;陰…

JavaScript變量的作用域介紹

JavaScript變量的作用域介紹 JavaScript 變量的作用域決定了變量在代碼中的可訪問性。 var 是 JavaScript 中最早用于聲明變量的關鍵字&#xff0c;它函數作用域或全局作用域。 let 關鍵字&#xff0c;具有塊級作用域、全局作用域。 const關鍵字&#xff0c;具有塊級作用域…

Microsoft 365 Copilot中使用人數最多的是哪些應用

今天在瀏覽Microsoft 365 admin center時發現&#xff0c;copilot會自動整理過去30天內所有用戶使用copilot的概況&#xff1a; 直接把這個圖丟給copilot讓它去分析&#xff0c;結果如下&#xff1a; 總用戶情況 總用戶數在各應用中均為 561 人&#xff0c;說明此次統計的樣本…