如何将 NanoPi 的 GPIO 设置为硬件级别的输出?
How can I set a NanoPi's GPIO as output on hardware level?
我正在尝试将 NanoPi Neo 上的 Pin G11 设置为 C++ 输出,方法是使用 mmap
并设置一位。
根据 Allwinner H3 Datasheet 指定了以下硬件地址:
PIO Base Address: 0x01C20800 (p. 316)
PG Configure Register 1 offset: 0xDC (p. 338)
PG11 bits: 14:12
类似于Raspberry Pi的功能寄存器,Pin至少知道两种状态:
000: input
001: output
因此,在我的脚本中,我尝试设置 PG 配置寄存器 1 的第 12 位。
这是我的 C++ 代码:
struct peripheral {
unsigned long addr_hardware;
int map_size;
int mem_fd;
void *mem_map;
volatile unsigned long *addr_virtual;
int map() {
if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) return -1;
// map addr_hardware=0x01C20000 into /dev/mem
if ((mem_map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, addr_hardware)) == MAP_FAILED) return -1;
// store virtual address with offset of 0x800, which was rounded down before
addr_virtual = (volatile unsigned long *) mem_map + 0x800;
return 0;
};
void unmap() {
munmap(mem_map, map_size);
close(mem_fd);
};
};
int main() {
// initialize peripheral at rounded down PIO base address
peripheral gpio {0x01C20000, 4096 * 10};
// map PIOs into virtual memory
if (gpio.map() == -1) return -1;
// output current value of PG Configure Register 1
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
// set 12th bit
*(gpio.addr_virtual + 0xDC) |= (1 << 12);
// output value of register after setting the 12th bit
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
gpio.unmap();
return 0;
}
不幸的是,我的代码不起作用。当我再次 运行 脚本时,PIO 配置寄存器的内容已更改为之前的状态。当我将 Pin G11 配置为输出(通过 WiringNP)时,显示值不会改变。我正在使用 LED 检查我的代码的正确功能(当状态从 IN 更改为 OUT 时它应该打开,因为它设置为 HIGH)。
当我更改 map_size
时,我得到以下结果(添加空格以提高可读性):
> gpio {0x01C20000, 4096 * 2}
< 00000000 00000000 00000000 00000000
< 00000000 00000000 00010000 00000000
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00000000 00110011
我期待以下结果:
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00010000 00110011
如果在设备树中定义了pio,内核将控制并覆盖相关的寄存器。例如,如果它想通过这个寄存器驱动闪烁的 LED 或其他 IO。
为了解决我的问题,我不得不按以下方式更改用于访问 PIO 硬件的虚拟地址:
addr_virtual = (volatile unsigned long *) mem_map + (0x800 >> 2);
等于缩短的语句:
addr_virtual = (volatile unsigned long *) mem_map + 0x200;
但更具可读性,因为 PIO 硬件地址是 0x01C20000 + 0x800
。我还使用了 4096 * 10
字节的地图大小。
我正在为 I/O-operations 使用这些宏:
#define GPIO_IN(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) &= ~(7 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4))
#define GPIO_OUT(g) GPIO_IN(g); *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) |= 1 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4)
#define GPIO_SET(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) |= 1 << ((g) - (((g) / 32) * 32))
#define GPIO_CLR(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) &= ~(1 << ((g) - (((g) / 32) * 32)))
我正在尝试将 NanoPi Neo 上的 Pin G11 设置为 C++ 输出,方法是使用 mmap
并设置一位。
根据 Allwinner H3 Datasheet 指定了以下硬件地址:
PIO Base Address: 0x01C20800 (p. 316)
PG Configure Register 1 offset: 0xDC (p. 338)
PG11 bits: 14:12
类似于Raspberry Pi的功能寄存器,Pin至少知道两种状态:
000: input
001: output
因此,在我的脚本中,我尝试设置 PG 配置寄存器 1 的第 12 位。 这是我的 C++ 代码:
struct peripheral {
unsigned long addr_hardware;
int map_size;
int mem_fd;
void *mem_map;
volatile unsigned long *addr_virtual;
int map() {
if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) return -1;
// map addr_hardware=0x01C20000 into /dev/mem
if ((mem_map = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, addr_hardware)) == MAP_FAILED) return -1;
// store virtual address with offset of 0x800, which was rounded down before
addr_virtual = (volatile unsigned long *) mem_map + 0x800;
return 0;
};
void unmap() {
munmap(mem_map, map_size);
close(mem_fd);
};
};
int main() {
// initialize peripheral at rounded down PIO base address
peripheral gpio {0x01C20000, 4096 * 10};
// map PIOs into virtual memory
if (gpio.map() == -1) return -1;
// output current value of PG Configure Register 1
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
// set 12th bit
*(gpio.addr_virtual + 0xDC) |= (1 << 12);
// output value of register after setting the 12th bit
cout << bitset<32>(*(gpio.addr_virtual + 0xDC)) << endl;
gpio.unmap();
return 0;
}
不幸的是,我的代码不起作用。当我再次 运行 脚本时,PIO 配置寄存器的内容已更改为之前的状态。当我将 Pin G11 配置为输出(通过 WiringNP)时,显示值不会改变。我正在使用 LED 检查我的代码的正确功能(当状态从 IN 更改为 OUT 时它应该打开,因为它设置为 HIGH)。
当我更改 map_size
时,我得到以下结果(添加空格以提高可读性):
> gpio {0x01C20000, 4096 * 2}
< 00000000 00000000 00000000 00000000
< 00000000 00000000 00010000 00000000
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00000000 00110011
我期待以下结果:
> gpio {0x01C20000, 4096 * 10}
< 00000000 00000000 00000000 00110011
< 00000000 00000000 00010000 00110011
如果在设备树中定义了pio,内核将控制并覆盖相关的寄存器。例如,如果它想通过这个寄存器驱动闪烁的 LED 或其他 IO。
为了解决我的问题,我不得不按以下方式更改用于访问 PIO 硬件的虚拟地址:
addr_virtual = (volatile unsigned long *) mem_map + (0x800 >> 2);
等于缩短的语句:
addr_virtual = (volatile unsigned long *) mem_map + 0x200;
但更具可读性,因为 PIO 硬件地址是 0x01C20000 + 0x800
。我还使用了 4096 * 10
字节的地图大小。
我正在为 I/O-operations 使用这些宏:
#define GPIO_IN(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) &= ~(7 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4))
#define GPIO_OUT(g) GPIO_IN(g); *(gpio.addr_virtual + (((((g) / 32) * 36) + ((((g) - ((g) & ~31)) / 8) * 4)) >> 2)) |= 1 << ((((g) - (((g) / 32) * 32)) - ((((g) - (((g) / 32) * 32)) / 8) * 8)) * 4)
#define GPIO_SET(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) |= 1 << ((g) - (((g) / 32) * 32))
#define GPIO_CLR(g) *(gpio.addr_virtual + (((((g) / 32) * 36) + 0x10) >> 2)) &= ~(1 << ((g) - (((g) / 32) * 32)))