阅读量:0
编写步骤:
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");