S3C2440的IO口操作
来源:互联网 发布:神州端口聚合配置 编辑:程序博客网 时间:2024/05/24 06:28
S3C2440的IO口操作
刘启明 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_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口操作,应该就一目了然了。
- S3C2440的IO口操作
- s3c2440的触摸屏操作
- s3c2440 的 rtc 操作
- IO口的操作
- s3c2440的IO静态映射的分析
- s3c2440的IO静态映射的分析
- s3c2440的IO静态映射的分析
- s3c2440对norflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- S3C2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- 20100317-001工程项目管理实用手册
- Forms身份验证实例
- 嵌入式系统多语言文本解决方案
- 时区转换
- 用Javascript评估用户输入密码的强度
- S3C2440的IO口操作
- CSS问题整理
- css hack 学习总结
- std::string 内存管理特别,多线程使用时应注意
- Eclipse debug技巧
- windows 7 系统下用tightVNC远程控制unbantu 9.10
- 获取http的gzip内容并解压相关问题
- 巧用JAVASCRIPT代码限制文本字节数
- Haskell,影印,新书推荐