Linux:使用/dev/mem内存映射的方式操作GPIO口

在全志H3芯片的Debian Armbian系统上,为实现GPIO高速控制,文章详细阐述了如何利用mmap函数将GPIO寄存器(基址0x01C20800)映射到内存,解决offset必须为页大小整倍数的问题,并展示了gpio.c、gpio.h和main.c的代码片段。
摘要由CSDN通过智能技术生成

使用的是全志H3的芯片,运行Debian Desktop系统的ARM版本Armbian,要控制外部几个IO口,可以使用很多种方法,如果对GPIO的操作速度有要求就需要使用直接操作内存寄存器的方式来控制GPIO口。AllWinner的官方数据手册文档上介绍了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<stdio.h>  
#include<unistd.h>  
#include<sys/mman.h>  
#include<sys/types.h>  
#include<sys/stat.h>  
#include<fcntl.h> 

#define PIO_BASE_ADDRESS	0x01C20800

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<stdio.h>  
#include<unistd.h>  
#include<sys/mman.h>  
#include<sys/types.h>  
#include<sys/stat.h>  
#include<fcntl.h> 
#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();
}

版权声明

原文链接:https://blog.csdn.net/tq384998430/article/details/53161192

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在树莓派上,可以使用映射GPIO地址的方式来控制GPIO引脚的电平状态。具体步骤如下: 1. 打开 /dev/mem 文件,获取系统内存的映射地址。代码如下: ``` int fd = open("/dev/mem", O_RDWR|O_SYNC); ``` 2. 获取GPIO寄存器的映射地址。GPIO控制寄存器的物理地址为0x20200000,使用 mmap 函数将其映射到用户空间。代码如下: ``` #define GPIO_BASE 0x20200000 #define GPIO_SIZE 4096 void *gpio_map; gpio_map = mmap(NULL, GPIO_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE); ``` 3. 设置GPIO引脚的输入输出状态。GPIO控制寄存器中每个寄存器都对应一个GPIO引脚,通过设置相应的位可以将引脚设置为输入或输出模式。例如,将第7个GPIO引脚设置为输出模式,代码如下: ``` #define GPFSEL0 0 #define GPFSEL1 1 #define GPFSEL2 2 #define GPFSEL3 3 #define GPFSEL4 4 #define GPFSEL5 5 #define GPIO_OUTPUT 1 #define GPIO_INPUT 0 volatile unsigned *gpio; gpio = (unsigned*)gpio_map; //将第7个GPIO引脚设置为输出模式 gpio[GPFSEL0] |= (GPIO_OUTPUT << ((7 % 10) * 3)); ``` 4. 控制GPIO引脚电平。通过设置GPIO控制寄存器中相应的位可以将GPIO引脚的电平设置为高或低。例如,将第7个GPIO引脚设置为高电平,代码如下: ``` #define GPSET0 7 #define GPCLR0 10 //将第7个GPIO引脚设置为高电平 gpio[GPSET0] |= (1 << 7); ``` 5. 关闭内存映射使用 munmap 函数关闭内存映射。代码如下: ``` munmap(gpio_map, GPIO_SIZE); ``` 需要注意的是,使用映射GPIO地址的方式需要具有 root 权限,因为需要访问内核态的内存地址。同时,使用方式需要非常小心,因为对GPIO控制寄存器的任何误操作都可能导致系统崩溃或引脚损坏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值