Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add open-drain GPIO support. #15282

Merged
merged 6 commits into from
Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 13 additions & 11 deletions docs/internals_gpio_control.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ QMK has a GPIO control abstraction layer which is microcontroller agnostic. This

The following functions provide basic control of GPIOs and are found in `platforms/<platform>/gpio.h`.

|Function |Description | Old AVR Examples | Old ChibiOS/ARM Examples |
|------------------------|--------------------------------------------------|-------------------------------------------------|-------------------------------------------------|
| `setPinInput(pin)` | Set pin as input with high impedance (High-Z) | `DDRB &= ~(1<<2)` | `palSetLineMode(pin, PAL_MODE_INPUT)` |
| `setPinInputHigh(pin)` | Set pin as input with builtin pull-up resistor | `DDRB &= ~(1<<2); PORTB \|= (1<<2)` | `palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)` |
| `setPinInputLow(pin)` | Set pin as input with builtin pull-down resistor | N/A (Not supported on AVR) | `palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)` |
| `setPinOutput(pin)` | Set pin as output | `DDRB \|= (1<<2)` | `palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)` |
| `writePinHigh(pin)` | Set pin level as high, assuming it is an output | `PORTB \|= (1<<2)` | `palSetLine(pin)` |
| `writePinLow(pin)` | Set pin level as low, assuming it is an output | `PORTB &= ~(1<<2)` | `palClearLine(pin)` |
| `writePin(pin, level)` | Set pin level, assuming it is an output | `(level) ? PORTB \|= (1<<2) : PORTB &= ~(1<<2)` | `(level) ? palSetLine(pin) : palClearLine(pin)` |
| `readPin(pin)` | Returns the level of the pin | `_SFR_IO8(pin >> 4) & _BV(pin & 0xF)` | `palReadLine(pin)` |
| `togglePin(pin)` | Invert pin level, assuming it is an output | `PORTB ^= (1<<2)` | `palToggleLine(pin)` |
| Function | Description | Old AVR Examples | Old ChibiOS/ARM Examples |
|------------------------------|-----------------------------------------------------|-------------------------------------------------|--------------------------------------------------|
| `setPinInput(pin)` | Set pin as input with high impedance (High-Z) | `DDRB &= ~(1<<2)` | `palSetLineMode(pin, PAL_MODE_INPUT)` |
| `setPinInputHigh(pin)` | Set pin as input with builtin pull-up resistor | `DDRB &= ~(1<<2); PORTB \|= (1<<2)` | `palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)` |
| `setPinInputLow(pin)` | Set pin as input with builtin pull-down resistor | N/A (Not supported on AVR) | `palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)` |
| `setPinOutput(pin)` | Set pin as output (alias of `setPinOutputPushPull`) | `DDRB \|= (1<<2)` | `palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)` |
| `setPinOutputPushPull(pin)` | Set pin as output, push/pull mode | `DDRB \|= (1<<2)` | `palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)` |
| `setPinOutputOpenDrain(pin)` | Set pin as output, open-drain mode | N/A (Not implemented on AVR) | `palSetLineMode(pin, PAL_MODE_OUTPUT_OPENDRAIN)` |
| `writePinHigh(pin)` | Set pin level as high, assuming it is an output | `PORTB \|= (1<<2)` | `palSetLine(pin)` |
| `writePinLow(pin)` | Set pin level as low, assuming it is an output | `PORTB &= ~(1<<2)` | `palClearLine(pin)` |
| `writePin(pin, level)` | Set pin level, assuming it is an output | `(level) ? PORTB \|= (1<<2) : PORTB &= ~(1<<2)` | `(level) ? palSetLine(pin) : palClearLine(pin)` |
| `readPin(pin)` | Returns the level of the pin | `_SFR_IO8(pin >> 4) & _BV(pin & 0xF)` | `palReadLine(pin)` |
| `togglePin(pin)` | Invert pin level, assuming it is an output | `PORTB ^= (1<<2)` | `palToggleLine(pin)` |

## Advanced Settings :id=advanced-settings

Expand Down
12 changes: 8 additions & 4 deletions platforms/arm_atsam/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@

typedef uint8_t pin_t;

#define SAMD_PORT(pin) ((pin & 0x20) >> 5)
#define SAMD_PIN(pin) (pin & 0x1f)
#define SAMD_PIN_MASK(pin) (1 << (pin & 0x1f))
#define SAMD_PORT(pin) (((pin)&0x20) >> 5)
#define SAMD_PIN(pin) ((pin)&0x1f)
#define SAMD_PIN_MASK(pin) (1 << ((pin)&0x1f))

#define setPinInput(pin) \
do { \
Expand All @@ -48,12 +48,16 @@ typedef uint8_t pin_t;
PORT->Group[SAMD_PORT(pin)].PINCFG[SAMD_PIN(pin)].bit.PULLEN = 1; \
} while (0)

