国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

樹莓派BCM2835芯片手冊導讀以及IO口驅(qū)動代碼編寫

這篇具有很好參考價值的文章主要介紹了樹莓派BCM2835芯片手冊導讀以及IO口驅(qū)動代碼編寫。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

驅(qū)動的兩大利器:電路圖(通過電路圖去尋找寄存器)和芯片手冊

樹莓派使用的是BCM2835CPU(博通),芯片手冊做到哪一章就看哪一章。芯片提供了54個IO口,對應了樹莓派的 BCM.

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔
bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

一、樹莓派GPIO口介紹

根據(jù)手冊知gpio在第6章,我們來看第89頁

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

GPFSEL0是pin0 ~ pin9的配置寄存器,GPFSEL1是pin10 ~ pin19的配置寄存器,以此類推,GPFSEL5就是pin50~pin53的配置寄存器。

GPFSEL0

GPIO Function Select 0:功能選擇輸入或輸出

GPSET0

GPIO Pin Output Set 0:輸出0

GPSET1

GPIO Pin Output Set 1:輸出1

GPCLR0

GPIO Pin Output Clear 0:清零

下圖給出第九個引腳的功能選擇示例,對寄存器的29-27進行配置,進而設置相應的功能。 根據(jù)圖片下方的register 0表示0~9使用的是register 0這個寄存器。

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

輸出集寄存器用于設置GPIO管腳。SET{n}字段定義,分別對GPIO引腳進行設置,將“0”寫入字段沒有作用。如果GPIO管腳為在輸入(默認情況下)中使用,那么SET{n}字段中的值將被忽略。然而,如果引腳隨后被定義為輸出,那么位將被設置根據(jù)上次的設置/清除操作。分離集和明確功能取消對讀-修改-寫操作的需要。GPSETn寄存器為了使IO口設置為1,set4位設置第四個引腳,也就是寄存器的第四位

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

輸出清除寄存器用于清除GPIO管腳。CLR{n}字段定義要清除各自的GPIO引腳,向字段寫入“0”沒有作用。如果的在輸入(默認),然后在CLR{n}字段的值是忽略了。然而,如果引腳隨后被定義為輸出,那么位將被定義為輸出根據(jù)上次的設置/清除操作進行設置。分隔集與清函數(shù)消除了讀-修改-寫操作的需要。GPCLRn是清零功能寄存器

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

二、代碼的編寫

編寫驅(qū)動程序時,首先要知道它的地址,IO口空間的起始地址是0x3f00 0000(文檔的起始地址是錯誤的),加上GPIO的偏移量0x200 0000,所以GPIO的物理地址應該是0x3f20 0000開始的,然后在這個基礎上進行Linux系統(tǒng)的MMU內(nèi)存虛擬化管理,映射到虛擬地址上。

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

上圖尾部的偏移量是正確的,根據(jù)gpio的物理地址0x3f200 0000得到

GPFSEL0 0x3f20 0000 //IO口的初始的物理地址,而并不是手冊里面的那個總線地址
GPSET0 0x3f20 001c  //地址通過查找芯片手冊里面的對應的GPSET0 的總線地址的后兩位決定是1c
GPCLR0 0x3f20 0028 //地址是查找GPCLR0在芯片手冊里的總線地址確定的28,所以地址后兩位是28

1.首先在原來的驅(qū)動框架上添加寄存器的定義

volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

volatile關鍵字的作用:確保指令不會因編譯器的優(yōu)化而省略,且要求每次直接讀值,在這里的意思就是確保地址不會被編譯器更換。

2.然后在pin4_drv_init這個函數(shù)里面添加寄存器地址的配置

GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);
GPSET0  = (volatile unsigned int *)ioremap(0x3f20001C,4);
GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028,4);

ioremap將物理地址轉(zhuǎn)換為虛擬地址

我們前面講到了在內(nèi)核里代碼和上層代碼訪問的是虛擬地址(VA),而現(xiàn)在設置的是物理地址,**所以必須把物理地址轉(zhuǎn)換成虛擬地址**

按位與或按位或

  1. 配置引腳4為輸出引腳,為了不影響其他引腳,需要使用與運算或運算。

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

根據(jù)圖片可知14-12bit需配置成001.

31 30 ······14 13 12 11 10 9 8 7 6 5 4 3 2 1 
0  0  ······0  0  1  0  0  0 0 0 0 0 0 0 0 0

 //配置pin4引腳為輸出引腳      bit 12-14  配置成001  
  *GPFSEL0 &= ~(0x6 <<12); // 把bit13 、bit14置為0  
 //0x6是110  <<12左移12位 ~取反 &按位與
  *GPFSEL0 |= (0x1 <<12); //把12置為1   |按位或

