目标
NVIDIA提供了python版本的GPIO库,但没有提供C版,并且python版本是通过文件句柄实现的,性能不行,需要C版本内存映射来操作GPIO。
寄存器地址映射规则
根据官方文档 Tegra_X1_TRM_DP07225001_v1.3p.pdf 第9章表32,A~Z、AA、BB、CC、DD、EE、FF共有32个port,每4个port为一个controller,每个port控制8个GPIO针(pin)。寄存器地址范围000~FFF共4096个字节,每4个port的地址是交错的,如下表中A、B、C、D所示。
步骤一:打开GPIO口
代码来自jetsonGPIO,gpio参数传入GPIO的设备地址,例如BCM=20/BOARD=38对应的设备地址是77,详见下表。
// Export the given gpio to userspace;
// Return: Success = 0 ; otherwise open file error
int gpioExport(unsigned int gpio)
{
int fileDescriptor, length;
char commandBuffer[MAX_BUF];
fileDescriptor = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
if (fileDescriptor < 0)
{
char errorBuffer[128];
snprintf(errorBuffer, sizeof(errorBuffer), "gpioExport unable to open gpio%d", gpio);
perror(errorBuffer);
return fileDescriptor;
}
length = snprintf(commandBuffer, sizeof(commandBuffer), "%d", gpio);
if (write(fileDescriptor, commandBuffer, length) != length)
{
perror("gpioExport");
return fileDescriptor;
}
close(fileDescriptor);
return 0;
}
步骤二:设置GPIO口的读/写
代码来自jetsonGPIO,gpio参数传入GPIO的设备地址,详见下表。
// Set the direction of the GPIO pin
// Return: Success = 0 ; otherwise open file error
int gpioSetDirection(unsigned int gpio, unsigned int out_flag)
{
int fileDescriptor;
char commandBuffer[MAX_BUF];
snprintf(commandBuffer, sizeof(commandBuffer), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
fileDescriptor = open(commandBuffer, O_WRONLY);
if (fileDescriptor < 0)
{
char errorBuffer[128];
snprintf(errorBuffer, sizeof(errorBuffer), "gpioSetDirection unable to open gpio%d", gpio);
perror(errorBuffer);
return fileDescriptor;
}
if (out_flag)
{
if (write(fileDescriptor, "out", 4) != 4)
{
perror("gpioSetDirection");
return fileDescriptor;
}
}
else
{
if (write(fileDescriptor, "in", 3) != 3)
{
perror("gpioSetDirection");
return fileDescriptor;
}
}
close(fileDescriptor);
return 0;
}
步骤三:建立mmap
代码来自jetson-nano-gpio-example。其中,GPIO_ADDRESS 见下表中的内存映射地址。
// read physical memory (needs root)
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
fprintf(stderr, "usage : $ sudo %s (with root privilege)\n", argv[0]);
exit(1);
}
// map a particular physical address into our address space
int pagesize = getpagesize();
int pagemask = pagesize-1;
// This page will actually contain all the GPIO controllers, because they are co-located
void *base = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (GPIO_ADDRESS & ~pagemask));
if (base == NULL) {
perror("mmap()");
exit(1);
}
内存映射表
该表是我根据jetson的地址规则计算的内存映射地址和设备地址。
BCM | JETSON | GPIO | 内存映射地址 | 设备地址 |
4 | 7 | GPIO3_PBB.00 | 0x6000d60C | 216 |
26 | 37 | GPIO3_PB.04 | 0x6000d004 | 12 |
25 | 22 | GPIO3_PB.05 | 0x6000d004 | 13 |
27 | 13 | GPIO3_PB.06 | 0x6000d004 | 14 |
24 | 18 | GPIO3_PB.07 | 0x6000d004 | 15 |
23 | 16 | GPIO3_PDD.00 | 0x6000d704 | 232 |
13 | 33 | GPIO3_PE.06 | 0x6000d100 | 38 |
5 | 29 | GPIO3_PS.05 | 0x6000d408 | 149 |
22 | 15 | GPIO3_PY.02 | 0x6000d600 | 194 |
12 | 32 | GPIO3_PV.00 | 0x6000d504 | 168 |
6 | 31 | GPIO3_PZ.00 | 0x6000d604 | 200 |
20 | 38 | GPIO3_PJ.05 | 0x6000d204 | 77 |
21 | 40 | GPIO3_PJ.06 | 0x6000d204 | 78 |
19 | 35 | GPIO3_PJ.04 | 0x6000d204 | 76 |
18 | 12 | GPIO3_PJ.07 | 0x6000d204 | 79 |
1 | 28 | GPIO3_PJ.01 | 0x6000d204 | 73 |
0 | 27 | GPIO3_PJ.00 | 0x6000d204 | 72 |
3 | 5 | GPIO3_PJ.02 | 0x6000d204 | 74 |
2 | 3 | GPIO3_PJ.03 | 0x6000d204 | 75 |
14 | 8 | GPIO3_PG.00 | 0x6000d108 | 48 |
15 | 10 | GPIO3_PG.01 | 0x6000d108 | 49 |
17 | 11 | GPIO3_PG.02 | 0x6000d108 | 50 |
16 | 36 | GPIO3_PG.03 | 0x6000d108 | 51 |
10 | 19 | GPIO3_PC.00 | 0x6000d008 | 16 |
9 | 21 | GPIO3_PC.01 | 0x6000d008 | 17 |
11 | 23 | GPIO3_PC.02 | 0x6000d008 | 18 |
8 | 24 | GPIO3_PC.03 | 0x6000d008 | 19 |
7 | 26 | GPIO3_PC.04 | 0x6000d008 | 20 |