在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
20150222 IO端口映射和IO内存映射(详解S3C24XX_GPIO驱动) 2015-02-22 李海沿 刚刚我们实现了linux系统内存的分配,读写,释放功能,下面,我们一鼓作气将IO端口映射及IO内存映射搞定,加油!
1)物理地址:CPU地址总线传来的地址,由硬件电路控制其具体含义。物理地址中很大一部分是留给内存条中的内存的,但也常被映射到其他存储器上(如显存、BIOS等)。在程序指令中的虚拟地址经过段映射和页面映射后,就生成了物理地址,这个物理地址被放到CPU的地址线上。 3)虚拟地址:现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要MMU(Memory Management Unit)的支持。MMU通常是CPU的一部分,如果处理器没有MMU,或者有MMU但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被内存芯片(物理内存)接收,这称为物理地址(Physical Address),如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address),而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将虚拟地址映射成物理地址。
统一编址:外设接口中的IO寄存器(即IO端口)与主存单元一样看待,每个端口占用一个存储单元的地址,将主存的一部分划出来用作IO地址空间,如,在 PDP-11中,把最高的4K主存作为IO设备寄存器地址。端口占用了存储器的地址空间,使存储量容量减小。 独立编址(单独编址):IO地址与存储地址分开独立编址,I/0端口地址不占用存储空间的地址范围,这样,在系统中就存在了另一种与存储地址无关的IO地址,CPU也必须具有专用与输入输出操作的IO指令(IN、OUT等)和控制逻辑。独立编址下,地址总线上过来一个地址,设备不知道是给IO端口的、还是给存储器的,于是处理器通过MEMR/MEMW和IOR/IOW两组控制信号来实现对I/O端口和存储器的不同寻址。如,intel 80x86就采用单独编址,CPU内存和I/O是一起编址的,就是说内存一部分的地址和I/O地址是重叠的。
不过Intel x86平台普通使用了名为内存映射(MMIO)的技术,该技术是PCI规范的一部分,IO设备端口被映射到内存空间,映射后,CPU访问IO端口就如同访问内存一样。看Intel TA 719文档给出的x86/x64系统典型内存地址分配表:
对于某一既定的系统,它要么是独立编址、要么是统一编址,具体采用哪一种则取决于CPU的体系结构。 如,PowerPC、m68k等采用统一编址,而X86等则采用独立编址,存在IO空间的概念。目前,大多数嵌入式微控制器如ARM、PowerPC等并 不提供I/O空间,仅有内存空间,可直接用地址、指针访问。但对于Linux内核而言,它可能用于不同的CPU,所以它必须都要考虑这两种方式,于是它采 用一种新的方法,将基于I/O映射方式的或内存映射方式的I/O端口通称为"I/O区域"(I/O region),不论你采用哪种方式,都要先申请IO区域:request_resource(),结束时释放 它:release_resource()。 2)对外设的访问 1、访问I/O内存的流程是:request_mem_region() -> ioremap() -> ioread8()/iowrite8() -> iounmap() ->release_mem_region() 。 2、访问I/O端口 Linux管理IO region: 1) request_region() 把一个给定区间的IO端口分配给一个IO设备。 2) check_region() 检查一个给定区间的IO端口是否空闲,或者其中一些是否已经分配给某个IO设备。 3) release_region() 释放以前分配给一个IO设备的给定区间的IO端口。 Linux中可以通过以下辅助函数来访问IO端口: inb(),inw(),inl(),outb(),outw(),outl() "b""w""l"分别代表8位,16位,32位。 对IO内存资源的访问 1) request_mem_region() 请求分配指定的IO内存资源。 2) check_mem_region() 检查指定的IO内存资源是否已被占用。 3) release_mem_region() 释放指定的IO内存资源。 其中传给函数的start address参数是内存区的物理地址(以上函数参数表已省略)。 驱动开发人员可以将内存映射方式的IO端口和外设内存统一看作是IO内存资源。 ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB - 4GB)中,参数addr是指向内核虚地址的指针。 Linux中可以通过以下辅助函数来访问IO内存资源: readb(),readw(),readl(),writeb(),writew(),writel()。 Linux在kernel/resource.c文件中定义了全局变量ioport_resource和iomem_resource,来分别描述基于IO映射方式的整个IO端口空间和基于内存映射方式的IO内存资源空间(包括IO端口和外设内存)。 1)关于IO与内存空间: (2)inb和outb: 在Linux设备驱动中,宜使用Linux内核提供的函数来访问定位于I/O空间的端口,这些函数包括:
以上知识点摘自: http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/20-2556.html 这儿我不献丑了,因为上面的已经写的很好了。
(四)Linux下的IO端口和IO内存 由于我本人对IMX257的IO内存分配不太了解,曾经询问过很多周立功公司那些所谓的技术人员,每个人回答的答案都是,"你好,我们提供的资料就只是官网上的光盘资料了哦",每次碰到这种话,我真的很无语,我只能说,好吧,只能说是我智商太低了,那些资料太高深了。 好了,废话也不多说了,这次,我就针对S3C24XX的gpio驱动程序来讲解io端口的分配。
1. 定义一些寄存器(数据寄存器,配置寄存器) 上图中,gpio_va就是我们GPIO的基址,是用来确定GPFCON配置寄存器和GPFDAT数据寄存器的指针地址的 ,其初始化我们在init函数中会讲解。
2.在init函数中确定gpio基址 前面我们寄存器的正常作用的前提就是gpio_va这个基址,所以我们在init函数中分配IO端口 如图所示,将gpio的地址0x56000000这个io端口分配为gpio_va,这样也就确定了,前面的配置寄存器和数据寄存器。
3.在open函数中配置GPIO 和单片机程序一样,使用GPIO都要先配置GPIO,原理一样: 给GPIO引脚赋初值,以后对GPIO引脚的操作,直接用这个寄存器赋值就可以了
4.在exit函数中释放IO端口 使用完GPIO引脚后,要将我们申请的IO端口释放掉。
总结一下,很简单,申请,配置,释放 申请IO端口 à 配置GPIO端口为输出(入) à 给数据寄存器赋值(读取) à 释放IO端口 附上驱动程序: 1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/fs.h> 4 #include <linux/init.h> 5 #include <linux/delay.h> 6 #include <asm/uaccess.h> 7 #include <asm/irq.h> 8 #include <asm/io.h> 9 #include <asm/arch/regs-gpio.h> 10 #include <asm/hardware.h> 11 12 #define DEVICE_NAME "leds" 13 /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */ 14 #define LED_MAJOR 231 /* 主设备号 */ 15 16 static struct class *leds_class; 17 static struct class_device *leds_class_devs[4]; 18 19 /* bit0<=>D10, 0:亮, 1:灭 20 * bit1<=>D11, 0:亮, 1:灭 21 * bit2<=>D12, 0:亮, 1:灭 22 */ 23 static char leds_status = 0x0; 24 static DECLARE_MUTEX(leds_lock); // 定义赋值 25 26 //static int minor; 27 static unsigned long gpio_va; //gpio基址 28 #define GPIO_OFT(x) ((x) - 0x56000000) 29 //GPIO 配置寄存器 30 #define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050))) 31 //GPIO 数据寄存器 32 #define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054))) 33 /* 应用程序对设备文件/dev/leds执行open(...)时, 34 * 就会调用s3c24xx_leds_open函数 35 */ 36 static int s3c24xx_leds_open(struct inode *inode, struct file *file) 37 { 38 int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); 39 switch(minor) 40 { 41 case 0: /* /dev/leds */ 42 { 43 // 配置3引脚为输出 44 //s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); 45 GPFCON &= ~(0x3<<(4*2)); 46 GPFCON |= (1<<(4*2)); 47 48 //s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); 49 GPFCON &= ~(0x3<<(5*2)); 50 GPFCON |= (1<<(5*2)); 51 52 //s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); 53 GPFCON &= ~(0x3<<(6*2)); 54 GPFCON |= (1<<(6*2)); 55 56 // 都输出0 57 //s3c2410_gpio_setpin(S3C2410_GPF4, 0); 58 GPFDAT &= ~(1<<4); 59 60 //s3c2410_gpio_setpin(S3C2410_GPF5, 0); 61 GPFDAT &= ~(1<<5); 62 //s3c2410_gpio_setpin(S3C2410_GPF6, 0); 63 GPFDAT &= ~(1<<6); 64 65 down(&leds_lock); 66 leds_status = 0x0; 67 up(&leds_lock); 68 69 break; 70 } 71 72 case 1: 全部评论
专题导读
上一篇:C#处理应用程序减少内存占用SetProcessWorkingSetSize减少内存占用发布时间:2022-07-13下一篇:C#讀取帶NameSpace的XML发布时间:2022-07-13热门推荐
热门话题
阅读排行榜
|
请发表评论