S3C2440的IO口操作

来源:互联网 发布:神州端口聚合配置 编辑:程序博客网 时间:2024/05/24 06:28

S3C2440IO口操作

                                                                                                               刘启明 2010-3-17

LED驱动的代码如下:

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
 
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_GPB50)中,该函数首先通过S3C2410_GPB5获得GPB的虚拟地址和偏移地址,再对GPB5GPBDAT寄存器进行操作,具体代码如下:
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);//读取GPIODAT数据到dat
    dat &= ~(1 << offs);            //先将要设置的IO口拉低
dat |= to << offs;              //再将形参的to值赋给dat
    __raw_writel(dat, base + 0x04);//最后将DAT值写进GPIODAT
 
    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表示2440GPIO的偏移地址,也就是说其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。有了*baseoff,就可以操作具体的寄存器了。
函数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

到此为止,整个S3C2440IO口操作,应该就一目了然了。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩发烧后又吐怎么办 11个月宝宝吐了怎么办 11个月宝宝拉肚子怎么办 2岁宝宝反复呕吐怎么办 1岁半幼儿拉肚子怎么办 宝宝打嗝呕吐胃难受怎么办 3岁宝宝发烧还吐怎么办 孩子喝水都吐怎么办啊 宝宝吃多了呕吐怎么办 3岁宝宝吐怎么办才好 儿童受凉肚子疼发热呕吐怎么办 两岁宝宝半夜呕吐怎么办 两岁宝宝吐了怎么办 2岁宝宝发烧吐怎么办 2岁多宝宝呕吐是怎么办 2周岁宝宝中暑了怎么办 2岁半宝宝着凉呕吐怎么办 3岁宝宝吐了几次怎么办 一岁宝宝恶心吐怎么办 9个月宝宝一直吐怎么办 晚上冻着了呕吐怎么办 2岁宝宝一直吐怎么办 两岁宝宝门牙龋齿怎么办 两岁宝宝得龋齿怎么办 两岁宝宝长龋齿怎么办 宝宝2岁不吃饭怎么办 两岁宝宝总是吐怎么办 3岁儿童受凉呕吐怎么办 两岁宝宝四天没拉大便怎么办 两岁宝宝发烧吐怎么办 四岁宝宝吐了怎么办啊 3岁宝宝突然吐了怎么办 宝宝撑着了吐拉怎么办 2岁宝宝体温37.5怎么办 宝宝2岁乳牙烂了怎么办 孕40周还没入盆怎么办 孕妇脸上长斑了怎么办 七个月宝宝大便干怎么办 两月大婴儿不拉大便怎么办 周岁宝宝大便出血了怎么办 十一个月宝宝大便干燥怎么办