4.讓引腳拉高

if(userCmd == 1)
    {
        printk("set 1\n");
        *GPSET0 |= (0x1 << 4); 
       //寫1左移4位是讓寄存器    開啟置1  讓bit4為高電平
    }
    else if(userCmd == 0)
    {
        printk("set 0\n");
        *GPCLR0 |= (0x1 << 4); 
     //寫1左移4位是讓清0寄存器 開啟置0 讓bit4為低電平
    }
    else
    {
        printk("nothing undo\n"); 
    }

補充:ioremap用法

開始映射:void* ioremap(unsigned long phys_addr , unsigned long size , unsigned long flags)
//用map映射一個設備意味著使用戶空間的一段地址關聯(lián)到設備內(nèi)存上,這使得只要程序在分配的地址范圍內(nèi)進行讀取或?qū)懭?,實際上就是對設備的訪問。
phys_addr:要映射的起始的IO地址
size:要映射的空間的大小
flags:要映射的IO空間和權限有關的標志

第二個參數(shù)怎么定?
這個由你的硬件特性決定。
比如,你只是映射一個32位寄存器,那么長度為4就足夠了。
(這里樹莓派IO口功能設置寄存器、IO口設置寄存器都是32位寄存器,所以分配四個字節(jié)就夠了)

比如:GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);
      GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);
      GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);
這三行是設置寄存器的地址,volatile的作用是作為指令關鍵字
確保本條指令不會因編譯器的優(yōu)化而省略,且要求每次直接讀值
ioremap函數(shù)將物理地址轉(zhuǎn)換為虛擬地址,IO口寄存器映射成普通內(nèi)存單元進行訪問。
 
解除映射:void iounmap(void* addr)//取消ioremap所映射的IO地址
比如:
        iounmap(GPFSEL0);
        iounmap(GPSET0);
        iounmap(GPCLR0); //卸載驅(qū)動時釋放地址映射

函數(shù)copy_from_user用法。

 函數(shù)copy_from_user原型:
 copy_from_user(void *to, const void __user *from, unsigned long n)

返回值:失敗返回沒有被拷貝成功的字節(jié)數(shù),成功返回0
參數(shù)詳解:
1. to 將數(shù)據(jù)拷貝到內(nèi)核的地址,即內(nèi)核空間的數(shù)據(jù)目標地址指針
2. from 需要拷貝數(shù)據(jù)的地址,即用戶空間的數(shù)據(jù)源地址指針
3. n 拷貝數(shù)據(jù)的長度(字節(jié))
也就是將@from地址中的數(shù)據(jù)拷貝到@to地址中去,拷貝長度是n

三、代碼整合

驅(qū)動代碼

#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  devise聲明
#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";   //模塊名--這個模塊名到時候是在樹莓派的/dev底下顯示相關驅(qū)動模塊的名字

volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

//volatile關鍵字的作用:確保指令不會因編譯器的優(yōu)化而省略,且要求每次直接讀值,在這里的意思就是確保地址不會被編譯器更換

//led_open函數(shù)
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //內(nèi)核的打印函數(shù)和printf類似    
    
    //由于pin4在 14-12位,所以將14-12位分別置為001即為輸出引腳,所以下面的那兩個步驟分別就是將14,13置為0,12置為1
    *GPFSEL0 &= ~(0x6 << 12); //把13,14位 置為0
    *GPFSEL0 |=  (0x1 << 12); //把12位 置為1 
    
    return 0;
}

//led_write函數(shù)
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    int userCmd;
    int copy_cmd;
    
    printk("pin4_write\\n");
    
    //copy_from_user(void *to, const void __user *from, unsigned long n)
    
    copy_cmd = copy_from_user(&userCmd,buf,count); //函數(shù)的返回值是,如果成功的話返回0,失敗的話就是返回用戶空間的字節(jié)數(shù)
    
    if(copy_cmd != 0)
    {
        printk("fail to copy from user\n");
    }

    if(userCmd == 1)
    {
        printk("set 1\n");
        *GPSET0 |= (0x1 << 4); //這里的1左移4位的目的就是促使寄存器將電平拉高,即變?yōu)镠IGH
    }
    else if(userCmd == 0)
    {
        printk("set 0\n");
        *GPCLR0 |= (0x1 << 4); //這里的1左移4位也是一樣只是為了讓寄存器將電平拉低,即變?yōu)長OW
    }
    else
    {
        printk("nothing undo\n"); 
    }
    
    return 0;
}

