linux设备驱动(10)-ioremap

使用ioremap函数控制LED

       前面我们已经知道了怎样通过GPIOlib库来控制我们的LED,并且也可以像控制51单片机一样在没有加载操作系统内核之前,直接访问寄存器来控制LED灯的闪烁,现在我们通过另外一种方法来控制LED灯,那就是通过ioremap函数将物理地址映射到虚拟地址上,进而操作寄存器。
       首先介绍几个常见的API:
request_mem_region

struct resource * __request_region(struct resource *parent,
				   resource_size_t start, resource_size_t n,
				   const char *name, int flags)
				   
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name))

作用:允许驱动程序程序申明自己需要操作的端口
start:需要申请的起始端口号
n:端口个数
name:驱动的名字

ioremap

static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size)
{
	return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
}

作用:将我们需要操作的物理地址映射到虚拟地址空间中
physaddr:我们需要使用的物理内存的首地址
size:我们需要使用的物理内存的大小
返回值:映射到虚拟内存后的虚拟内存地址

iounmap

void iounmap(void * addr);
作用:取消物理地址与虚拟地址之间的映射关系
addr:需要取消映射的虚拟内存地址

release_mem_region

#define release_mem_region(start,n)	__release_region(&ioport_resource, (start), (n))

作用:释放分配的IO端口范围。

示例代码如下:

led.h

/*************************************************************************
  > File Name:    led.h
  > Author:       Jia Xiang Hao
  > Description:  
  > Created Time: 2019年08月20日 星期二 03时53分40秒
 ************************************************************************/

#ifndef _LED_H
#define _LED_H

#define     __IO    volatile 
#define GPIO(x) ((GPIO_TypeDef *)(x))

typedef unsigned        char uint8_t;
typedef unsigned short  int uint16_t;
typedef unsigned        int uint32_t;

// 该结构体对数据手册提供的寄存器信息进行了封装。。。
typedef struct
{
	__IO uint32_t OUT;
	__IO uint32_t OUTENB;
	__IO uint32_t DETMODE0;
	__IO uint32_t DETMODE1;
	__IO uint32_t INTENB;//0x10
	__IO uint32_t DET;
	__IO uint32_t PAD;
	__IO uint32_t RSVD;
	__IO uint32_t ALTFN0;//0x20
	__IO uint32_t ALTFN1;
	__IO uint32_t MODEREX;//0x28
	__IO uint32_t RESV[4];
	__IO uint32_t DETENB;//0x3c
	__IO uint32_t SLEW;
	__IO uint32_t SLEW_DISABLE_DEFAULT;
	__IO uint32_t DRV1;
	__IO uint32_t DRV1_DISABLE_DEFAULT;
	__IO uint32_t DRV0;
	__IO uint32_t DRV0_DISABLE_DEFAULT;
	__IO uint32_t PULLSEL;
	__IO uint32_t PULLSEL_DISABLE_DEFAULT;
	__IO uint32_t PULLENB;
	__IO uint32_t PULLENB_DISABLE_DEFAULT;
}GPIO_TypeDef;


#endif //_LED_H

led.c

/*************************************************************************
  > File Name:    led.c
  > Author:       Jia Xiang Hao
  > Description:  
  > Created Time: 2019年08月20日 星期二 00时40分20秒
 ************************************************************************/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include "led.h"

void __iomem *gpioe_base = NULL;		// GPIOE的基地址
void __iomem *gpioc_base = NULL;		// GPIOC的基地址

