ioread32函数有关知识

x86体系和ARM体系的寻址方式是有差别的:

x86下,为了能够满足CPU高速地运行,内存与CPU之间通过北桥相连并通过地址方式访问,而外设通过南桥与CPU相连并通过端口访问。

ARM下也实现了类似的操作,通过两条不同的总线(AHBBUSAPBBUS)来连接不同访问速度的外设。但是它与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); //2425位清零

*GPECON|= (1 << 24); //2425位分别赋值为10

可以看到,操作寄存器其实就是拿个地址出来进行操作。其实在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内存的步骤:

其中第一步和最后一步可以不做。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

几何分布的期望和方差公式推导_超几何分布的数学期望与方差推导

考虑 个外表相同的物品&#xff0c;其中有 个同类物品与另一类的 个物品&#xff1b;抽取 个物品&#xff0c;每个物品的抽取等概率随机。上述便是一个超几何分布&#xff08;Hypergeometric Distribution&#xff09;的基本模型。抽取 个 类物品的概率 在研究超几何分布的数学…

FPGA开发全攻略—— 调试

原文链接&#xff1a; FPGA开发全攻略连载之十三&#xff1a;FPGA实战开发技巧&#xff08;12&#xff09; 5.6 大规模设计的调试经验 在大规模设计的调试应该按照和设计理念相反的顺序&#xff0c;从底层测试&#xff0c;主要依靠ChipScope Pro 工具。下面主要介绍ChipScope P…

003很好的网络博客(TCP/IP)-很全

http://www.cnblogs.com/obama/p/3292335.html 很全的计算机网络方面的资料。

linux驱动文件操作简单介绍

1、设备号 主设备号标识设备对应的驱动程序&#xff0c;次设备号由内核使用&#xff0c;用于确定设备文件所指的设备。 通过次设备号获得一个指向内核设备的直接指针&#xff0c;也可将此设备号当作设备本地数组的索引。 设备编号用dev_t表示&#xff08;Linux/types.h 32位…

php学习笔记细节部分。

