建快递网站需要多少钱,网站接做网站单,网站建设重点是什么,网站建设越来越难做x86体系和ARM体系的寻址方式是有差别的#xff1a;
在x86下#xff0c;为了能够满足CPU高速地运行#xff0c;内存与CPU之间通过北桥相连并通过地址方式访问#xff0c;而外设通过南桥与CPU相连并通过端口访问。
在ARM下也实现了类似的操作#xff0c;通过两条不同的总线…x86体系和ARM体系的寻址方式是有差别的
在x86下为了能够满足CPU高速地运行内存与CPU之间通过北桥相连并通过地址方式访问而外设通过南桥与CPU相连并通过端口访问。
在ARM下也实现了类似的操作通过两条不同的总线AHBBUS和APBBUS来连接不同访问速度的外设。但是它与x86不同无论是内存还是外设ARM都是通过地址访问。
因为这两种访问方式的不同linux分出了两种不同的访问操作
以地址方式访问硬件——使用IO内存操作。
以端口方式访问硬件——使用IO端口操作。 在ARM下访问寄存器就像访问内存一样——从指定的寄存器地址获取数据修改。所以ARM下一般是使用IO内存的操作。但这并不是说IO端口的操作在ARM下不能用它们的代码差不多只是没有使用的必要下面也将介绍IO内存操作。 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 二、如何使用IO内存获得硬件的地址 之前已经说过不能在linux使用实际的物理地址要对指定的物理地址进行操作必须要先将物理地址与虚拟地址对应通过虚拟地址访问。于是有了以下的物理地址映射函数 #include void*ioremap(unsigned long phys_addr, unsigned long size);
其实这也是上一节介绍的内存分配的一种方式它同样会建立新页表来管理虚拟地址。函数传入两个参数需要访问的物理内存寄存器的首地址phys_addr和这段内存区域的大小size返回与该段物理地址对应的虚拟地址。这段地址可以多次被映射当然每次映射的虚拟地址也不一样。
对应的也有撤销映射关系的函数 voidioumap(void *addr); 接下来我将会从一个裸板的ARMled驱动开始讲解linux下的操作和裸板有什么不一样。
我的ARM裸板程序是在linux下编写的我不知道这跟win下使用ADS有什么区别在裸板驱动中一般我是通过这样的办法来操作寄存器的
首先先给个地址定义个容易记的名字 #defineGPECON *(volatile unsigned long *) 0x56000040
接着我就要操作这个GPECON寄存器了 *GPECON ~(3 24); //将24和25位清零 *GPECON| (1 24); //将24和25位分别赋值为1、0
可以看到操作寄存器其实就是拿个地址出来进行操作。其实在linux下也是一样只是操作的时候不能使用物理地址需要用映射出来的虚拟地址。
上个函数这个程序我将要点亮连在我开发板上的led灯这个灯接在我开发板的GPE12上如果需要下载程序运行需要改一下接口。 /*5th_mm_2/1st/test.c*/ 1#include 2#include 3 4#include //上面介绍的函数需要包含该头文件 5 6volatile unsigned long virt, phys;//用于存放虚拟地址和物理地址 7volatile unsigned long *GPECON, *GPEDAT, *GPEUP;//用与存放三个寄存器的地址 8 9void led_device_init(void) 10{ 11 phys 0x56000000;//1、指定物理地址 12 virt (unsigned long)ioremap(phys,0x0c);//2、通过ioremap获得对应的虚拟地址 13//0x0c表示只要12字节的大小 14 GPECON (unsigned long *)(virt 0x40);//3、指定需要操作的三个寄存器的地址 15 GPEDAT (unsigned long *)(virt 0x44); 16 GPEUP (unsigned long *)(virt 0x48); 17} 18 19void led_configure(void)//led配置函数 20{ 21 *GPECON ~(3 24);//配置GPE12为输出端口 22 *GPECON | (1 24);//先清零再赋值 23 24 *GPEUP | (1 12);//禁止上拉电阻 25} 26 27void led_on(void)//点亮led 28{ 29 *GPEDAT ~(1 12); 30} 31 32void led_off(void)//灭掉led 33{ 34 *GPEDAT | (1 12); 35} 36 37static int __init test_init(void) //模块初始化函数 38{ 39 led_device_init(); 40 led_configure(); 41 led_on(); 42 printk(hello led!\n); 43 return 0; 44} 45 46static void __exit test_exit(void) //模块卸载函数 47{ 48 led_off(); 49 iounmap((void *)virt);//注意即使取消了映射通过之前的虚拟地址还能访问硬件 50 printk(bye\n);//但不是肯定可以只要该虚拟地址被内核改动后就不行了。 51} 52 53module_init(test_init); 54module_exit(test_exit); 55 56MODULE_LICENSE(GPL); 57MODULE_AUTHOR(xoao bai); 58MODULE_VERSION(v0.1);
从上面的程序可以看到除了获得地址有点和裸板驱动不一样外寄存器的操作还是一样的。
接下来验证一下 [root:1st]# insmod test.ko helloled!//这时候灯亮了 [root:1st]# rmmod test bye//灯灭了 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 三、改进函数使用更好的内存访问接口 为了实现更好的移植性上面的程序就有缺陷了。内核建议尽量使用内核提供的内存访问接口 #include //从内存读取数据,返回值是指定内存地址中的值 unsignedint ioread8(void *addr) unsignedint ioread16(void *addr) unsignedint ioread32(void *addr) //往指定内存地址写入数据 voidiowrite8(u8 value, void *addr) voidiowrite16(u16 value, void *addr) voidiowrite32(u32 value, void *addr)
一般常用的是32位内存存取接口。
接下来就改进一下函数其实实质没有改变上面的函数是根据对应的平台体系结构编写的这样可以提高驱动的移植性。 /*5th_mm_2/1st/test.c*/ 1#include 2#include 3 4#include 5#include 6 7volatile unsigned long virt, phys; 8volatile unsigned long *GPECON, *GPEDAT, *GPEUP; 9unsigned long reg; 10 11void led_device_init(void) 12{ 13 phys 0x56000000; 14 virt (unsigned long)ioremap(phys,SZ_16);//这里只是想介绍一下在asm/sizes.h中有一下 15//定义好用来表示内存大小的宏这里其实我只 16 GPECON (unsigned long *)(virt 0x40);//需要12个字节并不需要16个字节。 17 GPEDAT (unsigned long *)(virt 0x44); 18 GPEUP (unsigned long *)(virt 0x48); 19} 20 21void led_configure(void) 22{ 23 //*GPECON ~(3 24); 24 //*GPECON | (1 24); 25 reg ioread32(GPECON); 26 reg ~(3 24); 27 reg | (1 24); 28 iowrite32(reg, GPECON); 29 30 //*GPEUP | (1 12); 31 reg ioread32(GPEUP); 32 reg ~(3 12); 33 iowrite32(reg, GPEUP); 34} 35 36void led_on(void) 37{ 38 //*GPEDAT ~(1 12); 39 reg ioread32(GPEDAT); 40 reg ~(1 12); 41 iowrite32(reg, GPEDAT); 42} 43 44void led_off(void) 45{ 46 //*GPEDAT | (1 12); 47 reg ioread32(GPEDAT); 48 reg | (1 12); 49 iowrite32(reg, GPEDAT); 50} 51 52static int __init test_init(void) //模块初始化函数 53{ 54 led_device_init(); 55 led_configure(); 56 led_on(); 57 printk(hello led!\n); 58 return 0; 59} 60 61static void __exit test_exit(void) //模块卸载函数 62{ 63 led_off(); 64 iounmap((void *)virt); 65 printk(bye\n); 66} 67 68module_init(test_init); 69module_exit(test_exit); 70 71MODULE_LICENSE(GPL); 72MODULE_AUTHOR(xoao bai); 73MODULE_VERSION(v0.1);
会发现发现程序将原来直接访问内存的一句话变成了3句话其他都没有改变。
我就不验证了效果其实是一样的。 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 四、再改进一下程序 在使用IO内存映射操作之前其实还可以添加一个步骤分配内存区域。 #include structresource *request_mem_region(unsigned long start, unsinged long len,char *name)
该函数从start开始分配len字节长的内存空间。如果成功返回一个结构体指针但这结构体我们没必要用如果失败返回NULL。成功后可以在.proc/iomem查看到name的信息。
其实调用request_mem_region()不是必须的但是建议使用。该函数的任务是检查申请的资源是否可用如果可用则申请成功并标志为已经使用其他驱动想再申请该资源时就会失败。
如果不再使用需要调用释放函数 voidrelease_mem_region(unsigned long start, unsigned long len) 现在把这两个函数加上去 /*5th_mm_2/3rd/test.c*/ 1#include 2#include 3 4#include 5#include 6 7volatile unsigned long virt, phys; 8volatile unsigned long *GPECON, *GPEDAT, *GPEUP; 9unsigned long reg; 10struct resource *led_resource; 11 12void led_device_init(void) 13{ 14 phys 0x56000000; 15 virt (unsigned long)ioremap(phys, 0x0c); 16 17 GPECON (unsigned long *)(virt 0x40); 18 GPEDAT (unsigned long *)(virt 0x44); 19 GPEUP (unsigned long *)(virt 0x48); 20} 21 22void led_configure(void) 23{ 24 reg ioread32(GPECON); 25 reg ~(3 24); 26 reg | (1 24); 27 iowrite32(reg, GPECON); 28 29 reg ioread32(GPEUP); 30 reg ~(3 12); 31 iowrite32(reg, GPEUP); 32} 33 34void led_on(void) 35{ 36 reg ioread32(GPEDAT); 37 reg ~(1 12); 38 iowrite32(reg, GPEDAT); 39} 40 41void led_off(void) 42{ 43 reg ioread32(GPEDAT); 44 reg | (1 12); 45 iowrite32(reg, GPEDAT); 46} 47 48static int __init test_init(void) //模块初始化函数 49{ 50 led_device_init(); 51 52 led_resource request_mem_region(phys, 0x0c, LED_MEM); 53 if(NULL led_resource){ 54 printk(request mem error!\n); 55 return - ENOMEM; 56 } 57 58 led_configure(); 59 led_on(); 60 printk(hello led!\n); 61 return 0; 62} 63 64static void __exit test_exit(void) //模块卸载函数 65{ 66 if(NULL! led_resource){ 67 led_off(); 68 iounmap((void *)virt); 69 release_mem_region(phys, 0x0c); 70 } 71 printk(bye\n); 72} 73 74module_init(test_init); 75module_exit(test_exit); 76 77MODULE_LICENSE(GPL); 78MODULE_AUTHOR(xoao bai); 79MODULE_VERSION(v0.1);
写完就得验证一下 [root:3rd]# insmod test.ko helloled!//灯亮了 [root:3rd]# cat /proc/iomem 19000300-19000310: cs8900 19000300-19000310: cs8900 。。。。 56000000-5600000b: LED_MEM//看到了 57000000-570000ff: s3c2410-rtc 57000000-570000ff: s3c2410-rtc 5a000000-5a0fffff: s3c2440-sdi [root:3rd]# rmmod test bye//灯灭了 [root:3rd]# cat /proc/iomem //LED_MEM不见了 19000300-19000310: cs8900 19000300-19000310: cs8900 。。。。。。 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 五、总结 今天介绍的内容不多其实就几个函数下面重温一下使用IO内存的步骤 其中第一步和最后一步可以不做。