summaryrefslogtreecommitdiff
path: root/avr/gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'avr/gpio.c')
-rw-r--r--avr/gpio.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/avr/gpio.c b/avr/gpio.c
new file mode 100644
index 0000000..3c47247
--- /dev/null
+++ b/avr/gpio.c
@@ -0,0 +1,371 @@
+#include "common.h"
+#include <util/atomic.h>
+#include <limits.h>
+#include "debug.h"
+#include "gpio.h"
+
+
+/*
+
+Pin Name Port Timer Mode max div max div min f [Hz]
+----------------------------------------------------------------------------------
+0 PG5 OC0B PWM (2**8)*1024 262144 70.31
+1 PG4
+2 CLK2 PB4 OC2A Toggle (2**8)*1024*2 524288 35.16
+3 ZCLK PB5 OC1A PWM (2**16)*1024 67108864 0.2746
+4 PB6 OC1B PWM (2**16)*1024 67108864 0.2746
+5 PB7 OC0A Toggle (2**8)*1024*2 524288 35.16
+6 PG3
+7 PG2
+8 PG1
+9 PG0
+10 CLKO PE7
+
+
+pre Timer0 Timer1 Timer2
+--------------------------------------------------
+0 0 0 0
+1 1 1 1
+2 8 x8 8 x8 8 x8
+3 64 x8 64 x8 32 x4
+4 256 x4 256 x4 64 x2
+5 1024 x4 1024 x4 128 x2
+6 256 x2
+7 1024 x4
+--------------------------------------------------
+
+*/
+
+
+#define PWMTOGGLE 0b01
+#define PWMPOS 0b10
+#define PWMNEG 0b11
+
+
+const FLASH uint8_t prescale_factors_01[] =
+ { 8, 8, 4, 4, 0 };
+
+const FLASH uint8_t prescale_factors_2[] =
+ { 8, 4, 2, 2, 2, 4, 0 };
+
+typedef volatile struct {
+ uint8_t pin;
+ uint8_t ddr;
+ uint8_t pout;
+} port_t ;
+
+struct pindef_s {
+ port_t * const adr;
+ const uint8_t mask;
+#define NO_TIMER 0
+#define TIMER0 (1 << 0)
+#define TIMER1 (2 << 0)
+#define TIMER2 (3 << 0)
+#define TIMER (3 << 0)
+#define T_16BIT (1 << 3)
+#define CHANA (1 << 4)
+#define CHANB (0 << 4)
+ const uint8_t timer;
+};
+
+
+const FLASH struct pindef_s pinlist[GPIO_MAX] = {
+ { (port_t *) &PING, _BV(5), TIMER0 | CHANB },
+ { (port_t *) &PING, _BV(4), NO_TIMER },
+ { (port_t *) &PINB, _BV(4), TIMER2 | CHANA },
+ { (port_t *) &PINB, _BV(5), TIMER1 | CHANA | T_16BIT },
+ { (port_t *) &PINB, _BV(6), TIMER1 | CHANB | T_16BIT },
+ { (port_t *) &PINB, _BV(7), TIMER0 | CHANA },
+ { (port_t *) &PING, _BV(3), NO_TIMER },
+ { (port_t *) &PING, _BV(2), NO_TIMER },
+ { (port_t *) &PING, _BV(1), NO_TIMER },
+ { (port_t *) &PING, _BV(0), NO_TIMER },
+ { (port_t *) &PINE, _BV(7), NO_TIMER },
+};
+
+void gpio_timer_off(uint8_t timertype)
+{
+ uint8_t chan_mask;
+
+ if (timertype & CHANA)
+ chan_mask = 0xc0;
+ else
+ chan_mask = 0x30;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ if (TCCR0A & chan_mask) {
+ TCCR0B = 0;
+ TCCR0A = 0;
+ PRR0 |= _BV(PRTIM0);
+ }
+ break;
+ case TIMER1:
+ if (TCCR1A & chan_mask) {
+ TCCR1B = 0;
+ TCCR1A = 0;
+ PRR0 |= _BV(PRTIM1);
+ }
+ break;
+ case TIMER2:
+ if (TCCR2A & chan_mask) {
+ TCCR2B = 0;
+ TCCR2A = 0;
+ PRR0 |= _BV(PRTIM2);
+ }
+ break;
+ }
+}
+
+int gpio_config(int pin, gpiomode_t mode)
+{
+ if ((unsigned) pin >= ARRAY_SIZE(pinlist)) {
+ /* Invalid pin number */
+ return -1;
+ } else {
+
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ switch (mode) {
+ case INPUT:
+ gpio_timer_off(pinlist[pin].timer);
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr &= ~bit;
+ p->pout &= ~bit;
+ }
+ break;
+ case INPUT_PULLUP:
+ gpio_timer_off(pinlist[pin].timer);
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr &= ~bit;
+ p->pout |= bit;
+ }
+ break;
+ case OUTPUT:
+ gpio_timer_off(pinlist[pin].timer);
+ case OUTPUT_TIMER:
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ p->ddr |= bit;
+ }
+ break;
+ default:
+ /* Invalid pin mode */
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void gpio_write(int pin, uint8_t val)
+{
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ ATOMIC_BLOCK(ATOMIC_FORCEON) {
+ if (val)
+ p->pout |= bit;
+ else
+ p->pout &= ~bit;
+ }
+}
+
+int gpio_read(int pin)
+{
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ return (p->pin & bit) != 0;
+}
+
+gpiomode_t gpio_config_get(int pin)
+{
+ uint8_t timertype = pinlist[pin].timer;
+
+ if (timertype & TIMER) {
+
+ uint8_t chan_mask;
+ if (timertype & CHANA)
+ chan_mask = 0xc0;
+ else
+ chan_mask = 0x30;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ if (TCCR0A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ case TIMER1:
+ if (TCCR1A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ case TIMER2:
+ if (TCCR2A & chan_mask)
+ return OUTPUT_TIMER;
+ break;
+ }
+ }
+
+ port_t *p = pinlist[pin].adr;
+ uint8_t bit = pinlist[pin].mask;
+
+ if (p->ddr & bit)
+ return OUTPUT;
+
+ if (p->pout & bit)
+ return INPUT_PULLUP;
+
+ return INPUT;
+}
+
+/*
+ * return -1: pin has no timer output
+ * 0: pin is not configured for timer output
+ * > 0: divider
+ */
+
+long gpio_clockdiv_get(int pin)
+{
+ long divider;
+ uint8_t prescale;
+ const FLASH uint8_t *pstab;
+
+ uint8_t timertype = pinlist[pin].timer;
+ if ((timertype & TIMER) == 0)
+ return -1;
+
+ if (gpio_config_get(pin) != OUTPUT_TIMER)
+ return 0;
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ prescale = TCCR0B;
+ divider = OCR0A;
+ break;
+
+ case TIMER1:
+ prescale = TCCR1B;
+ divider = ICR1;
+ break;
+
+ case TIMER2:
+ prescale = TCCR2B;
+ divider = OCR2A;
+ break;
+ }
+
+ prescale = (prescale & 0x07) - 1;
+ divider += 1;
+
+ pstab = (timertype & TIMER) == TIMER2 ?
+ prescale_factors_2 : prescale_factors_01;
+
+ while (prescale--)
+ divider *= pstab[prescale];
+
+ if ((timertype & (CHANA|T_16BIT)) == CHANA)
+ divider *= 2;
+
+ return divider;
+}
+
+int gpio_clockdiv_set(int pin, unsigned long divider)
+{
+ unsigned long ltop;
+ uint16_t top;
+ uint8_t prescale;
+ const FLASH uint8_t *pstab;
+
+ uint8_t timertype = pinlist[pin].timer;
+ if ((timertype & TIMER) == 0)
+ return 0;
+
+ if (divider < 2)
+ return -1;
+
+ ltop = divider;
+ if ((timertype & (CHANA|T_16BIT)) == CHANA)
+ ltop /= 2;
+
+ if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8)))
+ return -1;
+
+ prescale = 1;
+ pstab = (timertype & TIMER) == TIMER2 ?
+ prescale_factors_2 : prescale_factors_01;
+
+// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d\n",
+// pin, ltop, prescale);
+
+ while (ltop > ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) {
+// debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d, *pstab %d\n",
+// pin, ltop, prescale, *pstab);
+
+ if (*pstab == 0)
+ return -1;
+ ltop /= *pstab++;
+ prescale++;
+ }
+
+ if (ltop == 0)
+ return -1;
+
+ top = ltop - 1;
+
+ PING |= _BV(0); /* Debug */
+
+ switch (timertype & TIMER) {
+ case TIMER0:
+ PRR0 &= ~_BV(PRTIM0);
+ TCCR0B = (1 << WGM02);
+ TCNT0 = 0;
+ OCR0A = top;
+ if (timertype & CHANA) {
+ TCCR0A = (PWMTOGGLE << COM0A0) | (0b11 << WGM00);
+ } else {
+ OCR0B = top/2;
+ TCCR0A = (PWMPOS << COM0B0) | (0b11 << WGM10);
+ }
+ TCCR0B = (1 << WGM02) | (prescale << CS10);
+ break;
+
+ case TIMER1:
+ PRR0 &= ~_BV(PRTIM1);
+ TCCR1B = (0b11 << WGM12);
+ TCNT1 = 0;
+ ICR1 = top;
+ if (timertype & CHANA) {
+ OCR1A = top/2;
+ TCCR1A = (PWMPOS << COM1A0) | (0b10 << WGM10);
+ } else {
+ OCR1B = top/2;
+ TCCR1A = (PWMPOS << COM1B0) | (0b10 << WGM10);
+ }
+// debug("pin: %d, top: %u,"
+// " ICR1: %u, OCR1A: %u, OCR1B: %u\n",
+// pin, top, ICR1, OCR1A, OCR1B);
+
+ TCCR1B = (0b11 << WGM12) | (prescale << CS10);
+ break;
+
+ case TIMER2:
+ PRR0 &= ~_BV(PRTIM2);
+ TCCR2B = (1 << WGM22);
+ TCNT2 = 0;
+ OCR2A = top;
+ if (timertype & CHANA) {
+ TCCR2A = (PWMTOGGLE << COM2A0) | (0b11 << WGM20);
+ } else {
+ OCR2B = top/2;
+ TCCR2A = (PWMPOS << COM2B0) | (0b11 << WGM10);
+ }
+ TCCR2B = (1 << WGM22) | (prescale << CS10);
+ break;
+ }
+
+ PING |= _BV(0); /* Debug */
+
+ gpio_config(pin, OUTPUT_TIMER);
+
+ return 0;
+}