日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试

發布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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文件

  • 加入紅框內的代碼

  • 回到源碼樹目錄下來進行模塊編譯,生成pin4driver.ko文件
  • ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
  • 把驅動代碼發送到樹莓派
  • scp pin4test.ko pi@192.168.1.108:/home/pi/work 指令 文件名 樹莓派賬號 IP 文件存放的路徑

    編譯上層測試代碼(pin4test.c)

  • 編譯上層測試代碼
  • arm-linux-gnueabihf-gcc pin4test.c -o pin4test
  • 將生成的可執行文件發送到樹莓派去
  • scp pin4test pi@192.168.1.108:/home/pi/work

    測試驅動代碼

  • 回到樹莓派加載剛剛編譯好的驅動
  • sudo insmod pin4driver.ko
  • 查看dev目錄底下是否生成pin4驅動文件
  • ls /dev/pin4

    或者

    lsmod
  • 給加載好的驅動(driver)分配可執行權限(因為加載過來的驅動只有root用戶具備權限所以要加權限不然打開驅動會失敗)
  • sudo chmod 666 /dev/pin4
  • 運行測試代碼
  • ./pin4test1

    效果展示

    輸入以下指令可查看引腳狀態

    gpio readall

    沒執行 pin4test 測試之前,4號引腳為 IN (輸入)狀態

    執行 pin4test 后4號引腳的狀態變為 OUT (輸出),輸出為 0 (低電平)

    此時我們輸入 1 ,輸出由 0 (低電平)變為 1 (高電平)

    此時我們重新運行 pin4test 測試,輸入 0 ,輸出由 1 (高電平)變為 0 (低電平)

    說明成功了!!!

    卸載驅動

    sudo rmmod pin4driver

    Salute to 老陳!!!

    總結

    以上是生活随笔為你收集整理的11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。