int misc_dev_open(struct inode *nodp, struct file *filp)
{
	//申请端口
    request_mem_region(0xC001C000, 0x1000,"GPIOC_MEM");
    request_mem_region(0xC001E000, 0x1000,"GPIOE_MEM");
    
    // 映射GPIOE的首地址
    gpioe_base = ioremap(0xC001E000, 0x1000);
    if(gpioe_base == NULL)
    {
        printk(KERN_EMERG "ioremap failed\n");
        goto release2; 
    }
    // 映射GPIOC的首地址
    gpioc_base = ioremap(0xC001C000, 0x1000);
    if(gpioc_base == NULL)
    {
        printk(KERN_EMERG "ioremap failed\n");
        goto unmap; 
    }
    // 配置IO引脚功能 
    GPIO(gpioe_base)->ALTFN0 &= ~(3<<(13 * 2));//E13, GPIO模式默认是00所以不用再设置了
    
    GPIO(gpioc_base)->ALTFN1 &= ~(3<<(17 - 16) * 2);//C17
    GPIO(gpioc_base)->ALTFN1 |= 1<<((17 - 16) * 2);   // 设置为GPIO模式

    GPIO(gpioc_base)->ALTFN0 &= ~(3<< (8 * 2));//C8
    GPIO(gpioc_base)->ALTFN0 |= 1<<(8 * 2);   // 设置为GPIO模式
   
    GPIO(gpioc_base)->ALTFN1 &= ~(3<< (7 * 2));//C7
    GPIO(gpioc_base)->ALTFN1 |= 1<< (7 * 2);   // 设置为GPIO模式
    
    // 配置IO的方向为输出
    GPIO(gpioe_base)->OUTENB |= (13 << 1);//E13
    GPIO(gpioc_base)->OUTENB |= (17 << 1);//C17
    GPIO(gpioc_base)->OUTENB |= (7 << 1);//C7
    GPIO(gpioc_base)->OUTENB |= (8 << 1);//C8
    
    // 配置输出电平
    GPIO(gpioe_base)->OUT |= (1 << 13);// 熄灭LED灯
    GPIO(gpioc_base)->OUT |= (1 << 17);// 熄灭LED灯
    GPIO(gpioc_base)->OUT |= (1 << 7);// 熄灭LED灯
    GPIO(gpioc_base)->OUT |= (1 << 8);// 熄灭LED灯

    printk(KERN_INFO "misc open successful\n");
    return 0;
    
unmap:
    iounmap(gpioe_base);
release2:
    release_mem_region(0xC001E000, 0x1000);
    release_mem_region(0xC001C000, 0x1000);
    return -1;
}

int misc_dev_release(struct inode *nodp, struct file *filp)
{
    iounmap(gpioe_base);
    iounmap(gpioc_base);
    release_mem_region(0xC001E000, 0x1000);
    release_mem_region(0xC001C000, 0x1000);

    printk("misc close successful\n");
    return 0;
}

ssize_t misc_dev_read(struct file *filp, char __user *buf, size_t len, loff_t *offp)
{
    return 0;
}
/*
通过传输一个整型数字,用其低四个二进制位来控制四盏LED灯的亮灭
*/
ssize_t misc_dev_write(struct file *filp, const char __user *buf, size_t len, loff_t *offp)
{
    int cmd = 0;
    if(len != sizeof(int))
    {
        printk(KERN_EMERG "len is not approiate\n");
        return -1;
    }
    // 将数据从用户空间拷贝到内核空间
    if(0 !=copy_from_user(&cmd, (void __user *) buf, len))
    {
        printk(KERN_EMERG "copy_from_user failed\n");
        return -1;
    }
    if(cmd & 1)
    {
        GPIO(gpioe_base)->OUT &= ~(1 << 13);// 点亮LED灯
    }
    else
    {
        GPIO(gpioe_base)->OUT |= (1 << 13);// 熄灭LED灯
    }
    if(cmd & 2)
    {
        GPIO(gpioc_base)->OUT &= ~(1 << 17);// 熄灭LED灯
    }
    else
    {
        GPIO(gpioc_base)->OUT |= (1 << 17);// 熄灭LED灯
    }
     if(cmd & 4)
    {
        GPIO(gpioc_base)->OUT &= ~(1 << 8);// 熄灭LED灯
    }
    else
    {
        GPIO(gpioc_base)->OUT |= (1 << 8);// 熄灭LED灯
    }
    if(cmd & 8)
    {
        GPIO(gpioc_base)->OUT &= ~(1 << 7);// 熄灭LED灯
    }
    else
    {
        GPIO(gpioc_base)->OUT |= (1 << 7);// 熄灭LED灯
    }
    return 0;    
}

// 定义文件操作结构体
struct file_operations fops= {
    .owner = THIS_MODULE,
    .open  = misc_dev_open,
    .release = misc_dev_release,
    .read = misc_dev_read,
    .write = misc_dev_write
};

// 定义杂项设备结构体
struct miscdevice misc_led = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "misc_led",
    .fops = &fops
};

static int __init led_init(void)
{
    // 注册杂项设备
    int ret = 0;
    ret = misc_register(&misc_led);
    if(ret < 0)
    {
        printk(KERN_EMERG "misc_register failed\n");
        return -ret;
    }
    return 0;
}

static void __exit led_exit(void)
{
    int ret = 0;
    // 注销杂项设备
    ret = misc_deregister(&misc_led);
    if(ret < 0)
    {
        printk(KERN_EMERG "misc_register failed\n");
    }   
}


MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值