1. 程式人生 > >GPIO驅動除錯

GPIO驅動除錯

最近在除錯安霸SDK,這個SDK中並沒有提供直接操作GPIO口的驅動設定,在使用GPIO 的時候一般是通過echo命令的匯出gpio口,設定其屬性,具體方法我在另一文章中寫過,並且網上也有很多的例子可以檢視就不多寫了。今天主要是對於晶片的GPIO的datasheet和驅動程式進行分析,在Linux系統中核心已經提供了很多介面去操作GPIO 核心中gpio的使用   1 測試gpio埠是否合法 int gpio_is_valid(int number);       2 申請某個gpio當然在申請之前需要顯示的配置該gpio埠的pinmux         int gpio_request(unsigned gpio, const char *label)
     3 標記gpio的使用方向包括輸入還是輸出        /*成功返回零失敗返回負的錯誤值*/         int gpio_direction_input(unsigned gpio);         int gpio_direction_output(unsigned gpio, int value);       4 獲得gpio引腳的值和設定gpio引腳的值(對於輸出)         int gpio_get_value(unsigned gpio);         void gpio_set_value(unsigned gpio, int value);       5 gpio
當作中斷口使用         int gpio_to_irq(unsigned gpio);          返回的值即中斷編號可以傳給request_irq()free_irq()         核心通過呼叫該函式將gpio埠轉換為中斷,在使用者空間也有類似方法      6 匯出gpio埠到使用者空間         int gpio_export(unsigned gpio, bool direction_may_change);          核心可以對已經被gpio_request()申請的gpio埠的匯出進行明確的管理,         引數direction_may_change
表示使用者程式是否允許修改gpio的方向,假如可以         則引數direction_may_change為真         /* 撤銷GPIO的匯出 */          void gpio_unexport();  核心實現的GPIO介面,在使用者空間得到了很好的體現,即使用echo的命令操作。具體的程式碼,在kernel/linux-3.10/drivers/gpio中實現,這是整個gpio子系統。話說這次寫驅動並沒有使用過上述介面,基本都是自己根據datasheet實現的介面。GPIO的驅動是個非常簡單的驅動,只要是學會如何去閱讀datasheet,然後去操作GPIO的暫存器就可以。 接下看看datasheet的,如下截圖 這是GPIO的暫存器的說明,限於篇幅問題,就只截上述圖,安霸晶片的GPIO的地址是不連續的,總共分為四組,定義如下 #define GPIO0_StartAddr 0xE8009000 #define GPIO1_StartAddr 0xE800A000 #define GPIO2_StartAddr 0xE800E000 #define GPIO3_StartAddr 0xE8010000 總共114個引腳,分為四組,每組32個引腳,每個引腳對應12個暫存器,換言之,每個暫存器對應32個引腳。確定基地址,每個bit位對應一個引腳。 關於暫存器的介紹: 如上術分別對應每個暫存器。只要把這些引腳和暫存器等之間的對應關係搞清楚,gpio的驅動實現就簡單多了。 好了,datasheet很多東西自己看的理解的可能有些清楚,但在敘述過程中可能有些繁瑣或是限於本人語文老師的原因,寫的有點亂,多多理解。 接下來直接附上GPIO驅動程式碼: #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/gpio.h> #include<linux/device.h> #define GPIO0_StartAddr 0xE8009000 #define GPIO1_StartAddr 0xE800A000 #define GPIO2_StartAddr 0xE800E000 #define GPIO3_StartAddr 0xE8010000 #define GPIO_MAPSIZE 0x2C #define GPIO_OUT 1 #define GPIO_IN 0 #define GPIO_HIGH 1 #define GPIO_LOW 0 #define REG(Addr) (*(volatile unsigned int*) (Addr)) #define DEVICE_NAME "AM_GPIO" //定義裝置名 //定義操作 #define SET_OUTPUT_LOW 0 //設定io口為輸出低電平模式 #define SET_OUTPUT_HIGH 1 //設定io口為輸出高電平模式 #define SET_INPUT 2 //設定io口為輸入模式 #define GET_VALUE 3 //獲取io口的狀態 #define SET_IRQ_LEVEL_LOW 4 //設定io口為低電平觸發中斷模式 #define SET_IRQ_LEVEL_HIGH 5 //設定io口為高電平觸發中斷模式 #define SET_IRQ_EDGES_FAILL 6 //設定io口為下降沿觸發模式 #define SET_IRQ_EDGES_RISI 7 //設定io口為上升沿觸發模式 //定義裝置結構體 static struct class *cdev_class; struct cdev cdev; //定義裝置號引數 dev_t dev = 0; //open函式 static int gpio_open(struct inode *inode, struct file *filp) { return 0; } //release函式 int gpio_release(struct inode *inode, struct file *filp) { printk("gpio device released\n"); return 0; } //ioctl函式 static void s2l_gpio_setpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit, unsigned int to) { unsigned int tmp=0; tmp = REG((unsigned int)Regaddr +Addr_offset); tmp &= ~(1<<MaskBit); tmp |= (!!to<<MaskBit); REG((unsigned int)Regaddr +Addr_offset) = tmp; } unsigned int s2l_gpio_getpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit) { unsigned int tmp=0; tmp=REG((unsigned int)Regaddr +Addr_offset); tmp &= (1<<MaskBit); return tmp?1:0; } static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long gpio_num) { int ret; unsigned int MaskBit; unsigned int group_num; void * GpioBase = NULL; group_num = gpio_num/32; MaskBit = gpio_num%32; switch (group_num) { case 0: GpioBase = ioremap(GPIO0_StartAddr,GPIO_MAPSIZE); break; case 1: GpioBase = ioremap(GPIO1_StartAddr,GPIO_MAPSIZE); break; case 2: GpioBase = ioremap(GPIO2_StartAddr,GPIO_MAPSIZE); break; case 3: GpioBase = ioremap(GPIO3_StartAddr,GPIO_MAPSIZE); break; default: break; } switch (cmd) { case SET_OUTPUT_LOW://0 { s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_OUT); //output s2l_gpio_setpin(GpioBase, 0x00, MaskBit , GPIO_LOW);//low break; } case SET_OUTPUT_HIGH: { s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_OUT); //output s2l_gpio_setpin(GpioBase, 0x00, MaskBit , GPIO_HIGH);//high break; } case SET_INPUT: { s2l_gpio_setpin(GpioBase, 0x04, MaskBit , GPIO_IN); //output break; } case GET_VALUE: { ret = s2l_gpio_getpin(GpioBase,0x00,MaskBit); return ret; printk("ret = %d\n",ret); break; } case SET_IRQ_LEVEL_LOW: { s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_LOW);//設定中斷為電平觸發模式 s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//設定低電平觸發 break; } case SET_IRQ_LEVEL_HIGH: { s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_LOW);//設定中斷為電平觸發模式 s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_HIGH);//設定高電平觸發 break; } case SET_IRQ_EDGES_FAILL: { s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_HIGH);//設定中斷為邊沿觸發模式 s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//設定下降沿觸發 break; } case SET_IRQ_EDGES_RISI: { s2l_gpio_setpin(GpioBase,0x08,MaskBit,GPIO_HIGH);//設定中斷為邊沿觸發模式 s2l_gpio_setpin(GpioBase,0x10,MaskBit,GPIO_LOW);//設定上升沿觸發 break; } default: break; } return 0; } //file_operations static struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_open, .release = gpio_release, .unlocked_ioctl = gpio_ioctl, }; //初始化並註冊cdev static int gpio_init(void) { int result; int err; result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); if (result) { printk("globalvar register failure\n"); unregister_chrdev_region(dev,1); return result; } else { printk(" register success\n"); } cdev_init(&cdev, &gpio_fops); err = cdev_add(&cdev, dev, 1); if (err < 0) { printk("add register error\n"); unregister_chrdev_region(dev, 1); return err; } cdev_class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(cdev_class)) { printk("ERR:cannot create a cdev_class\n"); unregister_chrdev_region(dev, 1); return -1; } device_create(cdev_class,NULL, dev, 0, DEVICE_NAME); return result; } //exit函式 static void gpio_exit(void) { device_destroy(cdev_class, dev); class_destroy(cdev_class); unregister_chrdev_region(dev,1); printk("exit success\n"); } module_init(gpio_init); module_exit(gpio_exit); MODULE_AUTHOR("wangmutian"); MODULE_DESCRIPTION("GPIO Driver"); // 一些描述資訊 MODULE_LICENSE("GPL"); 所有的程式碼不到三百行,最主要的就是實現了一個字元裝置驅動的結構體 static struct file_operations gpio_fops = { .owner = THIS_MODULE, .open = gpio_open, .release = gpio_release, .unlocked_ioctl = gpio_ioctl, }; 特別說明的一點就是 在Linux核心2.6版本之後的,ioctl全都變成了unlocked_ioctl ,我現在使用的系統核心是3.10的,原先在上家公司做pos機,主要是使用2.6核心,一直沒在意過這個事,這次這個問題導致我在編譯過程,始終出現錯誤,然後才發現是這個關鍵字導致的問題,希望這個對大家有所幫助。 還有一個可以學習的地方就是這兩個函式,用來操作GPIO的暫存器 static void s2l_gpio_setpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit, unsigned int to) { unsigned int tmp=0; tmp = REG((unsigned int)Regaddr +Addr_offset); tmp &= ~(1<<MaskBit); tmp |= (!!to<<MaskBit); REG((unsigned int)Regaddr +Addr_offset) = tmp; } unsigned int s2l_gpio_getpin(void *Regaddr, unsigned int Addr_offset, unsigned int MaskBit) { unsigned int tmp=0; tmp=REG((unsigned int)Regaddr +Addr_offset); tmp &= (1<<MaskBit); return tmp?1:0; } 其他都是一下Linux驅動提供的標準介面,在其他地方都可以找個解釋。