dev mem 原理 linux,【Linux开发】使用/dev/mem内存映射的方式操作GPIO口

使用的是全志H3的芯片,运行Debian Desktop系统的ARM版本Armbian,要控制外部几个IO口,可以使用很多种方法,如果对GPIO的操作速度有要求就需要使用直接操作内存寄存器的方式来控制GPIO口。AllWinner的官方数据手册文档上介绍了GPIO的寄存器内容:

GPIO寄存器映射表

GPIO配置寄存器

GPIO的寄存器在内存的基地址是0x01C20800,所以要将0x01C20800之后的内容映射到进程的虚拟内存之中,使用的mmap函数,这个函数的使用有不少限制,比如最后一个参数offset的意思是要被映射的物理内存地址偏移量,比如这里就是0x01C20800,但是要求这个offset必须是页大小的整倍数,所以这里0x01C20800并不能直接作为offset值,需要向前截取到为页大小整倍数的地址,然后在映射后的虚拟地址上加上多余的偏移量,具体程序执行如下面的GPIO_Init函数中的操作方法。

下面是gpio.c文件的内容:

#include "gpio.h"

PIO_Map *PIO = NULL;

unsigned int *gpio_map;

void GPIO_Init(void)

{

unsigned int fd;

unsigned int addr_start, addr_offset;

unsigned int PageSize, PageMask;

if((fd = open("/dev/mem",O_RDWR)) == -1)

{

printf("open error\r\n");

return;

}

PageSize = sysconf(_SC_PAGESIZE);//页大小

PageMask = (~(PageSize-1));//页掩码

printf("PageSize:%d,PageMask:%.8X\r\n",PageSize,PageMask);

addr_start = PIO_BASE_ADDRESS & PageMask;

addr_offset = PIO_BASE_ADDRESS & ~PageMask;

printf("addr_start:%.8X,addr_offset:%.8X\r\n",addr_start,addr_offset);

if((gpio_map = mmap(NULL,PageSize*2,PROT_READ|PROT_WRITE, MAP_SHARED,fd,addr_start)) == NULL)

{

printf("mmap error\r\n");

close(fd);

return;

}

printf("gpio_map:%.8X\r\n",gpio_map);

PIO = (PIO_Map *)((unsigned int)gpio_map + addr_offset);

printf("PIO:%.8X\r\n",PIO);

close(fd);

}

void GPIO_ConfigPin(PORT port,unsigned int pin,PIN_MODE mode)

{

if (gpio_map == NULL)

return;

PIO->Pn[port].CFG[pin / 8] &= ~((unsigned int)0x07 << pin % 8 * 4);

PIO->Pn[port].CFG[pin / 8] |= ((unsigned int)mode << pin % 8 * 4);

}

void GPIO_SetPin(PORT port,unsigned int pin,unsigned int level)

{

if (gpio_map == NULL)

return;

if(level)

PIO->Pn[port].DAT |= (1 << pin);

else

PIO->Pn[port].DAT &= ~(1 << pin);

}

下面是gpio.h文件:

#ifndef __GPIO_H__

#define __GPIO_H__

#include#include#include#include#include#include#define PIO_BASE_ADDRESS0x01C20800

typedef struct

{

unsigned int CFG[4];

unsigned int DAT ;

unsigned int DRV0;

unsigned int DRV1;

unsigned int PUL0;

unsigned int PUL1;

}PIO_Struct;

typedef struct

{

PIO_Struct Pn[7];

}PIO_Map;

typedef enum

{

PA = 0,

PB = 1,

PC = 2,

PD = 3,

PE = 4,

PF = 5,

PG = 6,

}PORT;

typedef enum

{

IN= 0x00,

OUT= 0x01,

AUX= 0x02,

INT= 0x06,

DISABLE= 0x07,

}PIN_MODE;

extern PIO_Map *PIO;

void GPIO_Init(void);

void GPIO_ConfigPin(PORT port,unsigned int pin,PIN_MODE mode);

void GPIO_SetPin(PORT port,unsigned int pin,unsigned int level);

unsigned int GPIO_GetPin(PORT port,unsigned int pin);

void GPIO_Free(void);

#endif

主文件main.c如下:

#include#include#include#include#include#include#include "gpio.h"

int main()

{

GPIO_Init();

int a = 0;

GPIO_ConfigPin(PA,15,OUT);

while(1)

{

GPIO_SetPin(PA,15,a = ~a);

usleep(100000);

}

GPIO_Free();

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,可以使用mmap函数将物理地址映射到用户空间,然后使用指针操作物理地址来访问寄存器。以下是一个例子: ``` #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #define PERIPHERAL_BASE 0x3F000000 // 树莓派3B+上的Peripheral Base Address #define GPIO_BASE (PERIPHERAL_BASE + 0x200000) // GPIO寄存器的基地址 #define GPIO_GPFSEL0 0x0000 // GPIO Function Select 0 #define GPIO_GPSET0 0x001C // GPIO Pin Output Set 0 #define GPIO_GPCLR0 0x0028 // GPIO Pin Output Clear 0 #define GPIO_PIN 17 // 使用GPIO17作为例子 int main() { // 打开/dev/mem文件 int fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { perror("open"); exit(EXIT_FAILURE); } // 映射GPIO寄存器到用户空间 volatile unsigned int *gpio = (volatile unsigned int *)mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE); if (gpio == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } // 设置GPIO17为输出 unsigned int reg = gpio[GPIO_GPFSEL0/4]; reg &= ~(7 << ((GPIO_PIN % 10) * 3)); reg |= 1 << ((GPIO_PIN % 10) * 3); gpio[GPIO_GPFSEL0/4] = reg; // 设置GPIO17为高电平 gpio[GPIO_GPSET0/4] = 1 << GPIO_PIN; // 等待1秒钟 sleep(1); // 设置GPIO17为低电平 gpio[GPIO_GPCLR0/4] = 1 << GPIO_PIN; // 取消映射 munmap((void *)gpio, 4096); // 关闭/dev/mem文件 close(fd); return 0; } ``` 该程序使用mmap函数将GPIO寄存器映射到用户空间,并使用指针访问寄存器。在这个例子中,我们将GPIO17设置为输出,并将其设置为高电平,等待1秒钟后将其设置为低电平。请注意,这需要在root权限下运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值