<?phpclass Car{public function Car()//构造函数 {//codeing 当实例化类的时候会调用这里面的方法}public function _destruct()//析构函数 {//codeing 对象的所有引用被删除&#xff0c;或者对象被显式的销毁时会执行的函数。 }} l转载于:https://www.cnblogs.com/OnlyL…

ctypes python_[python学习之路]ctypes,Python

ctypes 基本用法 ctypes 是一个方便 Python 调用本地已经编译好的外部库的模块。 from ctypes import util, CDLL 标准 C 库 使用 util 来找到标准 C 库&#xff1a; libc_name util.find_library(c) # on WINDOWS print libc_name msvcr90.dll 使用 CDLL 来加载 C 库&#xf…

oracle创建表空间和用户授权

确定数据文件的存储地址&#xff0c;有两种情况&#xff0c;一个是明确存储地址&#xff0c;一个是跟其他表空间存在一个地方&#xff0c;但是不知道地址&#xff0c;这时候可以根据如下SQL进行查找&#xff1a;select t.* from sys.dba_data_files t where t.tablespace_name…

linux驱动 cdev,inode结构体

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的 数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程…

如何在VS和CB中配置MySQL环境

这里&#xff0c;由于我的MySQL安装在D盘 MY SQL\MySQL Server 5.6该路径下&#xff0c;所以后面的路径均以D:\MY SQL\MySQL Server 5.6开头 在VS中配置MySQL环境 包含目录&#xff1a; D:\MY SQL\MySQL Server 5.6\include 库目录&#xff1a;D:\MY SQL\MySQL Server 5.…

点乘和叉乘的区别_关于延时和混响的区别与专用延时器与混响器的调控技巧

关于延时和混响的区别与专用延时器与混响器的调控技巧延时器与混响器是模拟室内声场声音信号特性的专用设备。在录音节目制作中&#xff0c;延时器和混响器可以在模拟的艺术声场中传递时间、空间、方位、距离等重要信息&#xff0c;并且可以制作某些特殊效果。延时器与混响器工…

zabbix监控工具

实验环境网关 classroom 172.25.8.254workstation 172.25.8.9server a-jeth0 172.25.8.10-外网eth1 192.168.0.x内网eth2 192.168.1.x备用servera:webservere:server--------------------------------------------需求&#xff1a;监控设计&#xff1a;原理&#xff1a;硬件&…

linux驱动 自旋锁

最近在内核频繁使用了自旋锁&#xff0c;自旋锁如果使用不当&#xff0c;极易引起死锁&#xff0c;在此总结一下。 自旋锁是一个互斥设备&#xff0c;它只有两个值&#xff1a;“锁定”和“解锁”。它通常实现为某个整数值中的某个位。希望获得某个特定锁得代码测试相关的位。…

百度输入法

[用户界面]&#xff1a;界面美观&#xff0c;有不同画风的ui界面&#xff0c;适合不同消费群体的需求。 [记住用户选择]&#xff1a;会默认记住用户以往的输入习惯&#xff0c;只需输入首拼音字母就会出现过去使用的高频词。 [短期刺激]&#xff1a;美化的用户界面让人眼前一新…

rs232读取智能电表_三相电表怎么看度数 怎么计算总电量

现在的三相电表一般都是在屏幕上面直接看&#xff0c;屏幕上面是有文字提示显示的&#xff0c;三相电表的总度数&#xff0c;上面的文字提醒一般是“正向有功总电量”&#xff0c;三相电表一般屏幕旁边都会上翻键和下翻键&#xff0c;可以上下翻开电表里面的数据。如下图所示&a…

android 学习随笔十六(广播 )

1、广播接收者 BroadcastReceiver接收系统发出的广播现实中的广播&#xff1a;电台为了传达一些消息&#xff0c;而发送的广播&#xff0c;通过广播携带要传达的消息&#xff0c;群众只要买一个收音机&#xff0c;就可以收到广播了 Android中的广播&#xff1a;系统在运行过程中…

驱动面试题总结

1、字符型驱动设备你是怎么创建设备文件的&#xff0c;就是/dev/下面的设备文件&#xff0c;供上层应用程序打开使用的文件&#xff1f; 答&#xff1a;mknod命令结合设备的主设备号和次设备号&#xff0c;可创建一个设备文件。 评&#xff1a;这只是其中一种方式&#xff0…

python程序写诗_将Python诗歌与D结合起来

在与docker一起使用poetry时&#xff0c;需要记住以下几点。 安装 安装poetry的正式方法是通过&#xff1a;curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python 这种方式允许poetry及其依赖项与依赖项分离。但是&#xff0c;在我看来…

Azure运维系列 4:安装和使用Azure PowerShell管理云

前面讲到了很多的管理方式&#xff0c;包括Azure中国最近更新的预览版管理门户和Azure云助理&#xff0c;都是非常不错的管理Azure的方式&#xff0c;今天我们再来介绍一种更加高效的管理方式Azure PowerShell。熟悉命令行的朋友都知道&#xff0c;Linux之所以那么好用是因为其…

[转]char * 和字符数组

[转]char * 和字符数组 原文地址&#xff1a;http://www.cnblogs.com/jeakon/archive/2012/05/27/2816809.html 代码中的int * i就是我们关注的焦点。它是一个指向int指针。也就是说&#xff1a;i指向一个内存地址&#xff0c;从这个地址开始存储了一个数据。int * i中的int标明…

设备模型1

作为开头篇&#xff0c;我不想写HELLLOWORLD驱动&#xff0c;甚至字符设备驱动的开发&#xff0c;这样文章充斥在各大网站上的博客上&#xff0c;随便搜搜&#xff0c;就可以找到几百篇。这是最基本的东西&#xff0c;通过这些内容的学习&#xff0c;我们要掌握LINUX驱动的基本…