diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2024-12-08 14:22:05 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2024-12-08 14:22:05 -0700 |
commit | f89f75b5616de99865448f41b068a2783cd3648e (patch) | |
tree | 1e7599b8915d3fbb9a1a10892ba58fd44bea3f5d /src/gpio.c | |
parent | 546a5ccdba66dd8d8c19ce6d8486f46c84637cf2 (diff) | |
download | ch573-f89f75b5616de99865448f41b068a2783cd3648e.tar.gz ch573-f89f75b5616de99865448f41b068a2783cd3648e.tar.bz2 ch573-f89f75b5616de99865448f41b068a2783cd3648e.zip |
Cleaned up GPIO code quite a bit.
Diffstat (limited to 'src/gpio.c')
-rw-r--r-- | src/gpio.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/gpio.c b/src/gpio.c new file mode 100644 index 0000000..e016179 --- /dev/null +++ b/src/gpio.c @@ -0,0 +1,168 @@ +#include "gpio.h" + +#include "ch573/pfic.h" +#include "isr_vector.h" + +void configure_gpio(gpio_pin_t pin, gpio_config_t cfg) +{ + int ipin = pin; + struct ch573_gpio__gpio_port_t* port; + if (pin & IS_PORT_B) { + port = GPIO_PORT_B; + ipin &= ~IS_PORT_B; + } else { + port = GPIO_PORT_A; + } + + switch (cfg) { + case PIN_CFG_INPUT_FLOATING: + GPIO_PORT.dir.set(port, DIR_IN, ipin); + GPIO_PORT.pu.set(port, DISABLED, ipin); + GPIO_PORT.pd_drv.set(port, 0, ipin); + break; + case PIN_CFG_INPUT_PULL_UP: + GPIO_PORT.dir.set(port, DIR_IN, ipin); + GPIO_PORT.pu.set(port, ENABLED, ipin); + GPIO_PORT.pd_drv.set(port, 0, ipin); + break; + case PIN_CFG_INPUT_PULL_DOWN: + GPIO_PORT.dir.set(port, DIR_IN, ipin); + GPIO_PORT.pu.set(port, DISABLED, ipin); + GPIO_PORT.pd_drv.set(port, 1, ipin); + break; + case PIN_CFG_OUTPUT_5mA: + GPIO_PORT.dir.set(port, DIR_OUT, ipin); + GPIO_PORT.pu.set(port, DISABLED, ipin); + GPIO_PORT.pd_drv.set(port, 0, ipin); + break; + case PIN_CFG_OUTPUT_20mA: + GPIO_PORT.dir.set(port, DIR_OUT, ipin); + GPIO_PORT.pu.set(port, DISABLED, ipin); + GPIO_PORT.pd_drv.set(port, 1, ipin); + break; + } +} + +int read_gpio(gpio_pin_t pin) +{ + int ipin = pin; + struct ch573_gpio__gpio_port_t* port; + if (pin & IS_PORT_B) { + port = GPIO_PORT_B; + ipin &= ~IS_PORT_B; + } else { + port = GPIO_PORT_A; + } + + return !!GPIO_PORT.pin.in.get(port, ipin); +} + +void set_gpio(gpio_pin_t pin, int high) +{ + int ipin = pin; + struct ch573_gpio__gpio_port_t* port; + if (pin & IS_PORT_B) { + port = GPIO_PORT_B; + ipin &= ~IS_PORT_B; + } else { + port = GPIO_PORT_A; + } + + GPIO_PORT.out.set(port, !!high, ipin); +} + +void enable_alternate_function(alternate_function_t afn) +{ + GPIO_I.pin_alternate.set(GPIO, 1 << afn); +} + +#define PFIC ch573_pfic__pfic +static void enable_gpio_pfic(int irq) +{ + PFIC->interrupt_priority_threshold = 0x10; + PFIC->interrupt_enable |= irq; +} + +void enable_gpio_interrupt(gpio_pin_t pin, interrupt_mode_t int_mode) +{ + int ipin = pin; + struct ch573_gpio__gpio_port_t* port; + + volatile uint16_t* port_int_mode; + volatile uint16_t* port_int_enable; + volatile uint16_t* port_int_flag; + + if (pin & IS_PORT_B) { + port = GPIO_PORT_B; + ipin &= ~IS_PORT_B; + port_int_mode = &GPIO->pb_interrupt_mode; + port_int_flag = &GPIO->pb_interrupt_flag; + port_int_enable = &GPIO->pb_interrupt_enable; + + enable_gpio_pfic(IRQ_GpioB); + } else { + port = GPIO_PORT_A; + port_int_mode = &GPIO->pa_interrupt_mode; + port_int_flag = &GPIO->pa_interrupt_flag; + port_int_enable = &GPIO->pa_interrupt_enable; + + enable_gpio_pfic(IRQ_GpioA); + } + + switch (int_mode) { + case INT_MODE_FALLING_EDGE: + *port_int_mode |= 1 << ipin; + GPIO_PORT.clr.set(port, 1 << ipin); + break; + + case INT_MODE_RISING_EDGE: + *port_int_mode |= 1 << ipin; + GPIO_PORT.out.set(port, 1, ipin); + break; + + case INT_MODE_LEVEL_HIGH: + *port_int_mode &= ~(1 << ipin); + GPIO_PORT.out.set(port, 1, ipin); + break; + + case INT_MODE_LEVEL_LOW: + *port_int_mode &= ~(1 << ipin); + GPIO_PORT.clr.set(port, 1 << ipin); + break; + } + + *port_int_flag = 1 << ipin; + *port_int_enable |= 1 << ipin; +} + +extern gpio_callback_t GPIO_LISTENERS_START; +extern gpio_callback_t GPIO_LISTENERS_END; +void fire_irq(volatile uint16_t* int_flag_ptr, int is_port_b) +{ + gpio_callback_t* cur = &GPIO_LISTENERS_START; + + uint16_t pin = 0; + uint16_t intflag = *int_flag_ptr; + + for (pin = 0; pin < 16; ++pin) { + if (intflag & (1 << pin)) { + // If this pin triggered an interrupt, fire the GPIO listeners. + for (cur = &GPIO_LISTENERS_START; cur < &GPIO_LISTENERS_END; ++cur) { + (*cur)(pin | is_port_b); + } + *int_flag_ptr |= 1 << pin; // Clear the interrupt for this pin. + } + } + + *int_flag_ptr = 0xffff; +} + +IRQ(gpio_b) +{ + fire_irq(&GPIO->pb_interrupt_flag, IS_PORT_B); +} + +IRQ(gpio_a) +{ + fire_irq(&GPIO->pa_interrupt_flag, 0); +} |