ARM开发板:S5PV210
驱动步骤:
1.定义一个cdev设备结构体变量
2.给cdev申请设备号
3.定义一个文件操作集
4.设备初始化
5.注册设备进内核
6.申请GPIO口
GPIO相关函数接口:
1.gpio_request(引脚,给引脚的名字) ----申请GPIO口
第一个参数:
在内核代码 \arch\arm\mach-s5pv210\include\mach\gpio.h中有定义,如下图(部分)
如GPJ2寄存器的第一个引脚,就是 S5PV210_GPJ2(0)
第二个参数:
为管脚起一个名字而已。
例如:
申请GPJ2CON的第一个管脚,并取名为GPJ2_0:
gpio_request(S5PV210_GPJ2(0),"GPJ2_0");;
2.gpio_free(引脚) ----释放引脚
既然有 gpio_request来申请引脚,那用完了就要释放掉。释放引脚用函数 gpio_free
例如:
释放GPJ2_0引脚:gpio_free(S5PV210_GPJ2(0));
3.gpio_direction_output(引脚,0/1) ----给引脚置0或1,并把引脚设置为输出模式
例如:
给引脚GPJ2_0设置为输出模式,并置1:gpio_direction_output(S5PV210_GPJ2(0),1);
返回值:失败返回负值
4.gpio_direction_intput(引脚) ----给引脚设置成输入模式
例如:
给GPJ2_0 设置成输入模式:gpio_direction_input(SPPV210_GPJ2(0));
5.gpio_set_value(引脚,0/1) ----给引脚置0或1
例如:
给引脚GPJ2_0设置为1(高电平):gpio_set_value(S5PV210_GPJ2(0),1);
返回值:失败返回负值
6.gpio_get_value(引脚) ----获取引脚的电平。看是0还是1
例如:
获取GPJ2_0 的电平: int ret;ret = gpio_get_value(S5PV210_GPJ2(0));
下面给出驱动代码和应用程序代码:(实现Led灯轮流闪烁)
#include <linux/module.h>
#include <linux/kernel.h> //for printk
#include <linux/cdev.h> //for cdev_init, cdev_add..
#include <linux/fs.h> //alloc_chrdev_region..
#include <asm/uaccess.h> //copy_to_user..
#include <linux/ioport.h> //request_mem_region
#include <linux/io.h>
#include <linux/device.h> //for class, device
#include <linux/gpio.h> //for gpio
/*1.定义设备结构体*/
static struct cdev led_cdev;
static dev_t dev_num;
static char *gpio_name[]={"GPJ20","GPJ21","GPJ22","GPJ23"};
static struct class *led_class = NULL;
static struct device *led_device = NULL;
static char wbuf[1]={0};
static int led_open(struct inode *inode, struct file *f)
{
int i;
printk("led open\n");
for(i=0;i<4;i++)
{
gpio_direction_output(S5PV210_GPJ2(i),1);
}
return 0;
}
void led_up(char i)
{
char j;
int k = i-'0';
printk("GET[%c]\n",i);
printk("GET[%d]\n",k);
for(j=0;j<4;j++)
{
gpio_set_value(S5PV210_GPJ2(j),1);
}
gpio_set_value(S5PV210_GPJ2(k),0);
}
static ssize_t led_write(struct file *f, const char __user*buf,
size_t len, loff_t *t)
{
copy_from_user(wbuf,buf,len);
if(wbuf[0]=='0')
{
led_up(wbuf[0]);
}
if(wbuf[0]=='1')
{
led_up(wbuf[0]);
}
if(wbuf[0]=='2')
{
led_up(wbuf[0]);
}
if(wbuf[0]=='3')
{
led_up(wbuf[0]);
}
return 0;
}
/*3.定义文件操作集*/
static const struct file_operations fops={
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int __init led_init(void)
{
int ret;
int i;
/*2.申请设备号*/
ret = alloc_chrdev_region(&dev_num,0,1,"led");
if(ret<0)
{
printk("failed to register dev num\n");
goto failed_register;
}
/*4.字符设备的初始化*/
cdev_init(&led_cdev,&fops);
/*5.注册设备*/
ret = cdev_add(&led_cdev,dev_num,1);
if(ret<0)
{
printk("failed to add cdev\n");
goto failed_add;
}
/*6.申请IO口*/
for(i=0;i<4;i++)
{
ret = gpio_request(S5PV210_GPJ2(i),gpio_name[i]);
if(ret<0)
{
printk("failed to request GPIO\n");
goto failed_request_IO;
}
}
/*7.创建设备类*/
led_class = class_create(THIS_MODULE,"led_class");
if(led_class == NULL)
{
printk("failed to create class\n");
goto failed_create_class;
}
/*创建设备文件结点*/
led_device = device_create(led_class,NULL,dev_num,NULL,"led_dev");
if(led_device == NULL)
{
printk("failed to create device\n");
goto failed_create_device;
}
printk("init completed\n");
return 0;
failed_create_device:
class_destroy(led_class);
failed_create_class:
failed_request_IO:
while(i--)
{gpio_free(S5PV210_GPJ2(i));}
cdev_del(&led_cdev);
failed_add:
unregister_chrdev_region(dev_num,1);
failed_register:
return -1;
}
static void __exit led_exit(void)
{
device_destroy(led_class,dev_num);
class_destroy(led_class);
gpio_free(S5PV210_GPJ2(3));
gpio_free(S5PV210_GPJ2(2));
gpio_free(S5PV210_GPJ2(1));
gpio_free(S5PV210_GPJ2(0));
cdev_del(&led_cdev);
unregister_chrdev_region(dev_num,1);
printk("led uninstall!\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
应用程序:
#include <stdio.h>
#include <fcntl.h>
int main()
{
char buf[1];
char i;
int fd = open("/dev/led_dev",O_WRONLY);
if(fd<0)
{
perror("failed to open");
return -1;
}
while(1)
{
for(i='0';i<'4';i++)
{
buf[0]=i;
write(fd,buf,1);
sleep(1);
}
}
return 0;
}