编写步骤:
1、确定LED是哪个GPIO,通过看原理图确认
2、配置GPIO的寄存器(复用关系、方向、数据寄存器)
找到相关寄存器的地址 例如设置复用功能的寄存器设置:
Linux系统 可以使用 io 命令查看寄存器的值 :
io -r -4 0xfdc2000c
3、驱动代码里使用ioremap 获取寄存器的虚拟地址
例程
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/modulepram.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define LED_GPIO_DIR_REG 0xfd000000
#define LED_GPIO_DATA_REG 0xfd00000C
struct led_device={
dev_t dev_num;
int major;
int minor;
struct cdev cdev_test;
struct class *class;
struct device *device;
char kbuf[32];
unsigened int *vir_gpio_dir;
}
struct led_device led1;
static int led_open(struct inode *node, struct file *file)
{
file->private_data=&led1;
printk("cdev_test_open \n");
return 0;
}
static int cdev_test_release(struct inode *node, struct file *file)
{
printk("cdev_test_release \n");
return 0;
}
static ssize_t cdev_test_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{
struct led_device *test_dev=(struct led_device *) file->private_data;
char kbuf[64]="123456hello";
copy_to_user(buf,test_dev ->kbuf,strlen(test_dev ->kbuf));
printk("cdev_test_read \n");
}
static ssize_t cdev_test_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{
struct led_device *test_dev=(struct led_device *) file->private_data;
char kbuf[64];
copy_from_user(test_dev -> kbuf,buf,size);
printk("write buf[%s]\n",test_dev ->kbuf);
printk("cdev_test_write\n");
}
struct file_operations cdev_test_ops={
.owner=THIS_MODULE,
.open=cdev_test_open,
.release=cdev_test_release,
.read=cdev_test_read,
.write=cdev_test_write,
.ioctl=cdev_test_ioctl
};
static int cdv_init(void)
{
int ret;
//动态申请
ret=alloc_chrdev_region(&led1.dev_num,0,1,"led_num");
if( ret <0 )
{
printk("alloc_chrdev_regionerr %d\n",ret);
goto err_alloc_chrdev_region;
}
printk("dev_num=%d \n",led1.dev_num);
led1.major = MAJOR(led1.dev_num);
led1.minor = MINOR(led1.dev_num);
printk("input major=%d minor=%d \n",led1.major,led1.minor);
led1.cdev_test.owner= THIS_MODULE;
cdev_init(&led1.cdev_test,&cdev_test_ops);
ret = cdev_add(&led1.cdev_test,led1.dev_num,1);
if( ret <0 )
{
printk("alloc_chrdev_regionerr %d\n",ret);
goto err_cdev_add;
}
led1.class = class_create(THIS_MODULE,"led1");
if(IS_ERR(led1.class ))
{
ret=PTR_ERR( led1.class );
goto err_device_create ;
}
led1.device = device_create(led1.class,NULL,led1.dev_num,NULL,"/dev/led1");
if(IS_ERR(led1.device ))
{
ret=PTR_ERR( led1.device );
goto err_device_create ;
}
//内核中不能直接操作虚拟地址,需要将物理地址转换成虚拟地址
led1.vir_gpio_dir= ioremap(LED_GPIO_DIR_REG,4);
printk("cdv init\n");
return 0;
err_device_create:
device_destroy(led1.class,led1.dev_num);
err_class_create:
classs_destroy(led1.class);
err_cdev_add:
cdev_del(&led1.cdev_test);
err_alloc_chrdev_region :
unregister_chrdev_region(led1.dev_num,1);
}
static void cdv_exit(void)
{
iounmap(led1.vir_gpio_dir);
device_destroy(led1.class,led1.dev_num);
classs_destroy(led1.class);
cdev_del(&led1.cdev_test);
//释放设备号
unregister_chrdev_region(led1.dev_num,1);
printk("cdv exit\n");
return 0;
}
module_init(cdv_init);
module_exit(cdv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SONG");
MODULE_VERSION("v1.0");