#define setPinOutput(pin) \
#define setPinOutputPushPull(pin) \
do { \
PORT->Group[SAMD_PORT(pin)].DIRSET.reg = SAMD_PIN_MASK(pin); \
PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
} while (0)

#define setPinOutputOpenDrain(pin) _Static_assert(0, "arm_atsam platform does not implement an open-drain output")

#define setPinOutput(pin) setPinOutputPushPull(pin)

#define writePinHigh(pin) \
do { \
PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \
Expand Down
8 changes: 6 additions & 2 deletions platforms/avr/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ typedef uint8_t pin_t;
#define setPinInput(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
#define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
#define setPinInputLow(pin) _Static_assert(0, "AVR processors cannot implement an input as pull low")
#define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
#define setPinOutputPushPull(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))
#define setPinOutputOpenDrain(pin) _Static_assert(0, "AVR platform does not implement an open-drain output")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it is possible to implement an open-drain output on AVR — set the PORTx bit to 0, then write the inverted output value to the DDRx bit. The problem is that it requires either some conditional code in the writePin() implementation, or a separate set of macros like writeOpenDrainPin(), writeOpenDrainPinHigh(), writeOpenDrainPinLow() (with bad consequences if you use a wrong kind of macro for the chosen pin mode).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, but nobody has asked for it, and nobody's using it. Figured it'd be a problem for another day.

#define setPinOutput(pin) setPinOutputPushPull(pin)

#define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))
#define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))
Expand All @@ -43,7 +45,9 @@ typedef uint8_t port_data_t;

#define setPortBitInput(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
#define setPortBitInputHigh(port, bit) (DDRx_ADDRESS(port) &= ~_BV((bit)&0xF), PORTx_ADDRESS(port) |= _BV((bit)&0xF))
#define setPortBitOutput(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
#define setPortBitOutputPushPull(port, bit) (DDRx_ADDRESS(port) |= _BV((bit)&0xF))
#define setPortBitOutputOpenDrain(port, bit) _Static_assert(0, "AVR platform does not implement an open-drain output")
#define setPortBitOutput(port, bit) setPortBitOutputPushPull((port), (bit))

#define writePortBitLow(port, bit) (PORTx_ADDRESS(port) &= ~_BV((bit)&0xF))
#define writePortBitHigh(port, bit) (PORTx_ADDRESS(port) |= _BV((bit)&0xF))
22 changes: 12 additions & 10 deletions platforms/chibios/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ typedef ioline_t pin_t;

/* Operation of GPIO by pin. */

#define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
#define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
#define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
#define setPinOutput(pin) palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)
#define setPinInput(pin) palSetLineMode((pin), PAL_MODE_INPUT)
#define setPinInputHigh(pin) palSetLineMode((pin), PAL_MODE_INPUT_PULLUP)
#define setPinInputLow(pin) palSetLineMode((pin), PAL_MODE_INPUT_PULLDOWN)
#define setPinOutputPushPull(pin) palSetLineMode((pin), PAL_MODE_OUTPUT_PUSHPULL)
#define setPinOutputOpenDrain(pin) palSetLineMode((pin), PAL_MODE_OUTPUT_OPENDRAIN)
#define setPinOutput(pin) setPinOutputPushPull(pin)

#define writePinHigh(pin) palSetLine(pin)
#define writePinLow(pin) palClearLine(pin)
Expand All @@ -41,10 +43,10 @@ typedef uint16_t port_data_t;

#define readPort(pin) palReadPort(PAL_PORT(pin))

#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT)
#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLUP)
#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_INPUT_PULLDOWN)
#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), bit, PAL_MODE_OUTPUT_PUSHPULL)
#define setPortBitInput(pin, bit) palSetPadMode(PAL_PORT(pin), (bit), PAL_MODE_INPUT)
#define setPortBitInputHigh(pin, bit) palSetPadMode(PAL_PORT(pin), (bit), PAL_MODE_INPUT_PULLUP)
#define setPortBitInputLow(pin, bit) palSetPadMode(PAL_PORT(pin), (bit), PAL_MODE_INPUT_PULLDOWN)
#define setPortBitOutput(pin, bit) palSetPadMode(PAL_PORT(pin), (bit), PAL_MODE_OUTPUT_PUSHPULL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks like the implementation of setPortBitOutputPushPull() and setPortBitOutputOpenDrain() is missing here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on Discord, given that it's not in use anywhere but one keyboard, and it has its own implementation of the port-based GPIO functions, the port-related operations have been removed for the time being until we work out a proper solution for them.


#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), bit))
#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), bit))
#define writePortBitLow(pin, bit) palClearLine(PAL_LINE(PAL_PORT(pin), (bit)))
#define writePortBitHigh(pin, bit) palSetLine(PAL_LINE(PAL_PORT(pin), (bit)))