参考一篇我之前写的文章:https://blog.csdn.net/tq384998430/article/details/53161192,这篇文章是通过操作/dev/mem内存映射的方式操作GPIO的寄存器以达到控制GPIO的功能,这篇文章没有问题,但是官方其实有一个更好的操作GPIO的库叫做WiringPi,参考官方的介绍:http://www.orangepi.org/Docs/WiringPi.html,引用文章的介绍:
Wiring Pi is a GPIO library written by Drogon. It is originally for the Raspberry Pi, but Orangepi has modified and adapted it to make it work on the Orange Pi mini/Pi, we call it WiringOP. More example details can be found at http://wiringpi.com/. All these examples can be used directly on the Orange Pi mini/Pi.
WiringPi其实源自树莓派,”迅龙“修改了其内容使得WiringPi可以用在Orange Pi上,我们可以参考官方的步骤下载源码并编译安装,然后就可以愉快地使用WiringPi库来操作GPIO、IIC、SPI、PWM等外设。
实验一下看看,查看所有引脚信息:
设置14号引脚为输入状态:
使用杜邦线将14号引脚接到3.3V上,在查看引脚状态:
但是好奇心迫使我想看看WiringPi是如何实现底层外设的驱动的,是不是也是mem映射的方式,查看WiringPi源码工程中的源码文件wiringPi/wiringPi.c中的pinMode函数:
void pinMode (int pin, int mode)
{
int fSel, shift, alt ;
struct wiringPiNodeStruct *node = wiringPiNodes ;
int origPin = pin ;
setupCheck ("pinMode") ;
#ifdef CONFIG_ORANGEPI
if(version == ORANGEPI ) {
if (wiringPiDebug)
printf("PinMode: pin:%d,mode:%d\n", pin, mode);
if ((pin & PI_GPIO_MASK) == 0) {
if (wiringPiMode == WPI_MODE_PINS)
pin = pinToGpio[pin];
else if (wiringPiMode == WPI_MODE_PHYS)
pin = physToGpio[pin];
else if (wiringPiMode != WPI_MODE_GPIO)
return;
if (-1 == pin) {
printf("[%s:L%d] the pin:%d is invaild,please check it over!\n",
__func__, __LINE__, pin);
return;
}
if (mode == INPUT) {
OrangePi_set_gpio_mode(pin, INPUT);
wiringPinMode = INPUT;
return;
} else if (mode == OUTPUT) {
OrangePi_set_gpio_mode(pin, OUTPUT);
wiringPinMode = OUTPUT;
return ;
} else if (mode == PWM_OUTPUT) {
if(pin != 5) {
printf("the pin you choose doesn't support hardware PWM\n");
printf("you can select wiringPi pin %d for PWM pin\n", 42);
printf("or you can use it in softPwm mode\n");
return;
}
OrangePi_set_gpio_mode(pin, PWM_OUTPUT);
wiringPinMode = PWM_OUTPUT;
return;
} else
return;
} else {
if ((node = wiringPiFindNode (pin)) != NULL)
node->pinMode(node, pin, mode);
return ;
}
}
#endif
......
}
发现该函数中调用了OrangePi_set_gpio_mode函数用于设置引脚模式,该函数在wiringPi/OrangePi.c文件中:
/*
* Set GPIO Mode on OrangePi 2G-IOT
*/
int OrangePi_set_gpio_mode(int pin, int mode)
{
unsigned int regval = 0;
unsigned int bank = pin >> 5;
unsigned int index = pin - (bank << 5);
unsigned int phyaddr = 0;
#ifdef CONFIG_ORANGEPI_2G_IOT
unsigned int base_address = 0;
#elif CONFIG_ORANGEPI_RK3399
int offset = ((index - ((index >> 3) << 3)));
unsigned int cru_phyaddr, grf_phyaddr, gpio_phyaddr;
#else
int offset = ((index - ((index >> 3) << 3)) << 2);
if (bank == 11) {
phyaddr = GPIOL_BASE + ((index >> 3) << 2);
}
else
phyaddr = GPIO_BASE_MAP + (bank * 36) + ((index >> 3) << 2);
#endif
#ifdef CONFIG_ORANGEPI_2G_IOT
/* Offset of register */
if (bank == 0) /* group A */
base_address = GPIOA_BASE;
else if (bank == 1) /* group B */
base_address = GPIOB_BASE;
else if (bank == 2) /* group C */
base_address = GPIOC_BASE;
else if (bank == 3) /* group D */
base_address = GPIOD_BASE;
else
printf("Bad pin number\n");
if (mode == INPUT)
phyaddr = base_address + SET_IN_REGISTER;
else if (mode == OUTPUT)
phyaddr = base_address + OEN_SET_OUT_REGISTER;
else
printf("Invalid mode\n");
#elif CONFIG_ORANGEPI_RK3399
if(bank == 1){
cru_phyaddr = PMUCRU_BASE + PMUCRU_CLKGATE_CON1_OFFSET;
grf_phyaddr = PMUGRF_BASE + ((index >> 3) << 2) + 0x10;
gpio_phyaddr = GPIO1_BASE + GPIO_SWPORTA_DDR_OFFSET;
}
else if(bank == 2){
cru_phyaddr = CRU_BASE + CRU_CLKGATE_CON31_OFFSET;
grf_phyaddr = GRF_BASE + ((index >> 3) << 2);
gpio_phyaddr = GPIO2_BASE + GPIO_SWPORTA_DDR_OFFSET;
}
else if(bank == 4){
cru_phyaddr = CRU_BASE + CRU_CLKGATE_CON31_OFFSET;
grf_phyaddr = GRF_BASE + ((index >> 3) << 2) +0x20;
gpio_phyaddr = GPIO4_BASE + GPIO_SWPORTA_DDR_OFFSET;
}
else;
#endif
/* Ignore unused gpio */
if (ORANGEPI_PIN_MASK[bank][index] != -1) {
#if ! (defined CONFIG_ORANGEPI_2G_IOT || defined CONFIG_ORANGEPI_RK3399)
regval = readR(phyaddr);
if (wiringPiDebug)
printf("Before read reg val: 0x%x offset:%d\n",regval,offset);
#endif
if (wiringPiDebug)
printf("Register[%#x]: %#x index:%d\n", phyaddr, regval, index);
/* Set Input */
if(INPUT == mode) {
#ifdef CONFIG_ORANGEPI_2G_IOT
writeR(GPIO_BIT(index), phyaddr);
#elif CONFIG_ORANGEPI_RK3399
writeR(0xffff0180, cru_phyaddr);
regval = readR(grf_phyaddr);
regval |= 0xffff << 16;
regval &= ~(0x3) << (offset << 1);
writeR(regval, grf_phyaddr);
regval = readR(gpio_phyaddr);
regval &= ~(1 << index);
writeR(regval, gpio_phyaddr);
if (wiringPiDebug){
regval = readR(gpio_phyaddr);
printf("Input mode set over reg val: %#x\n",regval);
}
#else
regval &= ~(7 << offset);
writeR(regval, phyaddr);
regval = readR(phyaddr);
if (wiringPiDebug)
printf("Input mode set over reg val: %#x\n",regval);
#endif
} else if(OUTPUT == mode) { /* Set Output */
#ifdef CONFIG_ORANGEPI_2G_IOT
writeR(GPIO_BIT(index), phyaddr);
/* Set default value as 0 */
writeR(GPIO_BIT(index), base_address + CLR_REGISTER);
#elif CONFIG_ORANGEPI_RK3399
writeR(0xffff0180, cru_phyaddr);
regval = readR(grf_phyaddr);
regval |= 0xffff << 16;
regval &= ~(0x3) << (offset << 1);
writeR(regval, grf_phyaddr);
regval = readR(gpio_phyaddr);
regval |= 1 << index;
writeR(regval, gpio_phyaddr);
if (wiringPiDebug){
regval = readR(gpio_phyaddr);
printf("Out mode get value: 0x%x\n",regval);
}
#else
regval &= ~(7 << offset);
regval |= (1 << offset);
if (wiringPiDebug)
printf("Out mode ready set val: 0x%x\n",regval);
writeR(regval, phyaddr);
regval = readR(phyaddr);
if (wiringPiDebug)
printf("Out mode get value: 0x%x\n",regval);
#endif
}else if (PWM_OUTPUT == mode) {
// set pin PWMx to pwm mode
regval &= ~(7 << offset);
regval |= (0x3 << offset);
if (wiringPiDebug)
printf(">>>>>line:%d PWM mode ready to set val: 0x%x\n", __LINE__, regval);
writeR(regval, phyaddr);
delayMicroseconds(200);
regval = readR(phyaddr);
if (wiringPiDebug)
printf("<<<<<PWM mode set over reg val: 0x%x\n", regval);
//clear all reg
writeR(0, SUNXI_PWM_CTRL_REG);
writeR(0, SUNXI_PWM_CH0_PERIOD);
//set default M:S to 1/2
sunxi_pwm_set_period(1024);
sunxi_pwm_set_act(512);
pwmSetMode(PWM_MODE_MS);
sunxi_pwm_set_clk(PWM_CLK_DIV_120); //default clk:24M/120
delayMicroseconds(200);
}
else {
printf("Unknow mode\n");
}
} else
printf("Pin mode failed!\n");
return 0;
}
这里面一堆readR、writeR函数的调用,以readR为例查看源码:
/*
* Read register value helper
*/
unsigned int readR(unsigned int addr)
{
#ifdef CONFIG_ORANGEPI_2G_IOT
unsigned int val = 0;
unsigned int mmap_base = (addr & ~MAP_MASK);
unsigned int mmap_seek = (addr - mmap_base);
if (mmap_base == 0x11a08000) /* Group C */
val = *((char *)OrangePi_gpioC + mmap_seek);
else /* Group A, B and D */
val = *((char *)OrangePi_gpio + mmap_seek);
return val;
#elif CONFIG_ORANGEPI_RK3399
unsigned int val = 0;
unsigned int mmap_base = (addr & ~MAP_MASK);
unsigned int mmap_seek = (addr - mmap_base);
if(mmap_base == CRU_BASE)
val = *((unsigned int *)((unsigned char *)cru_base + mmap_seek));
else if(mmap_base == GRF_BASE)
val = *((unsigned int *)((unsigned char *)grf_base + mmap_seek));
else if(mmap_base == GPIO2_BASE)
val = *((unsigned int *)((unsigned char *)gpio2_base + mmap_seek));
else if(mmap_base == GPIO1_BASE)
val = *((unsigned int *)((unsigned char *)gpio1_base + mmap_seek));
else if(mmap_base == PMUCRU_BASE)
val = *((unsigned int *)((unsigned char *)pmucru_base + mmap_seek));
else if(mmap_base == PMUGRF_BASE)
val = *((unsigned int *)((unsigned char *)pmugrf_base + mmap_seek));
else if(mmap_base == GPIO4_BASE)
val = *((unsigned int *)((unsigned char *)gpio4_base + mmap_seek));
else ;
return val;
#else
uint32_t val = 0;
uint32_t mmap_base = (addr & ~MAP_MASK);
uint32_t mmap_seek = ((addr - mmap_base) >> 2);
if (addr >= GPIOL_BASE) {
val = *(OrangePi_gpioC + mmap_seek);
} else
val = *(OrangePi_gpio + mmap_seek);
return val;
#endif
}
readR函数里面的内容很像是寄存器操作的内容了,但是没有发现mem映设的代码,难道是提前映设好了,因为我看例程中会用WiringPi的时候需要先调用 wiringPiSetup () 函数,这个函数还是在wiringPi.c里面:
int wiringPiSetup (void)
{
......
// Open the master /dev/ memory control device
// Device strategy: December 2016:
// Try /dev/mem. If that fails, then
// try /dev/gpiomem. If that fails then game over.
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0)
{
if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) >= 0) // We're using gpiomem
{
piGpioBase = 0 ;
usingGpioMem = TRUE ;
}
else
return wiringPiFailure (WPI_ALMOST, "wiringPiSetup: Unable to open /dev/mem or /dev/gpiomem: %s.\n"
" Aborting your program because if it can not access the GPIO\n"
" hardware then it most certianly won't work\n"
" Try running with sudo?\n", strerror (errno)) ;
}
......
看到了不,打开/dev/mem进行映设操作,和我写的GPIO操作库原理是一样的,还发现有一个/dev/gpiomem,这个我估计是直接将GPIO的寄存器编成了一个驱动,直接操作这个设备就能控制GPIO寄存器了。