• ioremap()


    活动地址:CSDN21天学习挑战赛

    前言

    之前,我们使用 devmem 命令的方式,直接操作寄存器,控制 LED 的亮灭。
    今天,我们在内核中使用 ioremap() 来实现该功能。

    物理地址 vs 虚拟地址

    在嵌入式 Linux 中,通常会将外设的寄存器地址,映射到物理内存地址上,方便统一管理。
    但是,不管是内核空间代码,还是用户空间代码,访问的都是虚拟地址

    *(int *)0x87800000 = 100;
    
    • 1

    上述代码,将地址为 0x87800000 的内存赋值 100。但是,你需要知道的是,这个 0x87800000 地址,是个虚拟内存地址,它和实际物理内存地址 0x87800000 没有半毛钱关系。虚拟地址 0x87800000 会经过 mmc 映射到物理地址上,这个物理地址是多少,我们不用关心,也无法左右,可能是 0x00008888,也可能是 0x77776666,都不影响程序运行。
    如果我们想要人为地和某个物理地址有联系该怎么办呢?比如说想要读写某一寄存器。这时候就要用到 ioremap() 了。

    ioremap()

    Linux 在 asm/io.h 中声明了 ioremap 函数,用来将 I/O 内存资源的物理地址映射到核心虚拟地址空间(内核空间:3GB~4GB)中,原型如下

    #define ioremap(offset, size)		__ioremap((offset), (size), 0)
    void __iomem *__ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
    
    • 1
    • 2

    参数:
    phys_addr:要映射的起始的 I/O 地址
    size:要映射的空间的大小
    flags:要映射的 I/O 空间和权限有关的标志

    返回值:
    返回映射后的内核虚拟地址(3G-4G)

    接着便可以通过读写该返回的内核虚拟地址去访问这段 I/O 内存资源。

    示例

    led_drv.c

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define GPIO_CLR_REG ((__u32)0x3f200028)
    #define GPIO_SET_REG ((__u32)0x3f20001C)
    static volatile __u32 *gpio_led_reg;
    
    static int led_init(void)
    {
    	printk("led on\n");
    
    	gpio_led_reg = (__u32 *)ioremap(GPIO_CLR_REG, 4);
    	writel(0x7F, gpio_led_reg);
    
    	return 0;
    }
    
    static void led_exit(void)
    {
    	printk("led off\n");
    
    	gpio_led_reg = (__u32 *)ioremap(GPIO_SET_REG, 4);
    	writel(0x7F, gpio_led_reg);
    }
    
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    Makefile

    obj-m = led_drv.o
    
    KDIR=/home/liyongjun/project/board/buildroot/RPi3/build/linux-custom
    CROSS_COMPILE=/home/liyongjun/project/board/buildroot/RPi3/host/bin/arm-buildroot-linux-uclibcgnueabihf-
    
    all:
    	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
    clean:
    	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行

    编译后将 led_drv.ko 上传到树莓派,在树莓派上安装、卸载。

    # insmod led_drv.ko 
    [ 3168.850425] led on
    
    • 1
    • 2

    在这里插入图片描述

    # rmmod led_drv.ko 
    [ 3242.458328] led off
    
    • 1
    • 2

    在这里插入图片描述

  • 相关阅读:
    【自动驾驶】ROS远程节点的分布式通信
    MySQL基础篇1
    转载: 又拍云【PrismCDN 】低延时的P2P HLS直播技术实践
    java中的异常
    MFC上下文菜单与定时器学习笔记
    C++基础知识精髓教程
    C#应用处理传入参数 - 开源研究系列文章
    TCO-PNB ester,1438415-89-4, 反式环辛烯-对硝基苯
    hudi系列-借助hudi优化架构
    4步消除漏洞积压
  • 原文地址:https://blog.csdn.net/lyndon_li/article/details/126277274