STB02500是一款嵌入IBM PPC405 CPU的面向视频,音频应用的SOC芯片,集成外围丰富,特别适合用户构建IPTV机顶盒系统。由于笔者以前惯用ARM系统,初次使用powerpc大端cpu还真是有点不适应,还有就是IBM的PPC系统GPIO操作起来要比起ARM系统要复杂些,这些新的特点给笔者的初次power pc之旅带来些额外的烦恼,好在经过一番研究笔者已经对PPC405的GPIO有了初步的熟悉,并顺利的完成控制任务,下面就将笔者对PPC405 GPIO的肤浅理解一一抛出。
一. GPIO物理起始地址: 对于要控制GPIO的嵌入式编程人员来讲,GPIO的物理地址是必须要搞清楚的。STB02500SOC中GPIO的物理基本地址是:0x4006 0000空间长度是:0x44。(gpio_base = 0x4006 0000 len = 0x44)
二. GPIO寄存器:
GPIO共有 9个寄存器分别是:
1. GPO: GPO(32bit)寄存器用于设置或清除对应的GPIO引脚值(高,或者低)。具体控制时需要先读出GPIO引脚的值,然后再用”或”,”与”进行相应的逻辑设置和清除操作,很显然这是非原子的操作,在多任务系统里,是有可能发生意外的,需要程序员在软件上实现相应的原子操作,这和ARM系统的 I/O操作相比应该是比较落后的)。
2. GPTC: GPTC(32bit)寄存器用于设置对应的GPIO引脚是输出还是输入(这样比较好理解些)程序设置相应的bit为1对应的GPIO引脚就是输出否则就是输入(呵呵,这里还要声明一下下,该寄存器要起作用还要设置好GPTS寄存器)。
3. GPOS: GPOS(64bit)寄存器用于控制GPIO相应的引脚是由GPO驱动还是有Alt_Output_x(1,2,3)驱动(就这样简单的理解吧,嘿嘿)。
4. GPTS: GPTS(64bit)这个寄存器功能还是比较难说清楚的呀,简单的讲如果需要用GPO驱动GPIO寄存器,就需要设置对应2bit为00选择GPTC作为输出使能控制器,这样GPTC寄存器才能够起作用的。
5. GPOD: GPOD(32bit)这个寄存器是比较单纯的,如果对应位设置1相应的GPIO引脚就处于漏极开路状态,这样可以适应多电压系统,和多输出驱动单输入系统。
6. GPI: GPI(32bit) 就是相应GPIO引脚的值(高电平,还是低电平)。
7. GPIS1:我暂时还没有研究。
8. GPIS2: 我暂时还没有研究。
9. GPIS3: 我暂时还没有研究。
注意:这里要特别声明:这里的GPIO0对应GPO的bit0,GPO的bit0实际是32位GPOd的bit31(以ARM系统来看)。
三. Linux 下操作gpio:
对于在不支持虚拟内存的操作系统和根本就没有使用操作系统的系统里操作GPIO直接读写对应的GPIO寄存器就可以啦,但是在linux这样的操作系统下,你必须编写一个操作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寄存器,这里提供一点程序片断供大家参考:
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);
3.
4.
四. 总结:
虽然GPIO寄存器很多但是熟悉后,使用起来也很简单的,关键是要理解透每个GPIO引脚的功能,和个寄存器的功能特点。其实如果只是做简单的I/O输入输出控制(大多数单片机开发人员最常用用到),只要熟悉 GPO,GPI,GPTC就可以啦。
附:IBM GPIO系统原理框图