1.MMU简介
完成虚拟空间到物理空间的映射
内存保护设立存储器的访问权限,设置虚拟存储空间的缓冲特性
stm32点灯可以直接操作寄存器,但是linux点灯不能直接访问寄存器,linux会使能mmu
linux中操作的都是虚拟地址,要想访问物理地址0x0a就得先搞清楚0xa对应的虚拟地址
获得物理地址对应的虚拟地址使用ioremap函数,本质是个宏,参数分别是物理地址启始大小,要转换的字节数量
卸载驱动的时候用iounmap()卸载映射
stm32没有这个MMU其控制gpio直接操作寄存器就行,在linux上由于这个内存映射在,需要知道真实物理地址,反推其虚拟地址才能像stm32一样操作寄存器
linux做驱动有配置设备树更高级的操作方式,像这种操作寄存器的好似手动档
2.代码:
驱动:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define LED_MAJOR 200
#define LED_NAME "led"
#define PMU_GRF_BASE (0xFDC20000)
#define PMU_GRF_GPIO0C_IOMUX_L (PMU_GRF_BASE + 0x0010)
#define PMU_GRF_GPIO0C_DS_0 (PMU_GRF_BASE + 0X0090)
#define GPIO0_BASE (0xFDD60000)
#define GPIO0_SWPORT_DR_H (GPIO0_BASE + 0X0004)
#define GPIO0_SWPORT_DDR_H (GPIO0_BASE + 0X000C)
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem *PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem *GPIO0_SWPORT_DR_H_PI;
static void __iomem *GPIO0_SWPORT_DDR_H_PI;
static int led_open(struct inode* inode, struct file* filp){
return 0;
}
static int led_release(struct inode* inode, struct file* filp){
return 0;
}
static ssize_t led_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos){
return 0;
}
/* 字符设备操作集*/
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.write = led_write,
.open = led_open,
.release = led_release,
};
/*注册驱动加载卸载*/
static int __init led_init(void){ // 入口
// 初始化led灯
int ret = 0;
u32 val = 0;
PMU_GRF_GPIO0C_IOMUX_L_PI = ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4);
PMU_GRF_GPIO0C_DS_0_PI = ioremap(PMU_GRF_GPIO0C_DS_0, 4);
GPIO0_SWPORT_DR_H_PI = ioremap(GPIO0_SWPORT_DR_H, 4);
GPIO0_SWPORT_DDR_H_PI = ioremap(GPIO0_SWPORT_DDR_H, 4);
// 初始化
// 设置GPIO0_c0为GPIO功能
val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);
val &= ~(0x7 << 0); //最低三位置0
val |= ((0x7 << 16) | (0x0 << 0)); // 16 17 18位置1其他不变,bit2:0:0,用作GPIO0_C0
writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);
// 设置GPIO_C0驱动能力为level5
val = readl(PMU_GRF_GPIO0C_DS_0_PI);
val &= ~(0x3f << 0); // 0 ~ 5置0
val |= ((0x3f << 16) | (0x3f << 0)); // 16 ~ 21置1,0~5置1同时用作GPIO0c0
writel(val, PMU_GRF_GPIO0C_DS_0_PI);
// 设置GPIOO0_c0为输出
val = readl(GPIO0_SWPORT_DDR_H_PI);
val &= ~(0x1 << 0); // 0置0
val |= ((0x1 << 16) | (0x1 << 0)); // 16置1,0置1
writel(val, GPIO0_SWPORT_DDR_H_PI);
// 设置GPIO_c0为低电平,关闭LED
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0x1 << 0);
val |= ((0x1 << 16) | (0x0 << 0));
writel(val, GPIO0_SWPORT_DR_H_PI);
// 开灯
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0,
bit0,高电平*/
writel(val, GPIO0_SWPORT_DR_H_PI);
// 注册字符设备
ret = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
if(ret < 0){
printk("register chrdev failed!\r\n");
return -EIO;
}
printk("led_init\r\n");
return 0;
}
static void __exit led_exit(void){ // 出口
u32 val = 0;
// 关灯
val = readl(GPIO0_SWPORT_DR_H_PI);
val &= ~(0X1 << 0); /* bit0 清零*/
val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0,
bit0,低电平 */
writel(val, GPIO0_SWPORT_DR_H_PI);
// 取消地址映射
iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);
iounmap(PMU_GRF_GPIO0C_DS_0_PI);
iounmap(GPIO0_SWPORT_DR_H_PI);
iounmap(GPIO0_SWPORT_DDR_H_PI);
// 注销
unregister_chrdev(LED_MAJOR, LED_NAME);
printk("led_exit\r\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Narnat");
ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4);将物理地址转虚拟内存
在程序挂载时初始化一些寄存器,并点亮led灯,卸载时关闭led灯
3.现象: