使用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);