static ssize_t pin4_read(struct file *file, const 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)   //設備驅(qū)動初始化函數(shù)(真實的驅(qū)動入口)
{
    int ret;
    
    devno = MKDEV(major,minor);  //創(chuàng)建設備號
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注冊驅(qū)動  告訴內(nèi)核,把這個驅(qū)動加入到內(nèi)核驅(qū)動的鏈表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo"); //這個是讓代碼在/dev目錄底下自動生成設備,自己手動生成也是可以的
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //創(chuàng)建設備文件
    
    //由于以下的地址全是物理地址,所以我們要將物理地址轉(zhuǎn)換成虛擬地址 
    GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4); //由于寄存器是32位的,所以是映射4個字節(jié),一個字節(jié)為8位
    GPSET0  = (volatile unsigned int *)ioremap(0x3f20001c,4);
    GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028,4);
    
    return 0;
}

void __exit pin4_drv_exit(void)  //卸載驅(qū)動,即將驅(qū)動從驅(qū)動鏈表中刪除掉 
{
    iounmap(GPFSEL0);
    iounmap(GPSET0);
    iounmap(GPCLR0);
    
    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸載驅(qū)動
}

module_init(pin4_drv_init);  //真正的入口
module_exit(pin4_drv_exit);  //卸載驅(qū)動
MODULE_LICENSE("GPL v2");    
  

上層代碼

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
        int fd;
        int userCmd;
        
        fd = open("/dev/pin4",O_RDWR);
        
        if(fd < 0)
        {
            printf("fail to open the pin4\n");
            perror("the reason:");
        }
        else
        {
            printf("success to open the pin4\n");
        }
            
        printf("please Input 1-HIGH,0-LOW \n");
        scanf("%d",&userCmd);

        write(fd,&userCmd,4); //這里userCmd是一個整型數(shù),所以寫的是4個字節(jié)

        return 0;
}

如何編譯驅(qū)動代碼并在樹莓派運行

樹莓派初始引腳

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

運行代碼后

bcm2835,驅(qū)動開發(fā),Powered by 金山文檔
bcm2835,驅(qū)動開發(fā),Powered by 金山文檔
bcm2835,驅(qū)動開發(fā),Powered by 金山文檔
bcm2835,驅(qū)動開發(fā),Powered by 金山文檔

學習筆記,僅供參考

優(yōu)秀博客文章來源地址http://www.zghlxwxcb.cn/news/detail-799836.html

