嵌入式Linux驅動開發基礎-2 LED驅動

imx6ull中GPIO涉及寄存器

1:CCM寄存器

????????GPIOx 要用 CCM_CCGRy 寄存器中的 2 位來決定該組 GPIO 是否使能。哪組 GPIO 用哪個 CCM_CCGR 寄存器來設置。
CCM_CCGR 寄存器中某 2 位的取值含義如下:
00 :該 GPIO 模塊全程被關閉
01 :該 GPIO 模塊在 CPU run mode 情況下是使能的;在 WAIT STOP
模式下,關閉
10 :保留
11 :該 GPIO 模塊全程使能

2:引腳功能寄存器

????????對于某個/ 某組引腳, IOMUXC 中有 2 個寄存器用來設置它: 選擇功能:
a) IOMUXC_SW_ MUX _CTL_ PAD _< PADNAME > Mux pad xxx ,選擇某個 pad 的功能
b) IOMUXC_SW_ MUX _CTL_ GRP _< GROUP NAME > Mux grp xxx ,選擇某組引腳的功能
某個引腳,或是某組預設的引腳,都有 8 個可選的模式 (alternate (ALT) MUX_MODE)。

設置上下拉電阻等參數:
a) IOMUXC_SW_ PAD _CTL_ PAD _< PAD_NAME > pad pad xxx ,設置某個 pad 的參數
b) IOMUXC_SW_ PAD _CTL_ GRP _< GROUP NAME > pad grp xxx ,設置某組引腳的參數

3:GPIO模塊內部

我們暫時只需要關心 3 個寄存器:
1)GPIOx_GDIR :設置引腳方向,每位對應一個引腳, 1-output 0-input

2)GPIOx_DR :設置輸出引腳的電平,每位對應一個引腳, 1- 高電平, 0- 低電平

3)GPIOx_PSR:讀取引腳的電平,每位對應一個引腳,1-高電平,0-低電平

4:總結

1)讀GPIO

設置 CCM_CCGRx 寄存器中某位使能對應的 GPIO 模塊 // 默認是使能 的,
設置 IOMUX 來選擇引腳用于 GPIO
設置 GPIOx_GDIR 中某位為 0 ,把該引腳設置為輸入功能
GPIOx_DR GPIOx_PSR 得到某位的值(讀 GPIOx_DR 返回的是 GPIOx_PSR 的值)

2)寫GPIO

設置 CCM_CCGRx 寄存器中某位使能對應的 GPIO 模塊 // 默認是使能的,
設置 IOMUX 來選擇引腳用于 GPIO
設置 GPIOx_GDIR 中某位為 1 ,把該引腳設置為輸出功能
GPIOx_DR 某位的值

2:代碼編寫

2.1:字符設備驅動程序框架

寫驅動程序的套路:

  1. 確定主設備號,也可以讓內核分配
  2. 定義自己的 file_operations 結構體
  3. 實現對應的 drv_open/drv_read/drv_write 等函數,填入 file_operations 結構體
  4. file_operations 結構體告訴內核:register_chrdev
  5. 誰來注冊驅動程序啊?得有一個入口函數:安裝驅動程序時,就會去調用這個入口函數
  6. 有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用unregister_chrdev
  7. 其他完善:提供設備信息,自動創建設備節點:class_create,device_create

驅動怎么操作硬件?

????????? 通過 ioremap 映射寄存器的物理地址得到虛擬地址,讀寫虛擬地址。

驅動怎么和 APP 傳輸數據?

????????? 通過 copy_to_usercopy_from_user 2 個函數

2.2:代碼邏輯圖

2.2:實現功能

先編寫驅動程序:
? 實現 led_open 函數,在里面初始化 LED 引腳。
? 實現 led_write 函數,在里面根據 APP 傳來的值控制 LED
? 再編寫測試程序。

2.2.1:確定主設備號。

? ? ? ? /* 1. 確定主設備號*/

