1、概述
- mmap函数可以把物理地址映射到用户空间,对映射到用户空间的地址读写即可完成相应物理空间的读写操作。在mspoc,zynq中pl侧经常会在axi总线线上挂载许多自定义的ip,用此函数可以省去内核驱动得开发直接操作自定义ip的寄存器。当然也可对片内外设用同样的方式操作,下面就使用mmap实现MIO(GPIO)的操作。
2、linux下MIO传统的操作方式
2.1 字符驱动的开发
- 通过ioremap_wc函数将MIO的物理地址映射到内核虚拟地址,然后将使能、驱动方向、控制输出等寄存器的操作封装为open、read、write、close等标准方法,向应用层开放。
2.2 字符驱动的加载
- 可以将字符设备驱动编译进内核,系统启动时自动加载。
- 还可以将字符驱动编译为ko文件,通过Insmod指令加载。
2.3 应用层使用
- 通过驱动层开放的标准方法操作MIO。
3、linux下mmap的操作方式操作MIO
3.1 mmap函数原型
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
- addr
- 指定映射到用户空间的起始地址,通常设为NULL,由内核来分配
- len
- 将文件(这里指的是MIO物理空间的长度)中映射到内存的部分的长度
- prot 可以为以下几种方式的组合
- PROT_EXEC 映射区域可被执行
- PROT_READ 映射区域可被读取
- PROT_WRITE 映射区域可被写入
- PROT_NONE 映射区域不能存取
- flags 两个选项
- MAP_SHARD:写入映射区的数据会复制回文件,且运行其他映射文件的进程共享(个人理解这种模式没有cashe)
- MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制,对此区域的修改不会写会原文件
- fd
- 映射到内存中的文件描述符,有open函数打开文件时返回的值
- offset
- 在此处为MIO物理偏移地址
3.2 mmap函数注意事项
- mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。原因是,内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作,mmap从磁盘到虚拟地址空间的映射也必须是页。
- 内核可以跟踪被内存映射的底层对象(文件)的大小,进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节。也就是说,如果文件的大小一直在扩张,只要在映射区域范围内的数据,进程都可以合法得到,这和映射建立时文件的大小无关。具体情形参见“情形三”。
- 映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小,因为是按页映射。
- 本节内容摘抄自https://blog.csdn.net/weixin_46211877/article/details/111823586
3.3 源码(有需要的拿走)
- 以下源码适用于mpsoc
- gpio_ctrl.h头文件
#ifndef __GPIO_CTRL_H
#define __GPIO_CTRL_H
/***************************** Include Files *********************************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/mman.h>
#include <vector>
#include <iostream>
#include <string.h>
/************************** Constant Definitions *****************************/
#define GPIO_PHY_ADDR 0xFF0A0000
#define set_bit(A, N) ((A) |= (0x01LLU << (N)))
#define clr_bit(A, N) ((A) &= ~(0x01LLU << (N)))
#define get_bit(A, N) (((A) >> (N)) & 0x01LLU)
/**************************** Type Definitions *******************************/
using namespace std;
typedef struct{
u32 dirm;
u32 oen;
u32 int_mask;
u32 int_en;
u32 int_dis_;
u32 int_stat_;
u32 int_tpye_;
u32 int_polarity;
u32 int_any;
u32 rsv[7];
}gpio_cfg;
typedef struct{
u32 data_l;
u32 data_m;
}gpio_mask;
typedef struct {
gpio_mask mask[6];
u32 rsv1[4];
u32 data_out[6];
u32 rsv2[2];
u32 data_in[6] ;
u32 rsv3[99];
gpio_cfg cfg[6];
} gpio_reg;
class gpio_ctrl{
public:
gpio_ctrl();
~gpio_ctrl();
/**********************************************
* @brief setting gpio direction
* @param gpio [0:173]
* @param direction: 1->output,0->input
* @return
*********************************************/
void set_direction(u32 gpio_id, u32 direction);
/**********************************************
* @brief write_pin
* @param gpio [0:173]
* @param value:1->high, 0->low
* @return
*********************************************/
void write_pin(u32 gpio_id,u32 value);
/**********************************************
* @brief write_pin
* @param gpio [0:173]
* @param value:1->high, 0->low
* @return
*********************************************/
u32 get_pin(u32 gpio_id);
private:
int gpio_mem;
gpio_reg *pgpio_reg;
};
#endif
-
GPIO_PHY_ADDR 是MIO的物理偏移地址
-
寄存器详情参见ug1087
-
gpio_ctrl.cpp文件
#include "gpio_ctrl.h"
gpio_ctrl::gpio_ctrl()
{
gpio_mem = open("/dev/mem", O_RDWR | O_SYNC);
pgpio_reg = (gpio_reg*)mmap(NULL,
65535,
PROT_READ | PROT_WRITE,
MAP_SHARED,
gpio_mem, GPIO_PHY_ADDR);
}
gpio_ctrl::~gpio_ctrl()
{
munmap(pgpio_reg,65536);
close(gpio_mem);
}
void gpio_ctrl::set_direction(u32 gpio_id, u32 direction)
{
u32 bank_id = gpio_id / 26;
u32 pin_id = gpio_id % 26;
if(direction == 1){
set_bit(pgpio_reg->cfg[bank_id].dirm,pin_id);
set_bit(pgpio_reg->cfg[bank_id].oen,pin_id);
}
else{
clr_bit(pgpio_reg->cfg[bank_id].dirm,pin_id);
clr_bit(pgpio_reg->cfg[bank_id].oen,pin_id);
}
}
void gpio_ctrl::write_pin(u32 gpio_id, u32 value)
{
u32 bank_id = gpio_id / 26;
u32 pin_id = gpio_id % 26;
if(value == 1){
set_bit(pgpio_reg->data_out[bank_id],pin_id);
}
else{
clr_bit(pgpio_reg->data_out[bank_id],pin_id);
}
}
u32 gpio_ctrl::get_pin(u32 gpio_id)
{
u32 value = 0;
u32 bank_id = gpio_id / 26;
u32 pin_id = gpio_id % 26;
value = get_bit(pgpio_reg->data_in[bank_id],pin_id);
return value;
}