LED驱动代码以及原理图
实验的目的:验证gpio的虚拟地址。
实验现象:在开发板加载模块时候,LED亮;卸载模块,LED灭
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <plat/map-base.h>
#include <plat/map-s5p.h>
#define MYMAJOR 0
#define NAME "MyModule"
//LED接在GPL2的第0管脚
#define GPL2CON ((volatile unsigned int *)(S5P_VA_GPIO2 + 0x100))
#define GPL2DAT ((volatile unsigned int *)(S5P_VA_GPIO2 + 0x104))
#define rGPL2CON (*GPL2CON)
#define rGPL2DAT (*GPL2DAT)
int major;
static int module_open(struct inode *inode, struct file *file)
{
printk("module_open\n");
return 0;
}
static ssize_t module_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
printk("module_write\n");
return 0;
}
static ssize_t module_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
printk("module_read\n");
return 0;
}
static int module_release(struct inode *inode, struct file *file)
{
printk("module_release\n");
return 0;
}
static const struct file_operations fops = {
.open = module_open,
.write = module_write,
.read = module_read,
.release = module_release,
.owner = THIS_MODULE,
};
static int __init module_test(void)
{
//int ret;
printk(KERN_DEBUG "install module_test");
major = register_chrdev(MYMAJOR, NAME, &fops);
if (major < 0)
{
printk(KERN_ERR "register_chrdev failed\n");
return -EINVAL;
}
printk("module_test major: %d\n",major);
//用来测试LED驱动
rGPL2CON = 0X11111111;
rGPL2DAT = (1 << 0);
printk("GPL2CON:%p\n", GPL2CON);
printk("GPL2DAT:%p\n", GPL2DAT);
return 0;
}
static void __exit module_ex(void)
{
unregister_chrdev(major, NAME);
printk(KERN_DEBUG "uninstall module_test");
rGPL2DAT = (0 << 0);
}
module_init(module_test);
module_exit(module_ex);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhou");
(1)exynos4412相关的虚拟地址包含哪些文件?
- map-base.h:定义了虚拟地址的基地址,
#define S3C_ADDR_BASE 0xF6000000
这个是所有虚拟地址的基地址,然后各个模块的基地址=S3C_ADDR_BASE + 偏移量。 - gpio-exynos4.h: gpio相关的虚拟映射表,表中定义各个IO口的地址。
比如:#define EXYNOS4_GPL0(_nr) (EXYNOS4_GPIO_L0_START + (_nr))
,我们调用内核的提供的API, 就能实现对GPIO的操作。
s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT); //将管脚设置为输出
gpio_set_value(EXYNOS4_GPL2(0), 1); //置1
gpio_set_value(EXYNOS4_GPL2(0), 0); //置0
- gpio-exynos4.c: 这个文件定义了类似标签的结构体,用来描述 GPIO虚拟地址的一些信息,比如,给它们一个名字,保持它们的基地址和偏移量等等。
我先截取一小部分出来,这个是实验用到的端口。我们只需知道GPL2的基地址是S5P_VA_GPIO2 + 0x100
,实验中点亮LED,是通过它寄存器的虚拟地址来操作。当然,也可以用内核提供号的API来操作。只不过,当你要点亮多盏LED灯时候,直接操作寄存器,还是挺方便的。
{
.base = (S5P_VA_GPIO2 + 0x100),
.eint_offset = 0x20,
.group = 22,
.chip = {
.base = EXYNOS4_GPL2(0),
.ngpio = EXYNOS4_GPIO_L2_NR,
.label = "GPL2",
}
}
(2)怎么对GPIO的虚拟寄存器进行操作?
- 第一步:需要将一串数字,变成一个指针变量,也就是真正的变成一个地址。接着,对地址取 * 就能对这个地址的内容进行读写。此处,我将它们封装为一个宏。
#define GPL2CON ((volatile unsigned int *)(S5P_VA_GPIO2 + 0x100))
#define GPL2DAT ((volatile unsigned int *)(S5P_VA_GPIO2 + 0x104))
#define rGPL2CON (*GPL2CON)
#define rGPL2DAT (*GPL2DAT)
- 第二步: 配置端口。
rGPL2CON = 0X11111111;
将GPL2的0-7管脚配置成输出状态,你也可以根据需求来配置。具体可查看数据手册的第243页。 - 第三步:写入数据。
rGPL2DAT = (1 << 0);
点亮GPL2(0)的LED灯。
(3)为什么GPL2DAT的偏移量比GPL2CON的多出 0x04?
从文档上可知,它们的偏移量相差是0X04.