Do you use standard GPIO_OUTPUT_SET() macros for your bit-banging and do you want to make it faster? Well, then you're on the right place. Continue reading and you'll see how to get 6x faster GPIOs compared to standard GPIO_OUTPUT_SET() macros.
The way I see it, GPIO_OUTPUT_SET() was/is intended to be just for first initialization. For controlling the output GPIOs in the code it's much better to skip all the unnecessary calling underlying the GPIO_OUTPUT_SET() and set output GPIO pins directly using GPIO_OUT_W1TS_ADDRESS / GPIO_OUT_W1TC_ADDRESS registers. The resulting code will be smaller and faster.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15);
GPIO_OUTPUT_SET(15, 1);
//
while(1){
uint8_t i;
for(i=0;i<20;i++) GPIO_OUTPUT_SET(15, 1);
GPIO_OUTPUT_SET(15, 0);
//
vTaskDelay(1);
}
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15);
GPIO_OUTPUT_SET(15, 1);
//
while(1){
uint8_t i;
for(i=0;i<20;i++) GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1<<15);
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1<<15);
//
vTaskDelay(1);
}
The timebase of the scope is set to 2 us per div in both test cases.
Test A - impulse length is around 12 us.
Test B - impulse length is around 1.9 us
=> code B is around 6x faster than A
Tests configuration: ESP8266 running at default 80 MHz, RTOS (SDK 1.4.0)
Each register is for all GPIOs - every bit corresponds to one GPIO (GPIO 0 is bit0, GPIO 1 is bit1, ...). You don't need to mask anything. If you just want to set one GPIO H/L, set the proper bit to 1, leave other bits to 0. Easy, fancy, fast.
GPIO_OUT_W1TS_ADDRESS - write 1 to set GPIO (high)
GPIO_OUT_W1TC_ADDRESS - write 1 to clear GPIO (low)
Reading these registers, will give you 0 - they are just for writing.
It's definitely better to use direct GPIO control of output pins by writing to GPIO_OUT_W1TS_ADDRESS / GPIO_OUT_W1TC_ADDRESS registers. I've briefly looked into Espressif's GPIO driver (gpio.c, gpio.h) but there are no macros dedicated to this exactly. But anyway, it's so easy that we can do our own macros.
Also, if you're using standard GPIO_INPUT_GET(...) for reading of GPIO inputs, you can optimize for speed. Just simply read GPIO_IN_ADDRESS register directly like this:
((GPIO_REG_READ(GPIO_IN_ADDRESS)&(BIT(12)))!=0)
To sum it up and give you some hints:
... macros:
#define GPIO4 ((GPIO_REG_READ(GPIO_IN_ADDRESS)&(BIT(4)))!=0) //each bit is for corresponding GPIO
#define GPIO2_H (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1<<2))
#define GPIO2_L (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1<<2))
#define GPIO2(x) ((x)?GPIO2_H:GPIO2_L)
... init:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
GPIO_OUTPUT_SET(2, 0); //GPIO2 as output low
GPIO_DIS_OUTPUT(4); //GPIO4 as input
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
... work with GPIOs:
GPIO2_H; //set GPIO2 high (as fast as possible)
GPIO2_L; //set GPIO2 low (as fast as possible)
GPIO2(1); //set GPIO2 high (slightly slower, because of decision in macro)
GPIO2(GPIO4); //set on GPIO2 the same level as is on GPIO4 input