static int major = 0; ? ? ??

? ? ? ? //后續在入口函數中,使用下列代碼:注冊函數進行分配

major = register_chrdev(0, "hello", &hello_drv);

2.2.2:定義自己的?file_operations?結構體

static struct file_operations led_fops = {

? ? .owner ? ? ?= THIS_MODULE,

? ? .write ? ? ?= led_write,

? ? .open ? ? ? = led_open,

};

2.2.3:實現對應的?drv_open/drv_read/drv_write?等函數,??

注:我們現在編寫的函數屬于驅動函數,那么我們在串口掉用這些函數時,

? ? ? ? 串口那邊,我們屬于APP

? ? ? ? 而這些驅動屬于 “內核”

? ? ? ? 我們串口調用read函數 ?len = read(fd, buf, 1024);時,我們是需要把kernel_buf的數據讀到buf中。所以在這read代碼中,我們使用 ?err = copy_to_user(buf, kernel_buf, MIN(1024, size));這行代碼,將kernel_buf寫入到buf中。

? ? ? ? 我們串口調用write函數 write(fd, argv[2], len);時,是把argv[2]的數據寫入到kernel_buf中。所以在這write代碼中,我們使用 ?err = copy_from_user(kernel_buf, buf, MIN(1024, size));這行代碼,將buf也就是argv[2]寫入到kernel_buf中。

注2:驅動怎么操作硬件?

????????? 通過 ioremap 映射寄存器的物理地址得到虛擬地址,讀寫虛擬地址。

static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

static volatile unsigned int *GPIO5_GDIR;

static volatile unsigned int *GPIO5_DR;

????????這是我們需要操作的三個寄存器,IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3在open函數中,將PIN配置為GPIO模式,GPIO5_GDIR在open函數中被配置為輸出模式,GPIO5_DR根據用戶傳輸的數據來點亮或者熄滅LED。

????????因為驅動不能直接訪問硬件地址,所以我們在后續入口函數中,會使用ioremap函數將物理地址映射至三個定義中,這樣后續對三個定義的操作就類似于直接對物理地址進行操作。

/* registers */

// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14

static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

// GPIO5_GDIR 地址:0x020AC004

static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR 地址:0x020AC000

static volatile unsigned int *GPIO5_DR;

static ssize_t led_write(struct file *filp, const char __user *buf,

? ? ? ? ? ? ?size_t count, loff_t *ppos)

{

? ? char val;

? ? int ret;? ?

? ? /* copy_from_user : get data from app */

? ? ret = copy_from_user(&val, buf, 1);

? ? /* to set gpio register: out 1/0 */

? ? if (val)

? ? {

? ? ? ? /* set gpio to let led on */

? ? ? ? *GPIO5_DR &= ~(1<<3);

? ? }

? ? else

? ? {

? ? ? ? /* set gpio to let led off */

? ? ? ? *GPIO5_DR |= (1<<3);

? ? }

? ? return 1;

}

static int led_open(struct inode *inode, struct file *filp)

{

? ? /* enable gpio5

? ? ?* configure gpio5_io3 as gpio

? ? ?* configure gpio5_io3 as output

? ? ?*/

? ? *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;

? ? *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;

? ? *GPIO5_GDIR |= (1<<3);

? ? return 0;

}

2.2.4:把 file_operations 結構體告訴內核:register_chrdev
2.2.5. 誰來注冊驅動程序啊?得有一個入口函數:?

?????????3.4和3.5可同時在入口函數中實現,安裝驅動程序時,系統去調用這個入口函數,這是直接在入口函數中直接將結構體告訴內核.

????????并且在這里我們還需要將物理地址進行映射,這樣后續open和write可以直接對硬件進行操作

/* 入口函數 */

static int __init led_init(void)

{

? ? printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

? ?

? ? major = register_chrdev(0, "100ask_led", &led_fops);

? ? /* ioremap */

? ? // IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14

? ? IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);

? ?

