《OK6410-LED驱动程序设计》之使用ioremap实现访问CPU寄存器

来源:互联网 发布:视频刷流量软件 编辑:程序博客网 时间:2024/05/16 01:56

Ok6410开发板LED连接电路图:

           从电路图上我们可以看到,发光二极管LED 的一端连接到了ARM 的GPIO,另一端经过一个限流电阻接电源VCC3。当GPIO 口为低电平时,LED 两端产生电压降,这时LED 有电流通过并发光。反之当GPIO 为高电平时,LED 将熄灭。注意亮灭之间要有一定的延时,以便人眼能够区分出来。

4个LED分别连接到核心板上的GPM端:

从上面可以看出4个LED对应的端口:

GPM0->LED1  GPM1->LED2 GPM2->LED3  GPM3->LED4

查看s3c6410芯片手册,端口M对应的三个寄存器地址:

                    实验相应寄存器

                    端口配置寄存器

                    端口数据寄存器

下面我们开始代码部分

I/O 内存访问流程:

      1. request_mem_region()  申请IO内存

      2. ioremap() 将物理地址映射到虚拟地址

      3. ioread8() 、ioread16()、ioread32()、iowrite8()、iowrite16()、iowrite32() 读写

     4. iounmap() 释放虚拟内存

     5. release_mem_region() 释放IO内存

注意:

     1、2 在模块初始化或打开设备时调用

     4、5 在模块卸载或关闭设备时调用

         request_mem_region()不是必须使用,但建议使用。任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再次申请该资源就会失败。

testled.c

#include <linux/init.h>#include <linux/module.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <asm/io.h>#include <linux/ioport.h>#include <asm/uaccess.h>#define DEVICE_NAME "liye_led"/* 定义幻数 */#define LED_IOC_MAGIC  'k'/* 定义命令 */#define LED_ALLOFF   _IO(LED_IOC_MAGIC, 0)#define LED_ALLON    _IO(LED_IOC_MAGIC, 1)#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)/*定义命令的最大个数*/#define LED_IOC_MAXNR 2#define GPMCON 0x7F008820 //寄存器地址(物理地址)#define GPMDAT 0x7F008824static volatile unsigned long *gpmcon_addr; //经过ioremap映射后的虚拟地址static volatile unsigned long *gpmdat_addr;static void Led_port_init(void){//设置GPM0-GPM3为输出端口//最后结果相当于…0001000100010001,都是输出状态*gpmcon_addr &= (1U<<16); *gpmcon_addr |= (1U<<0)|(1U<<4)|(1U<<8)|(1U<<12);   //全亮   低电平亮*gpmdat_addr &= (1U<<4);}static void led_all_on(void ){//*gpmdat_addr &= (1U<<4);这个也是可以的,一样的*gpmdat_addr &= 16; }static void led_all_off(void){*gpmdat_addr |= ~(1U<<4);}static void led_select_on(unsigned int led_nu){led_all_off();*gpmdat_addr &= ~(led_nu);}static int led_open(struct inode * inode , struct file * filp){return 0;}static int led_release(struct inode * inode, struct file *filp){return 0;}static int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret = 0;int err = 0;int ioarg = 0;   /* 检测命令的有效性 */    if (_IOC_TYPE(cmd) != LED_IOC_MAGIC)         return -EINVAL;    if (_IOC_NR(cmd) > LED_IOC_MAXNR)         return -EINVAL;   /* 根据命令类型,检测参数空间是否可以访问 */    if (_IOC_DIR(cmd) & _IOC_READ)        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));    else if (_IOC_DIR(cmd) & _IOC_WRITE)        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));    if (err)         return -EFAULT;switch (cmd){case LED_ALLOFF://全灭led_all_off();break;case LED_ALLON://全亮led_all_on();break;case LED_SELECTON://根据参数有选择的凉__get_user(ioarg,(int *)arg);//获得用户空间数据led_select_on(ioarg);break;default:ret = -EINVAL;break;}return ret;}static const struct file_operations led_fops ={.owner = THIS_MODULE,.open = led_open,.release = led_release,.unlocked_ioctl = led_ioctl,};static struct miscdevice led_dev ={.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &led_fops,};static int __init led_init(void){ int ret; //申请IO内存,不是必须的。if (!request_mem_region(GPMCON, 8, "leds")){ret = -EBUSY;goto request_mem_failed;} gpmcon_addr = ioremap(GPMCON, 4);//将物理地址映射为虚拟地址if (NULL == gpbcon_addr){ret = -EIO;printk("gpmcon remap failed\n");goto con_map_failed;}gpmdat_addr = ioremap(GPMDAT, 4);if (NULL == gpmdat_addr){ret = -EIO;printk("gpmdat remap failed\n");goto dat_map_failed;}printk("gpmcon_addr remap on %p\n", gpmcon_addr);printk("gpmdat_addr remap on %p\n", gpmdat_addr); Led_port_init();//初始化led灯ret = misc_register(&led_dev);//注册一个混杂设备驱动,主设备号为10if (ret){printk("misc_register failed\n");goto failed;} printk("leds init\n");return 0; failed:iounmap(gpmdat_addr);dat_map_failed:iounmap(gpmcon_addr);con_map_failed:release_mem_region(GPMCON, 8);request_mem_failed:return ret;}static void __exit led_exit(void){iounmap(gpmdat_addr); //取消映射iounmap(gpmcon_addr);release_mem_region(GPMCON, 8); //释放I/O内存misc_deregister(&led_dev);//注册混杂设备,主设备号为10 printk("leds exit\n");}MODULE_LICENSE("GPL");MODULE_AUTHOR("liye");module_init(led_init);module_exit(led_exit);


test.c

#include <stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include <linux/ioctl.h>/* 定义幻数 */#define LED_IOC_MAGIC  'k'/* 定义命令 */#define LED_ALLOFF   _IO(LED_IOC_MAGIC, 0)#define LED_ALLON    _IO(LED_IOC_MAGIC, 1)#define LED_SELECTON _IOW(LED_IOC_MAGIC, 2, int)int main(){    int fd = 0;    int cmd;    int arg = 0;    int choice;    int flag=0;    /*打开设备文件*/    fd = open("/dev/liye_led",O_RDWR);    if (fd < 0)    {        printf("Open Dev liye_led Error!\n");        return -1;    }        while(1)    {printf("\n********MENU**********\n");printf("     [0] LED all off    \n");printf("     [1] LED all on     \n");printf("     [2] Select LED     \n");printf("     [3] Exit           \n");printf("**********************\n\n");printf("Enter the choice: ");scanf("%d",&choice);switch(choice){case 0:cmd=LED_ALLOFF;break;case 1:cmd=LED_ALLON;break;case 2:printf("Enter the arg (0=<arg<=15) : ");scanf("%d",&arg);cmd=LED_SELECTON;break;case 3:flag=1;break;default:printf("Input Error\n");flag=2;break;} if(flag==1)break;if(flag==2){flag=0;continue;}if(ioctl(fd,cmd,&arg)<0){printf("Call ioctl error!\n");return -1;}    }     close(fd);    return 0;    }



Makefile

ifneq ($(KERNELRELEASE),)obj-m := testled.oelseKDIR := /home/liye/forlinux/linux-2.6.36all:make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-install:cp testled.ko test /home/liye/forlinux/rootfs/courseclean:rm -f *.o *.ko *.mod.c *.mod.o *.order *.symversendif