【Linux开发】Orange Pi上WiringPi的使用

参考一篇我之前写的文章: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寄存器了。

猜你喜欢

转载自blog.csdn.net/tq384998430/article/details/99711282