? ? // GPIO5_GDIR 地址:0x020AC004

? ? GPIO5_GDIR = ioremap(0x020AC004, 4);

? ?

? ? //GPIO5_DR 地址:0x020AC000

? ? GPIO5_DR ?= ioremap(0x020AC000, 4);

? ? led_class = class_create(THIS_MODULE, "myled");

? ? device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */

? ?

? ? return 0;

}

2.2.6:有入口函數就應該有出口函數:卸載驅動程序時,出口函數調用unregister_chrdev

將映射地址也進行消除

static void __exit led_exit(void)

{

? ? iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);

? ? iounmap(GPIO5_GDIR);

? ? iounmap(GPIO5_DR);

? ?

? ? device_destroy(led_class, MKDEV(major, 0));

? ? class_destroy(led_class);

? ?

? ? unregister_chrdev(major, "100ask_led");

}

3.1.7:其他完善:提供設備信息,

自動創建設備節點:class_create, device_creat

/* 7. 其他完善:提供設備信息,自動創建設備節點 */

module_init(hello_init);

module_exit(hello_exit);

MODULE_LICENSE("GPL");

2.3:全部代碼

2.3.1:led_drv.c


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>static int major;
static struct class *led_class;/* registers */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{char val;int ret;/* copy_from_user : get data from app */ret = copy_from_user(&val, buf, 1);/* to set gpio register: out 1/0 */if (val){/* set gpio to let led on */*GPIO5_DR &= ~(1<<3);}else{/* set gpio to let led off */*GPIO5_DR |= (1<<3);}return 1;
}static int led_open(struct inode *inode, struct file *filp)
{/* enable gpio5* configure gpio5_io3 as gpio* configure gpio5_io3 as output */*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;*GPIO5_GDIR |= (1<<3);return 0;
}static struct file_operations led_fops = {.owner		= THIS_MODULE,.write		= led_write,.open		= led_open,
};/* 入口函數 */
static int __init led_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_fops);/* ioremap */// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);// GPIO5_GDIR 地址:0x020AC004GPIO5_GDIR = ioremap(0x020AC004, 4);//GPIO5_DR 地址:0x020AC000GPIO5_DR  = ioremap(0x020AC000, 4);led_class = class_create(THIS_MODULE, "myled");device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */return 0;
}static void __exit led_exit(void)
{iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);iounmap(GPIO5_GDIR);iounmap(GPIO5_DR);device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, "100ask_led");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

2.3.2 led_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>// ledtest /dev/myled on
// ledtest /dev/myled offint main(int argc, char **argv)
{int fd;char status = 0;if (argc != 3){printf("Usage: %s <dev> <on|off>\n", argv[0]);printf("  eg: %s /dev/myled on\n", argv[0]);printf("  eg: %s /dev/myled off\n", argv[0]);return -1;}// openfd = open(argv[1], O_RDWR);if (fd < 0){printf("can not open %s\n", argv[0]);return -1;}// writeif (strcmp(argv[2], "on") == 0){status = 1;}write(fd, &status, 1);return 0;	
}

2.3.3:Makefile


# 1. 使用不同的開發板內核時, 一定要修改KERN_DIR
# 2. KERN_DIR中的內核要事先配置、編譯, 為了能編譯內核, 要先設置下列環境變量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的開發板不同的編譯器上述3個環境變量不一定相同,
#       請參考各開發板的高級用戶使用手冊KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f ledtestobj-m	+= led_drv.o

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

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

相關文章

深度解析】使用Go語言實現JWT:從原理到實踐

JWT&#xff08;JSON Web Token&#xff09;已成為現代Web應用中身份驗證的基石。本文深入剖析如何用Go語言實現JWT&#xff0c;從基礎概念、底層機制到完整代碼實踐&#xff0c;助你全面掌握。 一、JWT概述 JWT是一種開放標準&#xff08;RFC 7519&#xff09;&#xff0c;用…

