Linux 下操作gpio:
對於在不支援虛擬記憶體的作業系統和根本就沒有使用作業系統的系統裡操作GPIO直接讀寫對應的GPIO寄存器就可以啦,但是在linux這樣的作業系統下,核心層和應用程式層都是處於虛擬位址中,而GPIO的寄存器都是處於物理地址中,你必須編寫一個操作GPIO的驅動,或者是使用一些變通的技巧來操作GPIO.
目前我所知道的在linux下操作GPIO有兩種方法:
1. 編寫驅動,這當然要熟悉linux下驅動的編寫方法和技巧,在驅動裡都是虛擬位址,可以使用ioremap函數獲得GPIO物理基地址指標,然後使用這個指標根據ioctl命令進行GPIO寄存器的讀寫,並把結果回送到應用程式層。這裡提供一點程式片斷供大家參考:
int init_module(void){
printk(KERN_ALERT "ioctl load.\r\n");
register_chrdev(254,"ioreg",&fops);
stb_gpio = (STBX25XX_GPIO_REG *)ioremap(GPIO_BASE,GPIO_LEN);
if(stb_gpio == NULL){
printk(KERN_ALERT "can''t get io base.\r\n");
return -1;
}
return 0;
}
int io_ioctl (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg){
unsigned long uGpio;
printk(KERN_ALERT "io_ioctl cmd=%04x,arg=%04x.\r\n",cmd,(int)arg);
switch(cmd){
case SET_IO_DIR:{
printk(KERN_ALERT "SET_IO_DIR\r\n");
break;
}
case SET_IO_VALUE:{
printk(KERN_ALERT "SET_IO_VALUE\r\n");
break;
}
case GET_IO_VALUE:{
printk(KERN_ALERT "GET_IO_VALUE\r\n");
uGpio = stb_gpio->GPI;
printk(KERN_ALERT "GPIO = %08x",(int)uGpio);
copy_to_user((void *)arg,(const void *) &uGpio,sizeof(uGpio));
break;
}
case GET_IO_DIR:{
printk(KERN_ALERT "GET_IO_DIR\r\n");
break;
}
}
return 0;
}
2. 在應用程式層使用mmap函數在應用程式層獲得GPIO物理基地址對應的虛擬位址指標(應用程式層中),讓使用者程式直接存取裝置記憶體,然後使用這個指標來讀寫GPIO寄存器,這裡提供一點程式片斷供大家參考:
/dev/mem是實體記憶體的全映像,可以用來訪問實體記憶體,一般用法是open("/dev/mem",O_RDWR|O_SYNC),然後mmap,接著就可以用mmap的地址來訪問實體記憶體,這實際上就是實現使用者空間驅動的一種方法。
/dev/kmem:核心看到的虛擬記憶體的全映像,可以用來訪問kernel的內容。
核心空間用1G虛擬位址,使用者空間用3G虛擬位址
所以ioremap當然不能分出1G地址供你用(ioreamp的空間大小是有限制的)
一個物理地址,核心調用 ioremap得到一個1G內的虛擬位址,用來操作實體記憶體
應用程式層調用 mmap 得到一個3G內的虛擬位址,用來操作實體記憶體
char dev_name[] = "/dev/mem";
GPIO_REGISTER *gpio_base;
fd = open(dev_name,O_RDWR);
if(fd<0){
printf("open %s is error\n",dev_name);
return -1 ;
}
gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );
if(gpio_base == NULL){
printf("gpio base mmap is error\n");
close(fd);
return -1;
}
gpio_base->or = (gpio_base->or & 0x7fffffff);
完整的程式:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <time.h>
#define TIME_OUT 5 /* the timeout time, in seconds */
/**
* Define behaviour's when the button is pressed enough time.
*/
void longtu_timeout(){
printf("**** Pressed 5 seconds, Call RECOVERY! ****\n");
//system("/sbin/recover_longtu.sh");
//system("/sbin/reboot");
}
/*
* The main function.
*/
int main(int argc, char *argv[])
{
int mfd;
unsigned int val=0, last_val;
void *base;
char *sys_pinstaterd;
time_t t_now, t_old;
int flag_issued = 0;
#if 0
// uncomment these to make the program a daemon.
pid_t pid;
int i;
if ( (pid=fork())<0)
return -1;
else if (pid!=0)
exit(0);
setsid();
chdir("/");
umask(0);
for (i=0;i<256;i++)
close(i);
#endif
// open the memery mapped file.
mfd=open("/dev/mem", O_RDWR);
if (mfd < 0){
printf("Cannot open /dev/mem.\n");
exit(-1);
}
// Initialize the map
base = mmap( NULL, 0x130, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0x1fe00000);
if ( base < 0){
exit(-1);
}
// Got the pointer to SYS_PINSTATERD register of GS32I CPU.
sys_pinstaterd = base + 0x011c;
// init the temperay variables
t_now=t_old=time(NULL);
last_val = 0;
while(1)
{
// Get status of GPIO7 pin.
val = *( (volatile unsigned int*)sys_pinstaterd );
val = (val&0x4) ? 1:0;
printf("\tgpio 7 stat=%x.\n", val);
if (val){
// the button is pressed down !!
if ( last_val==0 ){
// starting time of press, log the time
t_old = time(NULL);
last_val=1;
printf("Button Down\n");
}else {
// already pressed down! let's count the time!
t_now = time(NULL);
if (t_now-t_old>=TIME_OUT && flag_issued==0){
// Pressed LONG ENOUGH, issue the handler script!!
flag_issued = 1;
longtu_timeout();
}
}
}else{
// No button pressed.
if (flag_issued){
flag_issued = 0;
printf("Button UP.\n");
}
}
last_val = val;
usleep(100);
}
munmap(base, 0x000);
close(mfd);
return 0;
}