嵌入式Linux 使用MMAP映射GPIO
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
#define GPIO_LED_BASE_ADDR 0x01c20800
#define PD_CFG_REG_1_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0070)
#define PD_CFG_REG_2_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0074)
#define PD_DATA_REG_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x007C)
#define PD_DRV0_REG_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0080)
#define PD_DRV1_REG_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0084)
#define PD_PULL0_REG_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0088)
#define PD_PULL1_REG_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x008C)
#define GPIO_PD_09 9
#define GPIO_PD_18 18
#define GPIO_PD_19 19
int main(int argc, char * argv[])
{
void * gpio;
unsigned int reg;
unsigned int * pd_cfg1_reg;
unsigned int * pd_cfg2_reg;
unsigned int * pd_data_reg;
unsigned int * pd_drv0_reg;
unsigned int * pd_drv1_reg;
unsigned int * pd_pull0_reg;
unsigned int * pd_pull1_reg;
int mem_fd = -1;
long sz = sysconf(_SC_PAGESIZE);
/*sz=4096,所以一页的大小为4096, MAP_SIZE必须为4096的整数倍, 否则mmap会返回22提示参数错误*/
printf("#######_SC_PAGESIZE = %ld\n",sz);
mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (mem_fd < 0) {
printf("Unable to open /dev/mem\n");
return -1;
}
gpio = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPIO_LED_BASE_ADDR & ~MAP_MASK);
if (gpio == MAP_FAILED) {
printf("Mmap failed errno = %d\n", errno);
close(mem_fd);
return -1;
}
pd_cfg1_reg = gpio + PD_CFG_REG_1_OFFSET;
pd_cfg2_reg = gpio + PD_CFG_REG_2_OFFSET;
pd_data_reg = gpio + PD_DATA_REG_OFFSET;
pd_drv0_reg = gpio + PD_DRV0_REG_OFFSET;
pd_drv1_reg = gpio + PD_DRV1_REG_OFFSET;
pd_pull0_reg = gpio + PD_PULL0_REG_OFFSET;
pd_pull1_reg = gpio + PD_PULL1_REG_OFFSET;
printf("CFG REG1:\n");
printf("##################### %08x ####################\n", *pd_cfg1_reg);
//PD9 set output mode
*pd_cfg1_reg &= 0x00;
*pd_cfg1_reg |= 0x10;
printf("##################### %08x ####################\n", *pd_cfg1_reg);
printf("CFG REG2:\n");
printf("##################### %08x ####################\n", *pd_cfg2_reg);
//PD18 and PD19 set output mode
*pd_cfg2_reg &= 0x00;
*pd_cfg2_reg |= 0x1100;
printf("##################### %08x ####################\n", *pd_cfg2_reg);
printf("DATA REG:\n");
printf("##################### %08x ####################\n", *pd_data_reg);
//set output high level
*pd_data_reg &= 0x00;
*pd_data_reg |= (0x01 << GPIO_PD_09);
*pd_data_reg |= (0x3 << GPIO_PD_18);
printf("##################### %08x ####################\n", *pd_data_reg);
printf("DRV0 REG:\n");
printf("##################### %08x ####################\n", *pd_drv0_reg);
*pd_drv0_reg &= 0x00;
*pd_drv0_reg |= 0x10000;
printf("##################### %08x ####################\n", *pd_drv0_reg);
printf("DRV1 REG:\n");
printf("##################### %08x ####################\n", *pd_drv1_reg);
*pd_drv1_reg &= 0x00;
*pd_drv1_reg |= 0x50;
printf("##################### %08x ####################\n", *pd_drv1_reg);
printf("PULL0 REG:\n");
printf("##################### %08x ####################\n", *pd_pull0_reg);
*pd_pull0_reg &= 0x00;
*pd_pull0_reg |= 0x10000;
printf("##################### %08x ####################\n", *pd_pull0_reg);
printf("PULL1 REG:\n");
printf("##################### %08x ####################\n", *pd_pull1_reg);
*pd_pull1_reg &= 0x00;
*pd_pull1_reg |= 0x50;
printf("##################### %08x ####################\n", *pd_pull1_reg);
close(mem_fd);
munmap(gpio, MAP_SIZE);
return 0;
}
注意的问题:
- mmap 映射SIZE必须为页大小的整数倍,这里取值4096刚好等于页大小。
- mmap返回值为void *类型,通过类似以下代码
pd_cfg1_reg = gpio + PD_CFG_REG_1_OFFSET;
进行了类型转换为unsigned int *类型。 - mmap函数的最后一个参数是
GPIO_LED_BASE_ADDR & ~MAP_MASK
所以寄存器地址得这样算
#define PD_CFG_REG_1_OFFSET ((GPIO_LED_BASE_ADDR & MAP_MASK) + 0x0070)