深入解讀 DeepSeek-V3 架構及落地的挑戰

從多專家架構&#xff08;MoE&#xff09;到模型落地實戰的一線觀察 一、引言&#xff1a;DeepSeek-V3 是什么&#xff1f; 在大模型百花齊放的今天&#xff0c;DeepSeek-V3 作為 DeepSeek 系列的第三代開源模型&#xff0c;不僅延續了高質量對話能力&#xff0c;還在架構上邁…

前端進階之路-從傳統前端到VUE-JS(第二期-VUE-JS框架結構分析)

經過上期內容的學習&#xff0c;我們已經可以構建一個VUE-CLI框架了&#xff0c;接下來我們分析一下這個框架&#xff0c;畢竟知己知彼&#xff0c;百戰百勝 我們創建完成后可以看到以下內容 接下來我們分析一下他的文件結構 node_modules用于存放項目所依賴的第三方模塊和包…

網絡協議 / 加密 / 簽名總結

加密方式&#xff1a; 對稱加密&#xff1a;key 不可公開。 非對稱加密&#xff1a;公鑰加密的信息只有私鑰能解密。私鑰加密的信息只有公鑰能解密&#xff0c;且公鑰只能解密私鑰加密的信息&#xff08;用于簽名&#xff09;。 非對稱加密應用&#xff1a; 簽名&#xff1a…

集成學習基礎:Bagging 原理與應用

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; Bagging 介紹 1. 定義與全稱&#xff1a; Bagging 是 Bootstrap Agg…

skiaSharp linux 上報錯

The type initializer for SkiaSharp.SKImageInfo threw an exception 這個錯誤表明在 Linux 系統上初始化 SkiaSharp 的 SKImageInfo 類型時出現了問題。以下是完整的解決方案&#xff1a; 安裝系統依賴&#xff1a; # Ubuntu/Debian sudo apt-get update sudo apt-get ins…

