久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    linux驅動開發(一)

     水波浪 2019-06-11

    前言

             GPIO驅動是Linux驅動開發中最基礎、但卻是很常用、很重要的驅動。比如你要點亮一個LED燈、鍵盤掃描、輸出高低電平等等。而Linux內核的強大之處在于對最底層的GPIO硬件操作層的基礎上封裝了一些統一的GPIO操作接口,也就是所謂的GPIO驅動框架。這樣開發人員可以調用這些接口去操作設備的IO口,不需要擔心硬件平臺的不同導致IO口的不同。

             今天,我主要講的就是如何使用Linux內核封裝好的GPIO接口函數在驅動開發中需要操作GPIO時候的使用。

              文章部分內容參考他人博客,特此聲明!

    概述

           GPIO是與硬件體系密切相關的,linux提供一個模型來讓驅動統一處理GPIO,即各個板卡都有實現自己的gpio_chip控制模塊:request, free, input,output, get,set,irq...然后把控制模塊注冊到內核中,這時會改變全局gpio數組:gpio_desc[]. 當用戶請求gpio時,就會到這個數組中找到,并調用這個GPIO對應的gpio_chip的處理函數。gpio實現為一組可用的 gpio_chip, 由驅動傳入對應 gpio的全局序號去 request, dataout ,datain, free. 這時會調用gpio_chip中具體的實現。

            GPIO是一組可控件的腳,由多個寄存器同時控制。通過設置對應的寄存器可以達到設置GPIO口對應狀態與功能。數據狀態,輸入輸出方向,清零,中斷(哪個邊沿觸發), 一般是一組(bank)一組的。寄存器讀寫函數: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()。

    1. Linux內核中GPIO模型的結構

    1.1 struct gpio_desc

    //表示一個gpio口,含對應的gpio_chip.
    //對于每一個gpio,都有一個gpio描述符,這個描述符包含了這個gpio所屬的控制器即chip和一些標志,label等。

    1. struct gpio_desc {
    2. struct gpio_chip *chip;
    3. unsigned long flags;
    4. /* flag symbols are bit numbers */
    5. #define FLAG_REQUESTED 0
    6. #define FLAG_IS_OUT 1
    7. #define FLAG_EXPORT 2 /* protected by sysfs_lock */
    8. #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
    9. #define FLAG_TRIG_FALL 4 /* trigger on falling edge */
    10. #define FLAG_TRIG_RISE 5 /* trigger on rising edge */
    11. #define FLAG_ACTIVE_LOW 6 /* value has active low */
    12. #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
    13. #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
    14. #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
    15. #define ID_SHIFT 16 /* add new flags before this one */
    16. #define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
    17. #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
    18. #ifdef CONFIG_DEBUG_FS
    19. const char *label;
    20. #endif
    21. };
    22. //采用了一個具有ARCH_NR_GPIOS大小的gpio描述符數組。這個描述符數組便代表了系統所有的gpio。
    static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

    1.2  struct davinci_gpio_controller

    //一組GPIO控制器結構,例如GPIO0和GPIO1是一組(共32個GPIO口),共用一組寄存器,所以GPIO0和GPIO1荷載一起用chips[0]來控制
    //假如有144個GPIO,分為4組(GPIO0~GPIO8),每組有2個banks(即GPIO0和GPIO1為1組),每組最多可以有32個GPIO,每組的控制寄存器空間有10個

    1. struct davinci_gpio_controller {
    2. struct gpio_chip chip;//每組對應的gpio_chip
    3. int irq_base;//每組對應的中斷
    4. spinlock_t lock;//自旋鎖
    5. void __iomem *regs;//每組的寄存器地址
    6. void __iomem *set_data;//設置數據寄存器地址
    7. void __iomem *clr_data;//清除數據寄存器地址
    8. void __iomem *in_data;//輸入數據寄存器地址
    9. };


    1.3 struct gpio_chip

    //每一個davinci_gpio_controller結構都對應于一個gpio_chip結構,gpio_chip既可看成是davinci_gpio_controller結構的補充
    //表示一個gpio controller.通過這個結構抽象化所有的GPIO源,而讓板上其它的模塊可以用相同的接口調用使用這些GPIO。

    1. struct gpio_chip {
    2. const char *label;
    3. struct device *dev;
    4. struct module *owner;
    5. int (*request)(struct gpio_chip *chip,unsigned offset);//請求gpio
    6. void *free)(struct gpio_chip *chip,unsigned offset);//釋放gpio
    7. int (*get_direction)(struct gpio_chip *chip,unsigned offset);
    8. int (*direction_input)(struct gpio_chip *chip,unsigned offset);//配置gpio為輸入,返回當前gpio狀態
    9. int (*get)(struct gpio_chip *chip,unsigned offset);//獲取gpio的狀態
    10. int (*direction_output)(struct gpio_chip *chip,unsigned offset, int value);//配置gpio為輸出,并設置為value
    11. int (*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce);//設置消抖動時間,尤其是gpio按鍵時有用
    12. void (*set)(struct gpio_chip *chip,unsigned offset, int value);//設置gpio為value值
    13. int (*to_irq)(struct gpio_chip *chip,unsigned offset);//把gpio號轉換為中斷號
    14. void (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);
    15. int base;// 這個gpio控制器的gpio開始編號
    16. u16 ngpio;//這個gpio控制器說控制的gpio數
    17. const char *const *names;
    18. unsigned can_sleep:1;
    19. unsigned exported:1;
    20. #if defined(CONFIG_OF_GPIO)
    21. struct device_node *of_node;
    22. int of_gpio_n_cells;
    23. int (*of_xlate)(struct gpio_chip *gc,const struct of_phandle_args *gpiospec, u32 *flags);
    24. #endif
    25. #ifdef CONFIG_PINCTRL
    26. struct list_head pin_ranges;
    27. #endif
    28. };


    1.4 struct davinci_gpio_regs

    //GPIO寄存器結構

    1. struct davinci_gpio_regs {
    2. u32 dir; // gpio方向設置寄存器
    3. u32 out_data; // gpio設置為輸出時,表示輸出狀態(0或1)
    4. u32 set_data; // gpio設置為輸出時,用于輸出高電平
    5. u32 clr_data; // gpio設置為輸出時,用于輸出低電平
    6. u32 in_data; // gpio設置為輸入時,用于讀取輸入值
    7. u32 set_rising; // gpio中斷上升沿觸發設置
    8. u32 clr_rising; // gpio中斷上升沿觸發清除
    9. u32 set_falling; // gpio中斷下降沿觸發設置
    10. u32 clr_falling; // gpio中斷下降沿觸發清除
    11. u32 intstat; // gpio中斷狀態位,由硬件設置,可讀取,寫1時清除。
    12. };


    1.5 struct gpio

    struct gpio {

        unsigned gpio;//gpio號

        unsigned long flags;//gpio標志

        const char *label;//gpio名

    };

    2.  驅動開發中GPIO初始化操作

    在實際的驅動開發中,根據板級資源和CPU手冊,GPIO初始化一般需要以下三個步驟:

           1.設置IO口的復用模式,如果某個IO當作GPIO使用,那么就需要根據CPU手冊去配置iomux(IO復用寄存器)為GPIO模式;

            2.設置IO口的輸入輸出方向,根據實際開發需求,將相應的GPIO配置為相應的輸入輸出方向;

            3.GPIO初始化賦值(輸出高低電平)、拉高拉低操作;

    2.1 GPIO申請

    #########################################

    #description:申請一個GPIO資源

    #unsigned gpio:要申請的GPIO管腳號,為一個正整數

    # const char *label:為申請的GPIO管腳取個名字                 

    #########################################           

    int gpio_request(unsigned gpio, const char *label);

    int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);

    2.2 GPIO輸入輸出設置

    #########################################

    #description:設置某個GPIO的輸入輸出方向

    #unsigned gpio:要設置的GPIO管腳號,為一個正整數

    # int value:設置的值                

    #########################################           

    int gpio_direction_input(unsigned gpio);
     int gpio_direction_output(unsigned gpio, int value);

    2.3 獲取GPIO管腳的值和設置GPIO管腳的值

    #########################################

    #description:獲取、設置某個GPIO的值

    #unsigned gpio:要獲取、設置的GPIO管腳號

    # int value:設置的值                

    #########################################

    int gpio_get_value(unsigned gpio);
    void gpio_set_value(unsigned gpio, int value);

    2.4 GPIO當作中斷口使用

    #########################################

    #description:設置某個GPIO為中斷口

    #unsigned gpio:要設置中斷的GPIO管腳號            

    #########################################

    int gpio_to_irq(unsigned gpio);

    返回的值即中斷編號可以傳給request_irq()和free_irq(),內核通過調用該函數將gpio端口轉換為中斷,在用戶空間也有類似方法。

    3. GPIO驅動實例

    3.1 以下GPIO驅動例子為矩陣鍵盤中對GPIO的操作

    //設置某個管腳為輸入

    1. int set_key_input(unsigned int gpio)
    2. {
    3. char name[32];
    4. sprintf(name, "GPIO%d", gpio);
    5. if(gpio_request(gpio,NULL) != 0)
    6. {
    7. printk("gpio request error!\n");
    8. return -1;
    9. }
    10. gpio_direction_input(gpio);
    11. gpio_free(gpio);
    12. return 0;
    13. }

    //設置某個管腳為輸出

    1. int set_key_output(unsigned int gpio,int value)
    2. {
    3. char name[32];
    4. sprintf(name, "GPIO%d", gpio);
    5. if(gpio_request(gpio,NULL) != 0)
    6. {
    7. printk("gpio request error!\n");
    8. return -1;
    9. }
    10. gpio_direction_output(gpio,value);
    11. gpio_free(gpio);
    12. return 0;
    13. }


    //獲取某個GPIO管腳的值

    1. int get_key_value(unsigned int gpio)
    2. {
    3. int value= -1;
    4. if (gpio_request(gpio, NULL) != 0)
    5. {
    6. printk("get_key_value err\n");
    7. return -1;
    8. }
    9. value = gpio_get_value(gpio);
    10. gpio_free(gpio);
    11. return value;
    12. }


    //設置某個GPIO輸出為低電平

    1. int set_key_low(unsigned int gpio)
    2. {
    3. if (gpio_request(gpio, NULL) != 0) {
    4. //printk("set_key_low request err\n");
    5. return -1;
    6. }
    7. gpio_direction_output(gpio, 0);
    8. __gpio_set_value(gpio, 0);
    9. gpio_free(gpio);
    10. return 0;
    11. }

    //拉高、拉低某個GPIO

    //GPIO的拉高拉低操作內核沒有提供通用的接口函數,這個需要驅動開發人員根據CPU手冊的寄存器配置去封裝拉高拉低函數,以下給出一個偽代碼的例子:

    //假設拉高GPIO1_IO01這個IO:

    1. <span style="font-size:12px;">#define SET_PULL_UP 0x01
    2. #define SET_PULL_DOWN 0x00
    3. #define REG_GPIO_BASE 0x8e000000
    4. #define GPIO1_IO01_OFFSET 0x400
    5. int set_pull_up(unsigned int reg_base,unsigned int offset,int up)
    6. {
    7. unsigned int gpio_base;
    8. unsigned int gpio;
    9. gpio_base = ioreamap(reg_base,SIZE_4K);//調用ioreamap映射GPIO空間到內存,映射大小根據實際需求而定
    10. gpio = gpio_base + offset;
    11. gpio |= up; //將某個GPIO拉高,根據具體的寄存器操作而定
    12. __raw_writel(gpio,gpio_base + offset);
    13. return 0;
    14. }
    15. set_pull_up(REG_GPIO_BASE,GPIO1_IO01_OFFSET,SET_PULL_UP);</span>


    //拉低某個GPIO

    1. #define SET_PULL_UP 0x01
    2. #define SET_PULL_DOWN 0x00
    3. #define REG_GPIO_BASE 0x8e000000
    4. #define GPIO1_IO01_OFFSET 0x400
    5. int set_pull_down(unsigned int reg_base,unsigned int offset,int down)
    6. {
    7. unsigned int gpio_base;
    8. unsigned int gpio;
    9. gpio_base = ioreamap(reg_base,SIZE_4K);//調用ioreamap映射GPIO空間到內存,映射大小根據實際需求而定
    10. gpio = gpio_base + offset;
    11. gpio &= down; //將某個GPIO拉低,根據具體的寄存器操作而定
    12. __raw_writel(gpio,gpio_base + offset);
    13. return 0;
    14. }
    15. set_pull_up(REG_GPIO_BASE,GPIO1_IO01_OFFSET,SET_PULL_DOWN);

     

    3.2 總結

             3.1中展示了基本的GPIO操作函數的編寫,在實際的驅動開發中,比如對某個連接到CPU的GPIO管腳的外設模塊需要初始化的時候,一般都是調用GPIO接口函數進行輸入輸出、拉高、拉低設置,讀者可以參考3.1的例子根據實際開發需求進行修改。

      本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發現有害或侵權內容,請點擊一鍵舉報。
      轉藏 分享 獻花(0

      0條評論

      發表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 熟妇人妻无码中文字幕| 伊人无码一区二区三区| 久久免费精品国自产拍网站| 亚洲精品麻豆一区二区| 国产激情电影综合在线看| 人人妻人人狠人人爽| 手机看片日本在线观看视频| 播放灌醉水嫩大学生国内精品| 香港日本三级亚洲三级| 久久男人AV资源网站| 国产一区二区三区导航| 精品国精品无码自拍自在线| 久久综合久中文字幕青草| 大学生被内谢粉嫩无套| 国产片AV国语在线观看手机版| 日本不卡一区二区三区| 日本高清在线天码一区播放| 亚洲AV无码一区二区乱子伦| 夜夜爱夜鲁夜鲁很鲁| 在线中文一区字幕对白| 无码人妻品一区二区三区精99| 加勒比无码人妻东京热| 又黄又爽又无遮挡免费的网站| 国产欧美丝袜在线二区| 国产精品福利中文字幕| 人人妻人人澡人人爽欧美精品 | 亚洲毛片不卡AV在线播放一区| 午夜在线观看成人av| 亚洲熟妇自偷自拍另欧美| 成人网站网址导航| 国产偷窥熟女高潮精品视频| 亚洲人成网站免费播放| 性欧美VIDEOFREE高清大喷水| 红杏亚洲影院一区二区三区| 伊人狠狠色丁香婷婷综合| 国产在线一区二区不卡| 人妻少妇精品一区二区三区| 冲田杏梨AV一区二区三区| 国产AV影片麻豆精品传媒| 最新亚洲av日韩av二区| 亚洲 欧美 国产 制服 动漫|