11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试
11.樹莓派博通BCM2835芯片手冊導讀與IO口驅動代碼調試和測試
硬件地址的相關概念
總線地址
32位的操作系統 ,cpu最多只能訪問2^32bit,即只能訪問4G的內存
64位的操作系統 ,cpu最多只能訪問2^64bit,即只能訪問8G的內存
物理地址
物理地址又叫硬件的實際地址或絕對地址
虛擬地址
是邏輯地址,有了虛擬地址cpu訪問的內存可完全大于8g。程序大都跑在由物理地址映射成的邏輯地址上。
閱讀芯片手冊
由于我們要進行引腳驅動的開發,所以我們直接查看第六章的內容。
查看樹莓派cpu型號
要編寫對io口進行操控,我們首先需要去閱讀芯片手冊,我使用的是樹莓派 3B,所以查看的手冊是BCM2835,查看cpu型號可以用這個指令來查看:
cat /proc/cpuinfo樹莓派寄存器的介紹
GPFSEL0 GPIO Function Select 0:功能選擇 輸入/輸出
GPSET0 GPIO Pin Output Set 0:輸出0
GPSET1 GPIO Pin Output Set 1:輸出1
0 = No effect
1 = Set GPIO pin n
GPCLR0 GPIO Pin Output Clear 0:清零
0 = No effect
1 = Clear GPIO pin n
GPCLR1 GPIO Pin Output Clear 1:清1
每個寄存器都是32位的
例如:我們把引腳4配置位輸出引腳
FSEL4 14-12 001 我們把4引腳的14-12配置成001 GPIO Pin 4 is an output
注意:我們配置的底層引腳對應得是BCM
寄存器第0組位FESL0–9
寄存器第1組位FSEL10–19
以此類推…
具體的引腳也可通過官方手冊查找
https://pinout.xyz/pinout/pin7_gpio4
寄存器的地址問題
我們在編寫驅動程序的時候,IO空間的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址應該是從0x3f200000開始的,然后在這個基礎上進行Linux系統的MMU內存虛擬化管理,映射到虛擬地址上。
該圖的尾部偏移是對的根據GPIO的物理地址0x3f200000可以知道:
GPFSEL0:0x3f200000
GPSET0: 0x3f20001c
GPCLR0: 0x3f200028
這里我們得到的是物理地址是不可操作的,我們需要轉化成虛擬地址,通過 ioremap() 函數:
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);ioremap宏定義在asm/io.h內:#define ioremap(cookie,size) __ioremap(cookie,size,0)phys_addr:要映射的起始的IO地址
size:要映射的空間的大小
flags:要映射的IO空間和權限有關的標志
該函數返回映射后的內核虛擬地址(3G-4G)。接著便可以通過讀寫該返回的內核虛擬地址去訪問之這段I/O內存資源。
輸出配置
這張圖片表示有GPSET0和GPSET1兩個寄存器,其中GPSET0表示操作0-31引腳,將對應引腳設置成1,可置高電平,設置成0,表示沒有效果。
清零配置
這張圖片表示有GPCLR0和GPCLR1兩個寄存器,其中GPCLR0表示操作0-31引腳,將對應引腳設置成1,可置低電平。設置成0,表示沒有效果。
底層驅動代碼及上層測試代碼
底層驅動代碼
#include <linux/fs.h> // file_operations聲明 #include <linux/module.h> // module_init module_exit聲明 #include <linux/init.h> // __init __exit 宏定義聲明 #include <linux/device.h> // class device聲明 #include <linux/uaccess.h> // copy_from_user的頭文件 #include <linux/types.h> // 設備號 dev_t 類型聲明 #include <asm/io.h> // ioremap iounmap 的頭文件static struct class *pin4_class; static struct device *pin4_class_dev;static dev_t devno; // 設備號 static int major = 231; // 主設備號 static int minor = 0; //次設備號 static char *module_name = "pin4"; //模塊名volatile unsigned int *GPFSEL0 = NULL; //volatile:不會因編譯器的優化而省略,每次直接讀值 volatile unsigned int *GPSET0 = NULL; //unsigned:將數字類型無符號化 volatile unsigned int *GPCLR0 = NULL;//pin4_open函數 static int pin4_open(struct inode *inode, struct file *file) {printk("pin4_open\n"); // 內核的打印函數,和printf類似//14~12位 設置為001*GPFSEL0 &=~(0x6<<12); //14,13位設置為00*GPFSEL0 |= 0x1<<12; //12位設置為1return 0; }//open_write函數 static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {int userCmd;printk("pin4_write\n");copy_from_user(&userCmd,buf,count);if(userCmd==1){*GPSET0 |= 0x1<<4;}if(userCmd==0){*GPCLR0 |= 0x1<<4;}else{printk("undo!\n");}return 0; }static int pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {printk("pin4_read\n");return 0; }static struct file_operations pin4_fops = {.owner = THIS_MODULE,.open = pin4_open,.write = pin4_write,.read = pin4_read, };int __init pin4_drv_init(void) //真實驅動入口 {int ret;devno = MKDEV(major,minor); //2. 創建設備號ret = register_chrdev(major , module_name , &pin4_fops); //3.注冊驅動,告訴內核,把這個驅動加入到內核的鏈表中pin4_class = class_create( THIS_MODULE, "myfirstdemo" ); // 讓代碼在dev自動生成設備pin4_class_dev = device_create( pin4_class , NULL , devno , NULL , module_name ); //創建設備文件GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4); //第一個參數真正的物理地址,第二個參數映射的大小 一個寄存器4個字節 4*8=32bit//32位計算機中,一個字長等于32位,一個字節是8位,所以從長度來說一個字長等于4個字節GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4);//由于返回值是void*型需要強制轉換 void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);//ioremap作用是:物理地址轉換成虛擬地址return 0; }void __exit pin4_drv_exit(void) {iounmap(GPFSEL0);iounmap(GPSET0);iounmap(GPCLR0);device_destroy(pin4_class,devno);class_destroy(pin4_class);unregister_chrdev( major, module_name); }module_init(pin4_drv_init); module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2"); #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h>int main() {int fd;int cmd;fd=open("/dev/pin4",O_RDWR);if(fd<0){printf("open failed!\n");perror("why");}else{printf("open successfully!\n");}printf("input commnd : 1/0 \n1: pin4 high\n0: pin4 low\n");scanf("%d",&cmd);if(cmd==1){write(fd,&cmd,sizeof(int)); //第二個參數是內容 是指針類型的參數;第三個參數是文件大小如果文件是char類型,則為1。如果是int類型,則為sizeof(int)}else if(cmd==0){write(fd,&cmd,sizeof(int));}else{printf("cmd is not good!\n");} }編譯驅動代碼(pin4driver.c)
把寫好的驅動代碼(driver2.c)放到源碼樹目錄的 /drivers/char 目錄下
修改Makefile文件
加入紅框內的代碼
編譯上層測試代碼(pin4test.c)
測試驅動代碼
或者
lsmod效果展示
輸入以下指令可查看引腳狀態
gpio readall沒執行 pin4test 測試之前,4號引腳為 IN (輸入)狀態
執行 pin4test 后4號引腳的狀態變為 OUT (輸出),輸出為 0 (低電平)
此時我們輸入 1 ,輸出由 0 (低電平)變為 1 (高電平)
此時我們重新運行 pin4test 測試,輸入 0 ,輸出由 1 (高電平)變為 0 (低電平)
說明成功了!!!
卸載驅動
sudo rmmod pin4driverSalute to 老陳!!!
總結
以上是生活随笔為你收集整理的11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2012文件共享服务器权限,局域网共享设
- 下一篇: keep-alive失效原因及解决方案