这是我进入嵌入式行业进公司做的第一个驱动,
平台:Ti dm6441 linu2.6.18
GPIO是嵌入式系统最简单、最常用的资源了,比如点亮LED,控制蜂鸣器,输出高低电平,检测按键,等等。
由于硬件资源的原因,DM6441并不是GPIO管脚就是纯粹的GPIO脚,GPIO管脚和其他一些标准接口复用相同的引脚,
比如SPI和GPIO复用,I2C和GPIO复用等,到底是使用GPIO还是其他接口,在初始化的时候,都需要对PINMUX0和PINMUX1
两个寄存器进行设置(见DM6441的芯片p72),而软件设置则在Montavista linux-2.6.18_pro500/arch/arm/mach-davinci
目录下mux_cfg.c和对应的include/asm/arch-davinci/mux.h里。由数据手册知道gpio5,6,7默认是gpio功能。
对mux_cfg.c的分析
对MUX_CFG()的参数解释
第一个是描述(和数据手册p72上要对应),第二个是pinmux0还是1,第三个是寄存器偏移。最后一个和调试有关的,
倒数第二个就是你需要给pinmux寄存器相应位设置的值,倒数第三个就是pinmux相应位全为1的值(掩码值),
倒数第四个就是pinmux相应位偏移值。如果你要改变引脚的模式,把对应引脚设置成0,再把其他功能设为1。
(这是我的理解,参考下面的I2C和GPIO43_44设置)。
#include <linux/module.h>
#include <linux/init.h>
#include <asm/hardware.h>
#include <asm/arch/cpu.h>
#include <asm/arch/mux.h>
#ifdef CONFIG_DAVINCI_MUX
struct pin_config __initdata_or_module davinci_dm644x_pins[] = {
/*
* description mux mode mode mux dbg
* reg offset mask mode
*/
MUX_CFG("HDIREN", 0, 16, 1, 1, 1)
MUX_CFG("ATAEN", 0, 17, 1, 1, 1)
MUX_CFG("MSTK", 1, 9, 1, 0, 0)
MUX_CFG("I2C", 1, 7, 1, 1, 0)
MUX_CFG("MCBSP", 1, 10, 1, 1, 0)
MUX_CFG("PWM0", 1, 4, 1, 1, 0)
MUX_CFG("PWM1", 1, 5, 1, 1, 0)
MUX_CFG("PWM2", 1, 6, 1, 1, 0)
MUX_CFG("VLINQEN", 0, 15, 1, 1, 0)
MUX_CFG("VLINQWD", 0, 12, 3, 3, 0) //pinmux上面是2位在表示 所以0x11=3 3位就是111=7
MUX_CFG("EMACEN", 0, 31, 1, 1, 1)
MUX_CFG("GPIO3V", 0, 31, 1, 0, 1)
MUX_CFG("GPIO0", 0, 24, 1, 0, 1)
MUX_CFG("GPIO3", 0, 25, 1, 0, 0)
MUX_CFG("GPIO43_44", 1, 7, 1, 0, 0)
MUX_CFG("GPIO46_47", 0, 22, 1, 0, 1)
MUX_CFG("RGB666", 0, 22, 1, 1, 1)
MUX_CFG("LOEEN", 0, 24, 1, 1, 1)
MUX_CFG("LFLDEN", 0, 25, 1, 1, 0)
};
内核里提供的接口在linux-2.6.18_pro500/arch/arm/mach-davinci目录下的gpio.c,这个是寄存器级的驱动。对这个文件的相关操作
见http://blog.csdn.net/langxing0508/archive/2008/12/25/3604043.aspx 达芬奇DM644X平台(ARM9, Linux-2.6.10)BSP之gpio.c浅析
说得不错,这里表示感谢。
下面是我的gpio驱动程序
/* gpio driver*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/arch/gpio.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <asm/arch-davinci/gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
//#include <asm/arch/regs-gpio.h>
#define DEVICE_NAME "dm644x_gpios" /*定义设备驱动的名字,或设备节点名称*/
#define GPIO_MAJOR 199 /*使用 cat /proc/devices查看不要和存在的char节点重复*/
/*my app gpio define*/
#define ZX_GPIO5 5 /*GPIO5*/
#define ZX_GPIO6 6 /*GPIO6*/
#define ZX_GPIO7 7 /*GPIO7*/
static int davinci_dm644x_gpio_open(struct inode *inode, struct file *file)
{
printk("open gpio,here is driver/n");
const char *tag = NULL;
int gpio5_value;
int gpio6_value;
int gpio7_value;
// gpio_request(ZX_GPIO6, tag);
// gpio_set_value(ZX_GPIO6, 0);
// __gpio_set(ZX_GPIO6, 0);
// printk("%s/n", tag);
gpio_direction_output(ZX_GPIO5, 0);
gpio_direction_output(ZX_GPIO6, 0);
gpio_direction_output(ZX_GPIO7, 0);
gpio5_value = gpio_get_value(ZX_GPIO5);
gpio6_value = gpio_get_value(ZX_GPIO6);
gpio7_value = gpio_get_value(ZX_GPIO7);
printk("gpio5_value = %d, gpio6_value =%d , gpio7_value = %d/n", gpio5_value, gpio6_value, gpio7_value);
return 0;/*该函数可以什么都不做,也可以加入类似初始化的设置*/
}
static int gpio_release(struct inode *inode,struct file *filp)
{
return 0;
}
static int davinci_dm644x_gpio_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
// const char *tag;
int gpio5_value1;
int gpio5_value2;
int gpio6_value1;
int gpio6_value2;
int gpio7_value1;
int gpio7_value2;
switch(cmd) /*cmd 表示应用程序传入的gpio动作,是on 还是off*/
{
case 0: //gpio = 0
/*if(0==arg) arg由自己硬件电路决定使用那些GPIO,我这里没有用这个扩展参数。应用程序就不能决定使用哪些gpio,
完全由驱动程序去设置使用哪些。应用程序控制输出高低电平。所以下面的if语句全都要注释掉*/
gpio_direction_output(ZX_GPIO5, 0); /*调用TI linux-2.6.18寄存器驱动*/
gpio_direction_output(ZX_GPIO6, 0);
gpio_direction_output(ZX_GPIO7, 0);
gpio5_value1 = gpio_get_value(ZX_GPIO5);
gpio6_value1 = gpio_get_value(ZX_GPIO6);
gpio7_value1 = gpio_get_value(ZX_GPIO7);
printk("gpio5_value1 = %d, gpio6_value1 = %d, gpio7_value1 = %d/n", gpio5_value1, gpio6_value1,gpio7_value1);
// }
/* else if(1==arg)
{
gpio_direction_output(ZX_GPIO6 , 0);
gpio_set_value(ZX_GPIO6, 0);
gpio_value = gpio_get_value(ZX_GPIO6);
printk("gpio_value = %d/n", gpio_value);
}
else if(2==arg)
{
gpio_direction_output(ZX_GPIO7, 0);
}
else
{
return -EINVAL;
}*/
break;
case 1: //gpio = 1
//if(0==arg)
//{
// gpio_request(ZX_GPIO5, tag);
// printk("cmd = 1, ZX_GPIO5= %d/n", ZX_GPIO5);
gpio_direction_output(ZX_GPIO5, 1); /*调用TI linux-2.6.18寄存器驱动*/
gpio_direction_output(ZX_GPIO6, 1);
gpio_direction_output(ZX_GPIO7, 1);
gpio5_value2 = gpio_get_value(ZX_GPIO5);
gpio6_value2 = gpio_get_value(ZX_GPIO6);
gpio7_value2 = gpio_get_value(ZX_GPIO7);
printk("gpio5_value2 = %d, gpio6_value2 =%d, gpio7_value2=%d/n", gpio5_value2, gpio6_value2, gpio7_value2);
//}
/*else if(1==arg)
{
gpio_direction_output(ZX_GPIO6, 1);
gpio_value = gpio_get_value(ZX_GPIO6);
printk("gpio_value = %d/n", gpio_value);
}
else if(2==arg)
{
gpio_direction_output(ZX_GPIO7, 1);
}
else
{
return -EINVAL;
}*/
break;
default:
return -EINVAL;
}
}
/*定义驱动设备文件API,在linux系统当中,任何设备都可以当做文件的方式操作,这一点和单片机和MCU有很大差别*/
static const struct file_operations davinci_dm644x_gpio_fileops = {
.owner = THIS_MODULE,
.open = davinci_dm644x_gpio_open,
.ioctl = davinci_dm644x_gpio_ioctl,
.release = gpio_release
};
static int __init davinci_dm644x_gpio_init(void) /*内核初始化会调用该函数*/
{
int ret;
ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &davinci_dm644x_gpio_fileops);
if(ret < 0)
{
printk(DEVICE_NAME " register falid!/n");
return ret;
}
printk (DEVICE_NAME" initialized/n");
return ret;
}
static void __exit davinci_dm644x_gpio_exit(void)
{
unregister_chrdev(GPIO_MAJOR, DEVICE_NAME);
}
module_init(davinci_dm644x_gpio_init);
module_exit(davinci_dm644x_gpio_exit);
MODULE_AUTHOR("xxx <>");
MODULE_DESCRIPTION("Davinci DM644x gpio driver");
MODULE_LICENSE("GPL");
下面是应用程序
/* dm644x_gpio_test.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define on (1)
#define off (0)
int main(void)
{
int gpio_number = 0;
int fd;
fd = open("/dev/dm644x_gpios", 0);
if (fd < 0)
{
perror("open device /dev/dm644x_gpios");
exit(1);
}
printf("open gpio, here is app/n");
sleep(1);
printf("gpio_number = %d/n", gpio_number);
sleep(1);
while (1) {
ioctl(fd, 1, 0);
sleep(5);
ioctl(fd, 0, gpio_number);
sleep(5);
} /*使5,6,7三个口循环的输出高低电平,便于用万用表测试*/
close(fd);
return 0;
}
下面是Makefile
#
# Makefile for the skeleton device drivers.
#
KDIR=/root/work/linux-2.6.18_pro500 # 内核目录
PWD := $(shell pwd) #是指要编译的内核模块的源程序在哪个目录下,$(PWD)指与Makefile在同一目录下,也就是当前目录。
ifeq ($(KERNELRELEASE), )
modules:
$(MAKE) -C $(KDIR) M=$(PWD) modules #编译模块的规则,
arm_v5t_le-gcc -o gpio_test app.c #编译应用程序
cp -f *.ko gpio_test /nfsroot/dm644x/filesys #把编译好的驱动和应用程序复制到扳子的文件系统中去
#scp char-reg.ko [email protected]:/home/zlh/opt/filesystem
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean: #删除生成的所有文件
rm -rf gpio_test *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
#obj-m += skeleton_dev_class.o
obj-m += gpio_driver.o #你要编译的模块名称,这里必须和$(PWD)目录下C程序文件的名称相对应,如果C程序文件为world.c,那么必须修改成obj-m = world.o,也可
#Obj-m = hello.o
#Hello-objs = hello1.o, hello2.o 编译多个文件生成hello.o
endif
直接make 就行了 如果不行的话make ARCH=arm CROSS_COMPILE=arm_v5t_le- 加上编译工具
超级终端操作:
insmod gpio_drive.ko
mknod /dev/dm644x_gpios c 199 0
./gpio_test