crawl4ai crawler.arun( 超時問題

delay_before_return_html500 # 單位&#xff1a;毫秒 會導致 crawler.arun 超時問題。按理說不應該 await crawler.arun( 1. 瀏覽器加載頁面 ? 2. 頁面DOM構建完成 ? 3. JavaScript執行完成 ? 4. 等待 delay_before_return_html 時間 ? (500ms) 5. 返回最終HTML內容 &…

Linux Kernel下exFat使用fallocate函數不生效問題

1&#xff09;Linux驅動開發相關問題&#xff0c;分享給將要學習或者正在學習Linux驅動開發的同學。 2&#xff09;內容屬于原創&#xff0c;若轉載&#xff0c;請說明出處。 3&#xff09;提供相關問題有償答疑和支持。 Linux下經常使用fallocate去預分配一個很大的文件空間…

大學專業科普 | 物聯網、自動化和人工智能

在選擇大學專業時&#xff0c;可以先從自身興趣、能力和職業規劃出發&#xff0c;初步確定幾個感興趣的領域。然后結合外部環境因素&#xff0c;如專業前景、教育資源和就業情況等&#xff0c;對這些專業進行深入的分析和比較。 物聯網專業 課程設置 基礎課程&#xff1a;包括…

人工智能-基礎篇-7-什么是大語言模型LLM(NLP重要分支、Transformer架構、預訓練和微調等)

大型語言模型&#xff08;Large Language Model&#xff09;。這類模型是自然語言處理&#xff08;NLP&#xff09;領域的一個重要分支&#xff0c;它們通過在大量文本數據上進行訓練來學習語言的結構和模式&#xff0c;并能夠生成高質量的文本、回答問題、完成翻譯任務等。 1…

【趙渝強老師】基于PostgreSQL的分布式數據庫:Citus

由于PostgreSQL具有強大的功能和良好的可擴展性&#xff0c;因此基于PostgreSQL很容易就可以實現分布式架構。Citus便是具體的一種實現方式。它以擴展的插件形式與PostgreSQL進行集成&#xff0c;且獨立于PostgreSQL內核&#xff0c;部署也比較簡單。Citus是現在非常流行的基于…

【趙渝強老師】OceanBase OBServer節點的接入層

OceanBase數據庫代理ODP&#xff08;OceanBase Database Proxy&#xff0c;又稱OBProxy&#xff09;是OceanBase數據庫的接入層&#xff0c;負責將用戶的請求轉發到合適的OceanBase數據庫實例上進行處理。ODP是獨立的進程實例&#xff0c;獨立于OceanBase數據庫實例部署。ODP監…

ISP Pipeline(8): Color Space Conversion 顏色空間轉換

Color Space Conversion&#xff08;顏色空間轉換&#xff09; 是圖像處理中的一個重要步驟&#xff0c;它將圖像從一個顏色空間&#xff08;Color Space&#xff09;轉換到另一個&#xff0c;以滿足 顯示、分析、壓縮或算法需求。 為什么轉換顏色空間&#xff1f; 應用場景…

Spring Web MVC ①

&#x1f680; 一、Spring MVC MVC三層 Controller&#xff1a;樂團指揮&#xff0c;接收請求→調用模型→選擇視圖&#xff08;Controller&#xff09; Model&#xff1a;樂手&#xff0c;處理業務邏輯與數據&#xff08;POJO對象&#xff09; View&#xff1a;舞臺展示&…

【數據挖掘】貝葉斯分類學習—NaiveBayes

NaiveBayes 樸素貝葉斯的核心是貝葉斯定理&#xff0c;它描述了如何根據新證據更新事件的概率。 要求&#xff1a; 1、實現樸素貝葉斯分類算法&#xff0c;驗證算法的正確性&#xff0c;并將算法應用于給定的數據集Data_User_Modeling數據集&#xff0c;選擇一部分數據集作為已…

Java面試寶典:基礎二

&#x1f512; 25. final vs abstract 關鍵字 關鍵字修飾對象作用規則final類禁止被繼承final class MyClass { ... }方法禁止被子類重寫public final void func()變量變為常量&#xff08;基本類型值不可變&#xff0c;引用類型地址不可變&#xff09;final int MAX 100;abs…

小米手機安裝charles證書

使用紅米手機下載Charles證書一直下載中&#xff0c;無法正常下載。 不使用原裝瀏覽器&#xff0c;使用第三方瀏覽器下載就可以了。 使用第三方瀏覽器安裝&#xff0c;如我使用的是UC瀏覽器 使用第三方瀏覽器安裝的證書格式是".pem"格式問卷 將這個文件放入小米的dow…

DeepSeek R2 推遲發布:因 H20 算力短缺

DeepSeek 今年早些時候憑借其 R1 AI 模型備受廣泛關注。據《The Information》報道&#xff0c;R2 模型的工作似乎因 H20 處理器而停滯不前。 DeepSeek尚未透露其R2 模型的具體上市時間。 DeepSeek 使用 5 萬塊 Hopper GPU&#xff08;包括 3 萬塊 H20、1 萬塊 H800 和 1 萬塊…

智能之火,重塑創造:大模型如何點燃新一代開發引擎?

導言&#xff1a;普羅米修斯之火再現 在科技演進的長河中&#xff0c;每一次生產力的躍遷都伴隨著工具的質變。從蒸汽機轟鳴到電力普及&#xff0c;再到信息高速公路的鋪就&#xff0c;人類駕馭能量的能力不斷突破。今天&#xff0c;我們站在一個嶄新的臨界點上&#xff1a;大語…

一文入門JS

轉自個人博客 因為本人經常使用QML&#xff0c;而由于QML與JS之間的關系&#xff0c;本人經常使用到JS相關語法&#xff0c;所以在此系統性對JS基礎知識進行總結、記錄。 1. 入門 JavaScript&#xff08;簡稱 JS&#xff09;是一種廣泛應用于Web開發的腳本語言&#xff0c;它…