1. 程式人生 > >Linux 下操作gpio(兩種方法,驅動和mmap)

Linux 下操作gpio(兩種方法,驅動和mmap)

目前我所知道的在linux下操作GPIO有兩種方法: 

1.  編寫驅動,這當然要熟悉linux下驅動的編寫方法和技巧,在驅動裡可以使用ioremap函式獲得GPIO物理基地址指標,然後使用這個指標根據ioctl命令進行GPIO暫存器的讀寫,並把結果回送到應用層。這裡提供一點程式片斷供大家參考:

  1.   int  init_module(void){   
  2.         printk(KERN_ALERT "ioctl load.\r\n");   
  3.     register_chrdev(254,"ioreg",&fops);   
  4.         stb_gpio = (STBX25XX_GPIO_REG *)ioremap(GPIO_BASE,GPIO_LEN);   
  5.     if(stb_gpio == NULL){   
  6.             printk(KERN_ALERT "can''t get io base.\r\n");   
  7.         return -1;   
  8.         }   
  9.     return 0;   
  10. }   
  11. int io_ioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg){  
  12.     unsigned long uGpio;   
  13.     printk(KERN_ALERT "io_ioctl cmd=%04x,arg=%04x.\r\n",cmd,(int)arg);   
  14.     switch(cmd){   
  15.         case SET_IO_DIR:{   
  16.              printk(KERN_ALERT "SET_IO_DIR\r\n");   
  17.              break;   
  18.         }   
  19.         case SET_IO_VALUE:{   
  20.              printk(KERN_ALERT "SET_IO_VALUE\r\n");   
  21.              break;   
  22.         }   
  23.         case GET_IO_VALUE:{   
  24.              printk(KERN_ALERT "GET_IO_VALUE\r\n");   
  25.              uGpio = stb_gpio->GPI;   
  26.              printk(KERN_ALERT "GPIO = %08x",(int)uGpio);   
  27.              copy_to_user((void *)arg,(const void *) &uGpio,sizeof(uGpio));   
  28.              break;   
  29.         }   
  30.         case GET_IO_DIR:{   
  31.              printk(KERN_ALERT "GET_IO_DIR\r\n");   
  32.              break;   
  33.         }   
  34.     }   
  35.     return 0;   
  36. }   

2.  在應用層使用mmap函式在應用層獲得GPIO物理基地址對應的虛擬地址指標,然後使用這個指標來讀寫GPIO暫存器,這裡提供一點程式片斷供大家參考: 

 

  1. char dev_name[] = "/dev/mem";   
  2.         GPIO_REGISTER  *gpio_base;   
  3.         fd  =  open(dev_name,O_RDWR);   
  4.         if(fd<0){   
  5.                printf("open %s is error\n",dev_name);   
  6.                return -1 ;   
  7.         }   
  8.         gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );  
  9.         if(gpio_base == NULL){   
  10.                 printf("gpio base mmap is error\n");   
  11.                 close(fd);   
  12.                 return -1;   
  13.         }   
  14.         gpio_base->or  = (gpio_base->or & 0x7fffffff);   




mmap

功能描述: 
mmap將一個檔案或者其它物件對映進記憶體。檔案被對映到多個頁上,如果檔案的大小不是所有頁的大小之和,最後一個頁不被使用的空間將會清零。munmap執行相反的操作,刪除特定地址區域的物件對映。
基於檔案的對映,在mmap和munmap執行過程的任何時刻,被對映檔案的st_atime可能被更新。如果st_atime欄位在前述的情況下沒有得到更新,首次對對映區的第一個頁索引時會更新該欄位的值。用PROT_WRITE 和 MAP_SHARED標誌建立起來的檔案對映,其st_ctime 和 st_mtime
在對對映區寫入之後,但在msync()通過MS_SYNC 和 MS_ASYNC兩個標誌呼叫之前會被更新。

用法:  
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags,
    int fd, off_t offset);
int munmap(void *start, size_t length); 

引數:   
start:對映區的開始地址。
length:對映區的長度。
prot:期望的記憶體保護標誌,不能與檔案的開啟模式衝突。是以下的某個值,可以通過or運算合理地組合在一起
PROT_EXEC //頁內容可以被執行
PROT_READ  //頁內容可以被讀取
PROT_WRITE //頁可以被寫入
PROT_NONE  //頁不可訪問
flags:指定對映物件的型別,對映選項和對映頁是否可以共享。它的值可以是一個或者多個以下位的組合體
MAP_FIXED //使用指定的對映起始地址,如果由start和len引數指定的記憶體區重疊於現存的對映空間,重疊部分將會被丟棄。如果指定的起始地址不可用,操作將會失敗。並且起始地址必須落在頁的邊界上。
MAP_SHARED //與其它所有對映這個物件的程序共享對映空間。對共享區的寫入,相當於輸出到檔案。直到msync()或者munmap()被呼叫,檔案實際上不會被更新。
MAP_PRIVATE //建立一個寫入時拷貝的私有對映。記憶體區域的寫入不會影響到原檔案。這個標誌和以上標誌是互斥的,只能使用其中一個。
MAP_DENYWRITE //這個標誌被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要為這個對映保留交換空間。當交換空間被保留,對對映區修改的可能會得到保證。當交換空間不被保留,同時記憶體不足,對對映區的修改會引起段違例訊號。
MAP_LOCKED //鎖定對映區的頁面,從而防止頁面被交換出記憶體。
MAP_GROWSDOWN //用於堆疊,告訴核心VM系統,對映區可以向下擴充套件。
MAP_ANONYMOUS //匿名對映,對映區不與任何檔案關聯。
MAP_ANON //MAP_ANONYMOUS的別稱,不再被使用。
MAP_FILE //相容標誌,被忽略。
MAP_32BIT //將對映區放在程序地址空間的低2GB,MAP_FIXED指定時會被忽略。當前這個標誌只在x86-64平臺上得到支援。
MAP_POPULATE //為檔案對映通過預讀的方式準備好頁表。隨後對對映區的訪問不會被頁違例阻塞。
MAP_NONBLOCK //僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在於記憶體中的頁面建立頁表入口。
fd:有效的檔案描述詞。如果MAP_ANONYMOUS被設定,為了相容問題,其值應為-1。
offset:被對映物件內容的起點。
  
    
返回說明:   
成功執行時,mmap()返回被對映區的指標,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設為以下的某個值   
EACCES:訪問出錯
EAGAIN:檔案已被鎖定,或者太多的記憶體已被鎖定
EBADF:fd不是有效的檔案描述詞
EINVAL:一個或者多個引數無效
ENFILE:已達到系統對開啟檔案的限制
ENODEV:指定檔案所在的檔案系統不支援記憶體對映
ENOMEM:記憶體不足,或者程序已超出最大記憶體對映數量
EPERM:權能不足,操作不允許
ETXTBSY:已寫的方式開啟檔案,同時指定MAP_DENYWRITE標誌
SIGSEGV:試著向只讀區寫入
SIGBUS:試著訪問不屬於程序的記憶體區