摘要
为了避免编写Linux设备驱动操作底层硬件设备的GPIO接口,本文提供了采用/dev/mem设备和mmap函数将GPIO物理地址映射到用户层进行操作的方式,通过在S3C2416和Linux 2.6.39.3系统上测试和工程实践应用表明该方案提供了一种简单可靠的用户层操作物理硬件设备的方式,具有较好的工程应用价值。
背景
最近在S3C2416的嵌入式Linux设备上开发一个智慧泊车相关的设备,其中需要操作底层硬件设备的一个GPIO接口,由于只需要操作一个GPIO接口,因此选择了采用/dev/mem设备和mmap函数在用户层操作GPIO接口的方法,在实现过程中因为没有很好的理解mmap的offset参数的页对齐机制,浪费了大概半天调试时间,因此留下记录。本文编写了在用户层操作GPIO的接口函数,为用户提供了采用访问物理内存的方法访问底层设备硬件的方法。
技术方案
mmap函数声明如下:
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
addr:指定文件或其他对象被映射到进程空间中的地址,一般设置为NULL,将选择地址的任务留给内核完成;
len:指定映射的进程控件的长度,一般设置为系统的页大小,原因是offset:只能是页地址对齐的地址,为了访问整个页地址内的偏移,因此建议设置为系统页大小,后文提供的接口文件中使用4096的页大小;
prot:指定共享内存的访问权限,可以为PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NONE(不可访问);
flags:指定对象的共享方式,可选的值为MAP_SHARED, MAP_PRIVATE, MAP_FIXED;
fd:映射到内存空间的文件描述符,对于设备文件一般为打开/dev/mem文件的返回的文件描述符;
offset:按页面大小对齐的页地址;
项目中使用的时S3C2416和Linux 2.6.39.3系统,系统的页面大小为4096字节,因此提供的GPIO操作函数接口代码如下。
gpio_ctrl.h文件
#ifndef __GPIO_CTRL_H__
#define __GPIO_CTRL_H__
#include
#include
#include
#include
#include
#define REG(addr) (*(volatile unsigned int *)(addr))
typedef enum _gpio_mode_t
{
GPIO_INPUT = 0x00,
GPIO_OUTPUT = 0x01,
GPIO_EINT = 0x02,
GPIO_RESERVED = 0x03,
}gpio_mode_t;
typedef enum _gpio_state_t
{
GPIO_STATE_LOW = 0x00,
GPIO_STATE_HIGH = 0x01,
}gpio_state_t;
typedef enum _gpio_pull_t
{
GPIO_PULL_DISABLE = 0x00,
GPIO_PULL_DOWN = 0x01,
GPIO_PULL_UP = 0x02,
GPIO_PULL_NOT_AVAILABLE = 0x03,
}gpio_pull_t;
typedef enum _gpio_pin_t
{
GPIO_PIN_0 = 0,
GPIO_PIN_1 = 1,
GPIO_PIN_2 = 2,
GPIO_PIN_3 = 3,
GPIO_PIN_4 = 4,
GPIO_PIN_5 = 5,
GPIO_PIN_6 = 6,
GPIO_PIN_7 = 7,
}gpio_pin_t;
void gpio_clock_init();
void gpio_set_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset, gpio_mode_t tMode);
gpio_mode_t gpio_get_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset);
void gpio_set_pin_state(void * pRegAddr, gpio_pin_t nPinOffset, gpio_state_t tState);
gpio_state_t gpio_get_pin_state(void * pRegAddr, gpio_pin_t nPinOffset);
void gpio_set_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset, gpio_pull_t tPull);
gpio_pull_t gpio_get_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset);
#endif
gpio_ctrl.c文件
#include "gpio_ctrl.h"
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define PAGE_OFFSET_MASK (PAGE_SIZE - 1)
#define MMAP_LENGTH PAGE_SIZE
void gpio_clock_init()
{
}
void gpio_set_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset, gpio_mode_t tMode)
{
int nFd = -1;
void * pGpioConAddr = NULL;
unsigned int nGpioConVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioConAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioConAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioConAddr == NULL || pGpioConAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioConVal = REG(pGpioConAddr);
nGpioConVal &= ~(3 << nShiftBits);
nGpioConVal |= ((unsigned int)tMode) << nShiftBits;
REG((unsigned int)pGpioConAddr) = nGpioConVal;
nGpioConVal = REG(pGpioConAddr);
failed:
if(pGpioConAddr)
{
msync(pGpioConAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioConAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
}
gpio_mode_t gpio_get_pin_mode(void * pRegAddr, gpio_pin_t nPinOffset)
{
int nFd = -1;
void * pGpioConAddr = NULL;
unsigned int nGpioConVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioConAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioConAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioConAddr == NULL || pGpioConAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioConVal = REG(pGpioConAddr);
nGpioConVal &= (3 << nShiftBits);
nGpioConVal >>= nShiftBits;
failed:
if(pGpioConAddr)
{
munmap(pGpioConAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
return nGpioConVal;
}
void gpio_set_pin_state(void * pRegAddr, gpio_pin_t nPinOffset, gpio_state_t tState)
{
int nFd = -1;
void * pGpioStateAddr = NULL;
unsigned int nGpioStateVal = 0;
unsigned int nShiftBits = (unsigned int)nPinOffset;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioStateAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioStateAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioStateAddr == NULL || pGpioStateAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioStateVal = REG(pGpioStateAddr);
nGpioStateVal &= ~(1 << nShiftBits);
nGpioStateVal |= ((unsigned int)tState) << nShiftBits;
REG((unsigned int)pGpioStateAddr) = nGpioStateVal;
failed:
if(pGpioStateAddr)
{
msync(pGpioStateAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioStateAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
}
gpio_state_t gpio_get_pin_state(void * pRegAddr, gpio_pin_t nPinOffset)
{
int nFd = -1;
void * pGpioStateAddr = NULL;
unsigned int nGpioStateVal = 0;
unsigned int nShiftBits = (unsigned int)nPinOffset;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioStateAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioStateAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioStateAddr == NULL || pGpioStateAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioStateVal = REG(pGpioStateAddr);
nGpioStateVal &= (1 << nShiftBits);
nGpioStateVal >>= nShiftBits;
failed:
if(pGpioStateAddr)
{
munmap(pGpioStateAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
return nGpioStateVal;
}
void gpio_set_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset, gpio_pull_t tPull)
{
int nFd = -1;
void * pGpioPullAddr = NULL;
unsigned int nGpioPullVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioPullAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr) & PAGE_MASK);
pGpioPullAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioPullAddr == NULL || pGpioPullAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioPullVal = REG(pGpioPullAddr);
nGpioPullVal &= ~(3 << nShiftBits);
nGpioPullVal |= ((unsigned int)tPull) << nShiftBits;
REG((unsigned int)pGpioPullAddr) = nGpioPullVal;
failed:
if(pGpioPullAddr)
{
msync(pGpioPullAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioPullAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
}
gpio_pull_t gpio_get_pin_pull(void * pRegAddr, gpio_pin_t nPinOffset)
{
int nFd = -1;
void * pGpioPullAddr = NULL;
unsigned int nGpioPullVal = 0;
unsigned int nShiftBits = ((unsigned int)nPinOffset) * 2;
nFd = open("/dev/mem", O_RDWR | O_SYNC);
if(nFd < 0)
{
perror("open /dev/mem device failed.
");
goto failed;
}
pGpioPullAddr = mmap(NULL, MMAP_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, nFd, ((unsigned int)pRegAddr)&PAGE_MASK);
pGpioPullAddr += ((unsigned int)pRegAddr & PAGE_OFFSET_MASK);
if(pGpioPullAddr == NULL || pGpioPullAddr == MAP_FAILED)
{
perror("gpio mmap failed.
");
goto failed;
}
nGpioPullVal = REG(pGpioPullAddr);
nGpioPullVal &= (3 << nShiftBits);
nGpioPullVal >>= nShiftBits;
failed:
if(pGpioPullAddr)
{
msync(pGpioPullAddr, MMAP_LENGTH, MS_SYNC);
munmap(pGpioPullAddr, MMAP_LENGTH);
}
if(nFd > 0)
{
close(nFd);
}
return nGpioPullVal;
}
工程中使用GPG4对上文中提供的GPIO接口函数进行了测试,测试代码
main.c文件
#include
#include "gpio_ctrl.h"
int main(int argc, char * * argv, char * * env)
{
gpio_state_t nGPIOState;
gpio_mode_t nGPIOMode;
gpio_pull_t nGPIOPull;
unsigned char ch;
gpio_set_pin_mode((void *)(GPGCON_ADDR), GPIO_PIN_4, GPIO_INPUT);
nGPIOMode = gpio_get_pin_mode((void *)(GPGCON_ADDR), GPIO_PIN_4);
printf("%s,L%d, in %s, current gpio mode is:nGPIOMode=%d.
", __FILE__, __LINE__, __func__, nGPIOMode);
gpio_set_pin_pull((void *)(GPGUDP_ADDR), GPIO_PIN_4, GPIO_PULL_UP);
nGPIOPull = gpio_get_pin_pull((void *)(GPGUDP_ADDR), GPIO_PIN_4);
printf("%s,L%d, in %s, current gpio pull is:nGPIOPull=%d.
", __FILE__, __LINE__, __func__, nGPIOPull);
while(1)
{
nGPIOState = gpio_get_pin_state((void *)(GPGDAT_ADDR), GPIO_PIN_4);
printf("%s,L%d,in %s, current GPG_4 pin state=%d.
", __FILE__, __LINE__, __func__, (int)(nGPIOState));
usleep(1000000);
}
return 0;
}
工程的
Makefile文件如下:
#!/bin/sh
CROSS=arm-linux-
CC=$(CROSS)gcc
FLAGS=-lpthread -wall
objects = main.o gpio_ctrl.o
gpio_test:$(objects)
$(CC) -o gpio_test $(objects)
rm -f gpio_test *.o
本文提供的pudn工程源码链接如下:
http://www.pudn.com/downloads711/sourcecode/embedded/detail2854138.html
结语
本文提供了采用/dev/mem和mmap方式在用户态操作物理层资源和对象的方式,与采用编写驱动方式操作GPIO相比简化了物理层资源访问流程,提高了工程研发效率,本文为嵌入式系统开发人员在应用层访问物理层资源对象提供了较好的参考。