Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4470389
  • 博文数量: 1148
  • 博客积分: 25453
  • 博客等级: 上将
  • 技术积分: 11949
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-06 21:14
文章分类

全部博文(1148)

文章存档

2012年(15)

2011年(1078)

2010年(58)

分类: 嵌入式

2011-04-24 15:40:51


S3C2440 IO操作S3C2440的IO口操作
                                                                                                            刘启明 2010-3-17
LED驱动的代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

static unsigned long led_table [] =
{
    S3C2410_GPB5,   //32*1 + 5
    S3C2410_GPB6,   //32*1 + 6
    S3C2410_GPB8,   //32*1 + 8
    S3C2410_GPB10   //32*1 + 10
};

static unsigned int led_cfg_table [] = {
    S3C2410_GPB5_OUTP,  //0x01<<10
    S3C2410_GPB6_OUTP,  //0x01<<12
    S3C2410_GPB8_OUTP,  //0x01<<16
    S3C2410_GPB10_OUTP  //0x01<<20
};

static int s3c2440_leds_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned int cmd,
    unsigned long arg)
{
    switch(cmd)
    {
    case 0:
    case 1:
        if (arg > 4)
        {
            return -EINVAL;
        }
        s3c2410_gpio_setpin(led_table[arg], !cmd);
        return 0;
    default:
        return -EINVAL;
    }
}

static struct file_operations s3c2440_leds_fops =
{
    .owner  =   THIS_MODULE,     
.ioctl  =   s3c2440_leds_ioctl,
};

static int __init s3c2440_leds_init(void)
{
    int ret;
    int i;
    ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c2440_leds_fops);
    if (ret < 0)
    {
        printk(DEVICE_NAME " can't register major number\n");
        return ret;
    }
    devfs_mk_cdev(MKDEV(LED_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);
   
    for (i = 0; i < 4; i++)
    {
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
        //printk("led%d_con:0x%x\n",i,s3c2410_gpio_getcfg(led_table[i]));
        s3c2410_gpio_setpin(led_table[i], 1);
    }

    printk(DEVICE_NAME " initialized\n");
    return 0;
}

static void __exit s3c2440_leds_exit(void)
{
    devfs_remove(DEVICE_NAME);
    unregister_chrdev(LED_MAJOR, DEVICE_NAME);
}

module_init(s3c2440_leds_init);
module_exit(s3c2440_leds_exit);
上面代码中,led_table数组相当于对应了GPB的四个IO口的索引,通过这四个值,对这四个IO口进行相关操作。例如:
        S3C2410_GPB5 = S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
                     = S3C2410_GPIO_BANKB + 5
                     = 32*1 + 5
在s3c2410_gpio_setpin(S3C2410_GPB5,0)中,该函数首先通过S3C2410_GPB5获得GPB的虚拟地址和偏移地址,再对GPB5的GPBDAT寄存器进行操作,具体代码如下:
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
    void __iomem *base = S3C2410_GPIO_BASE(pin);
    unsigned long offs = S3C2410_GPIO_OFFSET(pin);
    unsigned long flags;
    unsigned long dat;

    local_irq_save(flags);

    dat = __raw_readl(base + 0x04);//读取GPIO的DAT数据到dat
    dat &= ~(1 << offs);                //先将要设置的IO口拉低
dat |= to << offs;                //再将形参的to值赋给dat
    __raw_writel(dat, base + 0x04);//最后将DAT值写进GPIO的DAT

    local_irq_restore(flags);
}
上面的 函数调用了两个子函数,具体定义如下:
#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
其中S3C24XX_VA_GPIO定义如下:
#define S3C24XX_VA_GPIO    S3C2410_ADDR(0x00E00000)
#define S3C2410_ADDR(x)   (0xF0000000 + (x))
这里S3C2410_ADDR的基地址为0xF0000000,也即2440所有寄存器的虚拟地址的基地址。0x00E00000表示2440的GPIO的偏移地址,也就是说其GPIO的虚拟地址首地址为0xF0E00000。
再看看S3C2410_GPIO_BASE(pin)的定义,我们不仿把S3C2410_GPB5的值放进去计算,可以得到 (S3C2410_GPB5&~31)=32。其目的就是去掉GPB的偏移值,然后再右移一位,和GPIO的虚拟地址首地址相加。因 此,S3C2410_GPIO_BASE(pin)只代表了对应GPIO组的虚拟地址,如GPB的虚拟地址为 10000(B)+0xF0E00000=0xF0E00010。依此类推,可以得到所有GPIO的偏移地址,具体如下表:
        BANK        (pin&~31)        (pin&~31)>>1        S3C2410_GPIO_BASE(pin)
GPA        32*0        0000,0000        0x00        0xF0E00000
GPB        32*1        0010,0000        0x10        0xF0E00010
GPC        32*2        0100,0000        0x20        0xF0E00020
GPD        32*3        0110,0000        0x30        0xF0E00030
GPE        32*4        1000,0000        0x40        0xF0E00040
GPF        32*5        1010,0000        0x50        0xF0E00050
GPG        32*6        1100,0000        0x60        0xF0E00060
GPH        32*7        1110,0000        0x70        0xF0E00070
    S3C2410_GPIO_OFFSET用于获得具体GPIO的偏移地址。如GPB5,则S3C2410_GPIO_OFFSET(pin) = (pin)&31 = (32*1 + 5) & 31 = 5。有了*base和off,就可以操作具体的寄存器了。
函数s3c2410_gpio_cfgpin()用于配置GPCON寄存器。具体代码如下:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
    void __iomem *base = S3C2410_GPIO_BASE(pin);
    unsigned long mask;
    unsigned long con;
    unsigned long flags;

    if (pin < S3C2410_GPIO_BANKB)
    {
        mask = 1 << S3C2410_GPIO_OFFSET(pin);//GPA的寄存器只占一位
    }
    else
    {
        mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;//非GPA的寄存器占两位
    }

    local_irq_save(flags);

    con  = __raw_readl(base + 0x00);//先保留GPCON的值
    con &= ~mask;                   //再将要设置的管脚的CON值清零
    con |= function;                //然后将形参传进来的配置赋给CON
    __raw_writel(con, base + 0x00); //最后将CON值写进GPCON寄存器

    local_irq_restore(flags);
}
上面的LED驱动程序中,led_cfg_table数组给出了GPB相应管脚的属性设置,调用上面的函数后即设置为Output。
到此为止,整个S3C2440的IO口操作,应该就一目了然了。
阅读(1806) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~