使用IO内存控制硬件点亮LED灯.doc_第1页
使用IO内存控制硬件点亮LED灯.doc_第2页
使用IO内存控制硬件点亮LED灯.doc_第3页
使用IO内存控制硬件点亮LED灯.doc_第4页
使用IO内存控制硬件点亮LED灯.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux中控制GPIO点亮LED的方法有好几种。一种是使用内核提供的专门用来控制GPIO的函数来点亮LED,如:s3c2410_gpio_cfgpin(led_tablei, led_cfg_tablei); s3c2410_gpio_setpin(led_tablei, 0);一种是通过IO内存获取硬件地址从而控制GPIO来点亮LED,如:void *ioremap(unsigned long phys_addr, unsigned long size);void ioumap(void *addr);这里主要介绍第二种点亮LED方式。1. 理论支撑1.1 IO内存分配和映射1.1.1在使用IO内存之前首先分配IO内存区域。分配和撤销IO内存区域使用的函数如下: #include struct resource *request_mem_region(unsigned long start,unsigned long len,char *name);参数说明:start 分配内存起始地址 len 分配内存长度 返回 成功非NULL,否则返回NULL。相应的释放函数: void release_mem_region(unsigned long start,unsigned long len);1.1.2在linux中不能使用实际的物理地址,要对指定的物理地址进行操作必须先将物理地址映射到虚拟地址中。下面的函数就是实现物理地址到虚拟地址的映射:#include void *ioremap(unsigned long phys_addr, unsigned long size);参数说明:phys_addr 需要访问的物理内存(寄存器)的首地址 size 内存区域大小返回与该段物理地址对应的虚拟地址 相应的撤销映射关系的函数是:void ioumap(void *addr);1.1.3使用IO内存时,request_mem_region函数并不是必须的,该函数只是在内核中标志该内存区域已经分配出去,不能再进行分配出去。但是,这不不是说别的驱动不能再使用该IO内存。至于能不能使用,分下面2种情况: 1. 两个驱动都用request_mem_region分配相同的IO内存,则两个驱动只能有一个驱动可以使用。 2. 两个驱动只有一个使用了request_mem_region函数,那么这两个驱动可以同时使用,并可以同时访问该IO内存。1.2 访问IO内存 ioremap函数的返回值可以直接当做指针(指向对应的物理内存(寄存器)地址)使用,但是这种使用方式不具有可移植性。使用下面的访问IO内存的专用函数符合可移植性要求。从 I/O 内存读取, 使用下列函数之一: unsigned int ioread8(void *addr); unsigned int ioread16(void *addr); unsigned int ioread32(void *addr);参数:addr是从 ioremap 获得的地址(可能包含一个整数偏移量)返回值: 从给定 I/O 内存读取的到的值相应的有一系列函数来写 I/O 内存: void iowrite8(u8 value, void *addr); void iowrite16(u16 value, void *addr); void iowrite32(u32 value, void *addr);参数:value 要写入IO内存值 addr 所要写入的IO内存地址2. 具体操作与实现2.1只使用ioremap映射GPIO寄存器实现LED点亮 内核模块代码(基于mini2440开发板的4个LED) 如下:/*/#include #include #include volatile unsigned long virt, phys;/用于存放虚拟地址和物理地址volatile unsigned long *GPBCON, *GPBDAT, *GPBUP;/用与存放三个寄存器的地址void led_device_init(void) / 0x56000000 + 0xd0 包揽全所有的IO引脚寄存器地址 phys = 0x56000000; / 0x56000000=GPACON /在虚拟地址空间中申请一块长度为0xd0的连续空间 /这样,物理地址phys到phys+0xd0对应虚拟地址virt到virt+0xd0 virt =(unsigned long)ioremap(phys, 0xd0); GPBCON = (unsigned long *)(virt + 0x10);/指定需要操作的三个寄存器的地址 GPBDAT = (unsigned long *)(virt + 0x14); GPBUP = (unsigned long *)(virt + 0x18); /led配置函数,这种是裸板控制寄存器的方式void led_configure(void) *GPBCON &= (3 10)&(312)&(3 14)&(316);/GPB12 defaule *GPBCON |= (1 10)|(112)|(114)|(116); /output *GPBUP |= (1 5)|(1 6)|(1 7)|(1 8); /禁止上拉电阻void led_on(void) /点亮led *GPBDAT &= (1 5)&(1 6)&(1 7)&(1 8); void led_off(void) /灭掉led *GPBDAT |= (1 5)|(1 6)|(1 7)|(1 8); static int _init test_init(void) /模块初始化函数 led_device_init(); /实现IO内存的映射 led_configure(); /配置GPB5-8为输出 led_on(); printk(hello led!n); return 0; static void _exit test_exit(void) /模块卸载函数 led_off(); iounmap(void *)virt); /撤销映射关系 printk(byen);module_init(test_init); module_exit(test_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(hrz.2ml); MODULE_VERSION(v0.1);/*/该模块在安装时点亮所有的LED灯,模块卸载时关闭所有LED灯。这个模块只使用了ioremap一个函数,把寄存器地址映射到虚拟地址,其余的操作都是基于裸板控制GPIO寄存器的方式。2.2使用ioremap映射GPIO寄存器,内核函数读写寄存器实现LED点亮上面模块的实现方式像我们在裸板上点亮LED一样,对于从裸机转到操作系统的同学,这样的控制方式很容易理解。但是,这样的方式不具有可移植性,我们应该尽量使用内核提供的IO内存访问接口,即我们上面介绍的函数: unsigned int ioread32(void *addr); void iowrite32(u32 value, void *addr);接着修改一下函数(蓝色字体为添加代码),以内核接口实现LED点亮:/*/#include #include #include volatile unsigned long virt, phys;/用于存放虚拟地址和物理地址volatile unsigned long *GPBCON, *GPBDAT, *GPBUP;/用与存放三个寄存器的地址unsigned long reg;void led_device_init(void) phys = 0x56000000; /指定物理地址 0x56000000=GPACON virt =(unsigned long)ioremap(phys, 0xd0); GPBCON = (unsigned long *)(virt + 0x10); GPBDAT = (unsigned long *)(virt + 0x14); GPBUP = (unsigned long *)(virt + 0x18); void led_configure(void) /led配置函数 /*GPBCON &= (3 10)&(312)&(3 14)&(316);/GPB12 defaule /*GPBCON |= (1 10)|(112)|(114)|(116); /output reg=ioread32(GPBCON);/读取GPBCON值到reg中 reg &= (3 10)&(312)&(3 14)&(316); reg |=(1 10)|(112)|(114)|(116); /output iowrite32(reg,GPBCON);/把修改后的reg值写回GPBCON /*GPBUP |= (1 5)|(1 6)|(1 7)|(1 8); /禁止上拉电阻 reg=ioread32(GPBUP); reg |= (1 5)|(1 6)|(1 7)|(1 8); /禁止上拉电阻 iowrite32(reg,GPBUP);void led_on(void) /点亮led /*GPBDAT &= (1 5)&(1 6)&(1 7)&(1 8); reg=ioread32(GPBDAT); reg &= (1 5)&(1 6)&(1 7)&(1 8); iowrite32(reg,GPBDAT); void led_off(void) /灭掉led /*GPBDAT |= (1 5)|(1 6)|(1 7)|(1 8); reg=ioread32(GPBDAT); reg |= (1 5)|(1 6)|(1 7)|(1 8); iowrite32(reg,GPBDAT); static int _init test_init(void) /模块初始化函数 led_device_init(); led_configure(); led_on(); printk(hello led!n); return 0; static void _exit test_exit(void) /模块卸载函数 led_off(); iounmap(void *)virt); printk(byen);module_init(test_init); module_exit(test_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(hrz.2ml); MODULE_VERSION(v0.2);/*/这里只是把访问内存的方式改了,由原来的一行代码变成三行。把模块安装到内核中,结果一样。2.3添加内存分配函数实现LED点亮在这个模块中添加request_mem_region()进行内存的分配。而调用request_mem_region()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。模块代码如下:/*/#include #include #include #include volatile unsigned long virt, phys;/用于存放虚拟地址和物理地址volatile unsigned long *GPBCON, *GPBDAT, *GPBUP;/用与存放三个寄存器的地址unsigned long reg;struct resource *led_resource;void led_device_init(void) phys = 0x56000000; virt =(unsigned long)ioremap(phys, 0xd0); GPBCON = (unsigned long *)(virt + 0x10); GPBDAT = (unsigned long *)(virt + 0x14); GPBUP = (unsigned long *)(virt + 0x18); void led_configure(void) /led配置函数 /*GPBCON &= (3 10)&(312)&(3 14)&(316);/GPB12 defaule /*GPBCON |= (1 10)|(112)|(114)|(116); /output reg=ioread32(GPBCON); reg &= (3 10)&(312)&(3 14)&(316); reg |=(1 10)|(112)|(114)|(116); /output iowrite32(reg,GPBCON); /*GPBUP |= (1 5)|(1 6)|(1 7)|(1 8); /禁止上拉电阻 reg=ioread32(GPBUP); reg |= (1 5)|(1 6)|(1 7)|(1 8); /禁止上拉电阻 iowrite32(reg,GPBUP);void led_on(void) /点亮led /*GPBDAT &= (1 5)&(1 6)&(1 7)&(1 8); reg=ioread32(GPBDAT); reg &= (1 5)&(1 6)&(1 7)&(1 8); iowrite32(reg,GPBDAT); void led_off(void) /灭掉led /*GPBDAT |= (1 5)|(1 6)|(1 7)|(1 8); reg=ioread32(GPBDAT); reg |= (1 5)|(1 6)|(1 7)|(1 8); iowrite32(reg,GPBDAT); static int _init test_init(void) /模块初始化函数 led_device_init(); led_resource=request_mem_region(phys,0xd0,LED_MEM); if(led_resource=NULL) printk(request mem for led error!n); return -ENOMEM; led_configure(); led_on(); printk(hello led!n); return 0; static void _exit test_exit(void) /模块卸载函数 if(led_resource!=NULL) led_off(); iounmap(void *)virt); release_mem_region(phys,0xd0); printk(byen);module_init(test_init); module_exit(test_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(hrz.2ml); MODULE_VERSION(v0.3);/*/结果还是一样。安装后执行:# cat /proc/iomem就会看到:56000000-560000cf : LED_MEM 2.4最终的LED驱动程序/*/#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include volatile unsigned long virt, phys;volatile unsigned long *GPBCON, *GPBDAT, *GPBUP;unsigned long reg;struct resource *led_resource;dev_t my_major=0;/主设备号#define MY_DEVNAME leds /设备名称/设备结构体struct my_led struct cdev my_cdev;struct my_led *my_ledp;struct class *my_class;void led_device_init(void) /0x56000000=GPACON 0x56000000 + 0xd0 包揽所有的IO引脚寄存器地址 phys = 0x56000000; /在虚拟地址空间中申请一块长度为0xd0的连续空间 /这样,物理地址phys到phys+0xd0对应虚拟地址virt到virt+0xd0 virt =(unsigned long)ioremap(phys, 0xd0); GPBCON = (unsigned long *)(virt + 0x10); GPBDAT = (unsigned long *)(virt + 0x14); GPBUP = (unsigned long *)(virt + 0x18); void led_configure(void) reg=ioread32(GPBCON); reg &= (3 10)&(312)&(3 14)&(316); reg |=(1 10)|(112)|(114)|(116); /output iowrite32(reg,GPBCON); reg=ioread32(GPBUP); reg |= (1 5)|(1 6)|(1 7)|(1 8); iowrite32(reg,GPBUP);void led_on(void) /*GPBDAT &= (1 5)&(1 6)&(1 7)&(1 8); reg=ioread32(GPBDAT); reg &= (1 5)&(1 6)&(1 7)&(1 4) return -EINVAL; reg=ioread32(GPBDAT); if(cmd=1) reg &= (1 (arg+5); if(cmd=0) reg |= (1 (arg+5); iowrite32(reg,GPBDAT); return 0; default: return -EINVAL; void led_off(void) reg=ioread32(GPBDAT); reg |= (1 5)|(1 6)|(1 7)|(1 8); iowrite32(reg,GPBDAT);static const struct file_operations my_led_fops= .owner=THIS_MODULE, .ioctl=leds_ioctl,;static int _init test_init(void) dev_t devno=MKDEV(my_major,0); int result; led_device_init(); led_resource=request_mem_region(phys,0xd0,LED_MEM); if(led_resource=NULL) printk(request mem for led error!n); return -ENOMEM; led_configure(); led_on(); /设备号申请 if(my_major) result=register_chrdev_region(devno,1,MY_DEVNAME); else result=alloc_chrdev_region

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论