到了這里,關于樹莓派BCM2835芯片手冊導讀以及IO口驅(qū)動代碼編寫的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 樹莓派4B采用設備樹(DTS)提供硬件信息,編寫platform驅(qū)動控制io(LED)

    樹莓派4B采用設備樹(DTS)提供硬件信息,編寫platform驅(qū)動控制io(LED)

    設備樹是一種描述硬件資源的數(shù)據(jù)結構,它通過bootloader將硬件資源傳給內(nèi)核,使得內(nèi)核和硬件資源描述相對獨立。 1DT:Device Tree //設備樹 2FDT:Flattened Device Tree //展開設備樹|開放固件,設備樹起源于OF,所以我們在設備樹中可以看到很多有of字母的函數(shù) 3device tree source(dts) //設備

    2024年02月14日
    瀏覽(21)
  • WT2605C藍牙音頻語音芯片:具備大功率IO驅(qū)動能力,引領音頻技術新紀元

    在當今的電子科技時代,功率強大的IO驅(qū)動能力成為音頻設備性能的重要指標。近日,一款名為WT2605C的藍牙音頻語音芯片,以其最高可直接驅(qū)動64mA的大功率IO驅(qū)動能力,引起業(yè)界的廣泛關注。這款芯片的出現(xiàn),無疑將為音頻設備的設計與應用帶來全新的可能性。 WT2605C藍牙音

    2024年01月16日
    瀏覽(22)
  • 博通BCM575系列 RDMA 網(wǎng)卡驅(qū)動 bnxt_re 分析(一)

    博通BCM575系列 RDMA 網(wǎng)卡驅(qū)動 bnxt_re 分析(一)

    整個BCM系列驅(qū)動分成以太網(wǎng)部分(bnxt_en.ko)和RDMA部分(bnxt_re.ko), 兩個模塊之間通過內(nèi)核的auxiliary_bus進行管理.我們主要分析下bnxt_re驅(qū)動. 這個驅(qū)動的核心是 qplib_fp.c, 這個文件主要包含了驅(qū)動的數(shù)據(jù)路徑, 包括Post Send, Post Recv, Poll CQ流程的實現(xiàn). ib_verbs.c主要是實現(xiàn)了上層的Verbs接口

    2024年02月08日
    瀏覽(35)
  • 實時時鐘芯片DS1307的使用及驅(qū)動代碼

    實時時鐘芯片DS1307的使用及驅(qū)動代碼

    DS1307實時時鐘芯片的介紹及驅(qū)動代碼 目錄 一、DS1307是什么? 二、DS1307的功能 三、DS1307的寄存器 四、代碼 1.讀出數(shù)據(jù) 2.寫入數(shù)據(jù) 3.時間初始化設置 4.獲取當前時間 五、注意事項 總結 DS1307是一款基于IIC總線接口的實時時鐘芯片,可以獨立于MCU工作,芯片具有備用電源自動切

    2024年02月05日
    瀏覽(18)
  • 講解STM32驅(qū)動WS281x燈珠的多種實現(xiàn)方式:普通IO、SPI+DMA、以及PWM+DMA驅(qū)動方法

    STM32作為一款高性能、功能豐富的單片機,其豐富的外設和強大的性能,使其在嵌入式領域得到了廣泛的應用。本篇文章將介紹如何利用STM32驅(qū)動WS281x系列的LED燈珠。我們會使用三種不同的驅(qū)動方式進行實現(xiàn):一種是普通IO方式驅(qū)動,一種是SPI+DMA方式驅(qū)動,最后一種是PWM+DMA方

    2024年02月11日
    瀏覽(20)
  • 基于樹莓派實現(xiàn)的IO-Link 項目

    基于樹莓派實現(xiàn)的IO-Link 項目

    ? ?IO-Link 協(xié)議 (IEC 61131-9) 是從傳感器或執(zhí)行器到 IO-Link 主站的串行半雙工點對點連接。目前IO-Link 的硬應已經(jīng)越來越普及。具有IO-Link接口的傳感器和執(zhí)行器技術的廣泛基礎已經(jīng)可用。如今,市場上總共有超過13,000種不同的IO-Link產(chǎn)品。國外產(chǎn)品以巴魯夫為代表。如何開發(fā)

    2024年02月16日
    瀏覽(25)
  • 【數(shù)據(jù)手冊】CH340G芯片使用介紹

    【數(shù)據(jù)手冊】CH340G芯片使用介紹

    CH340是一系列USB總線適配器,它通過USB總線提供串行、并行或IrDA接口。CH340G集成電路提供通用的MODEM信號,允許將UART添加到計算機上,或?qū)F(xiàn)有的UART設備轉(zhuǎn)換為USB接口。 全速USB接口,兼容USB 2.0接口。 使用最小數(shù)量的外部組件:一個晶體管和至少四個電容器。 提供了一個虛

    2024年02月03日
    瀏覽(438)
  • 使用樹莓派Pico、DHT11和SSD1306搭建一個溫度濕度計(只使用官方庫,以及官方案例代碼的錯誤之處和解決方案)

    使用樹莓派Pico、DHT11和SSD1306搭建一個溫度濕度計(只使用官方庫,以及官方案例代碼的錯誤之處和解決方案)

    最近想樹莓派 Pico、DHT11 溫濕度傳感器和 SSD1306 OLED 屏幕做一個溫度濕度計,樹莓派官方案例也分別有這兩個設備的案例,我就想做個簡單的溫度濕度計作為學習微控制器的開始,結果遇到了一個大坑,所以寫本文記錄一下整個過程。 本文最后會實現(xiàn)一個能在 SSD1306 OLED 屏幕上

    2024年02月11日
    瀏覽(20)
  • 【數(shù)據(jù)手冊】LM1117L3芯片的使用

    【數(shù)據(jù)手冊】LM1117L3芯片的使用

    可調(diào)或固定輸出 1A 輸出電流 低損耗,在 1A 輸出電流時最大電壓為 1.3V 0.04% 的線路調(diào)節(jié) 0.2% 負載調(diào)節(jié) 100% 熱極限燃燒 快速瞬態(tài)響應 ???? LM1117系列正可調(diào)和固定調(diào)節(jié)器設計提供1A高電流效率。 所有內(nèi)部電路設計為低至 1.3V 輸入輸出差。 片內(nèi)微調(diào)將參考電壓調(diào)整為 1% 高效線性

    2024年02月08日
    瀏覽(18)
  • W25Q128芯片手冊精讀

    W25Q128芯片手冊精讀

    之前寫SPI通信的時候用到了W25Q128,其中對芯片手冊的閱讀我只寫了我們所需要的的部分。 這篇博客就對這個芯片進行詳細的閱讀并記錄,文章可能會比較長,但絕對是結合了自己的理解進行闡述。 芯片手冊剛開始看的時候最大的攔路虎其實就是英文,看習慣了中文,直接看

    2024年02月07日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包