GPIO control register questions
Posted: Mon Sep 04, 2017 6:17 am
I've been digging through the ESP8266 technical reference, eagle_soc.h, esp8266_peri.h, and core_esp8266_wiring_digital.c to better under stand the GPIO registers. Its left me a bit confused, so I have a few questions about the GPIO control registers and how they really work.
Its my understanding that GPIO pins can be in one of 5 'modes'. Input floating, input pullup, input pulldown (only 1 pin supports this, GPIO16 or something I think), output push pull, and output open drain. So I've been looking through the source code files to try to understand which registers I need to write (and what values) to enable each of the 4 modes (ignoring input pulldown) on the GPIO pins.
Now the discrepancy is with the GPIO control registers. The ESP8266 techincal reference doesn't even mention the control registers in the documentation (despite describing all the other registers in detail), the only small acknowledgement of their existence is buried deep in Appendix 1. Even the examples of how to use the GPIO pins fails to mention their existence (but it also has references to macros that I can't find anywhere in code or documentation either so... I dunno... its like sections are just missing). eagle_soc.h likewise has no mention of them, no code for them, macros, etc...
Now the arduino source files, esp8266_peri.h and core_esp8266_wiring_digital.c, define and use these control registers, and for the most part it makes sense. Here's one function in particular:
Now the control registers define a 'driver' setting for each pin which is described in the documentation as: "1: open drain; 0: normal". I was assuming this was meant for output, and that normal was push-pull, while open drain meant just that, open drain output. Except as you can see in the above Arduino code, open drain is specifically being used for input GPIO modes. Why? Is my understanding of GPIO 'modes' wrong? Can you have an input that is also open drain? Is it something to do with the hardware I don't understand? Is it a mistake in the Arduino source code?
Also, is it possible to have interrupts enabled on an output pin? This seems counter-intuitive, so why would the Arduino code strive to leave interrupts unchanged if they cannot be used?
Last of all, what is a 'sigma-delate' pin source?
Its my understanding that GPIO pins can be in one of 5 'modes'. Input floating, input pullup, input pulldown (only 1 pin supports this, GPIO16 or something I think), output push pull, and output open drain. So I've been looking through the source code files to try to understand which registers I need to write (and what values) to enable each of the 4 modes (ignoring input pulldown) on the GPIO pins.
Now the discrepancy is with the GPIO control registers. The ESP8266 techincal reference doesn't even mention the control registers in the documentation (despite describing all the other registers in detail), the only small acknowledgement of their existence is buried deep in Appendix 1. Even the examples of how to use the GPIO pins fails to mention their existence (but it also has references to macros that I can't find anywhere in code or documentation either so... I dunno... its like sections are just missing). eagle_soc.h likewise has no mention of them, no code for them, macros, etc...
Now the arduino source files, esp8266_peri.h and core_esp8266_wiring_digital.c, define and use these control registers, and for the most part it makes sense. Here's one function in particular:
Code: Select all
extern void __pinMode(uint8_t pin, uint8_t mode) {
if(pin < 16){
if(mode == SPECIAL){
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
GPEC = (1 << pin); //Disable
GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin)
if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
} else if(mode & FUNCTION_0){
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
GPEC = (1 << pin); //Disable
GPF(pin) = GPFFS((mode >> 4) & 0x07);
if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX
} else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD);
GPES = (1 << pin); //Enable
} else if(mode == INPUT || mode == INPUT_PULLUP){
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
GPEC = (1 << pin); //Disable
GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
if(mode == INPUT_PULLUP) {
GPF(pin) |= (1 << GPFPU); // Enable Pullup
}
} else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){
GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO
GPEC = (1 << pin); //Disable
if(mode == WAKEUP_PULLUP) {
GPF(pin) |= (1 << GPFPU); // Enable Pullup
GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED)
} else {
GPF(pin) |= (1 << GPFPD); // Enable Pulldown
GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED)
}
}
} else if(pin == 16){
GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO
GPC16 = 0;
if(mode == INPUT || mode == INPUT_PULLDOWN_16){
if(mode == INPUT_PULLDOWN_16){
GPF16 |= (1 << GP16FPD);//Enable Pulldown
}
GP16E &= ~1;
} else if(mode == OUTPUT){
GP16E |= 1;
}
}
}
Now the control registers define a 'driver' setting for each pin which is described in the documentation as: "1: open drain; 0: normal". I was assuming this was meant for output, and that normal was push-pull, while open drain meant just that, open drain output. Except as you can see in the above Arduino code, open drain is specifically being used for input GPIO modes. Why? Is my understanding of GPIO 'modes' wrong? Can you have an input that is also open drain? Is it something to do with the hardware I don't understand? Is it a mistake in the Arduino source code?
Also, is it possible to have interrupts enabled on an output pin? This seems counter-intuitive, so why would the Arduino code strive to leave interrupts unchanged if they cannot be used?
Last of all, what is a 'sigma-delate' pin source?