From f582a36e4df16d5709943f7df17a900c8bcc12ab Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 25 May 2016 11:37:40 -0400 Subject: Initial commit of source code. Signed-off-by: Kevin O'Connor --- src/avr/gpio.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 src/avr/gpio.c (limited to 'src/avr/gpio.c') diff --git a/src/avr/gpio.c b/src/avr/gpio.c new file mode 100644 index 00000000..d2cdfbd8 --- /dev/null +++ b/src/avr/gpio.c @@ -0,0 +1,337 @@ +// GPIO functions on AVR. +// +// Copyright (C) 2016 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include // NULL +#include "autoconf.h" // CONFIG_MACH_atmega644p +#include "command.h" // shutdown +#include "gpio.h" // gpio_out_write +#include "irq.h" // irq_save +#include "pgm.h" // PROGMEM +#include "sched.h" // DECL_INIT + + +/**************************************************************** + * AVR chip definitions + ****************************************************************/ + +#define GPIO(PORT, NUM) (((PORT)-'A') * 8 + (NUM)) +#define GPIO2PORT(PIN) ((PIN) / 8) +#define GPIO2BIT(PIN) (1<<((PIN) % 8)) + +static volatile uint8_t * const digital_regs[] PROGMEM = { +#ifdef PINA + &PINA, +#else + NULL, +#endif + &PINB, &PINC, &PIND, +#ifdef PINE + &PINE, &PINF, &PING, &PINH, NULL, &PINJ, &PINK, &PINL +#endif +}; + +struct gpio_digital_regs { + // gcc (pre v6) does better optimization when uint8_t are bitfields + volatile uint8_t in : 8, mode : 8, out : 8; +}; + +#define GPIO2REGS(pin) \ + ((struct gpio_digital_regs*)READP(digital_regs[GPIO2PORT(pin)])) + +struct gpio_pwm_info { + volatile void *ocr; + volatile uint8_t *rega, *regb; + uint8_t en_bit, pin, flags; +}; + +enum { GP_8BIT=1, GP_AFMT=2 }; + +static const struct gpio_pwm_info pwm_regs[] PROGMEM = { +#if CONFIG_MACH_atmega168 + { &OCR0A, &TCCR0A, &TCCR0B, 1< ARRAY_SIZE(digital_regs)) + goto fail; + struct gpio_digital_regs *regs = GPIO2REGS(pin); + if (! regs) + goto fail; + uint8_t bit = GPIO2BIT(pin); + uint8_t flag = irq_save(); + regs->out = val ? (regs->out | bit) : (regs->out & ~bit); + regs->mode |= bit; + irq_restore(flag); + return (struct gpio_out){ .regs=regs, .bit=bit }; +fail: + shutdown("Not an output pin"); +} + +void gpio_out_toggle(struct gpio_out g) +{ + g.regs->in = g.bit; +} + +void +gpio_out_write(struct gpio_out g, uint8_t val) +{ + uint8_t flag = irq_save(); + g.regs->out = val ? (g.regs->out | g.bit) : (g.regs->out & ~g.bit); + irq_restore(flag); +} + +struct gpio_in +gpio_in_setup(uint8_t pin, int8_t pull_up) +{ + if (GPIO2PORT(pin) > ARRAY_SIZE(digital_regs)) + goto fail; + struct gpio_digital_regs *regs = GPIO2REGS(pin); + if (! regs) + goto fail; + uint8_t bit = GPIO2BIT(pin); + uint8_t flag = irq_save(); + regs->out = pull_up > 0 ? (regs->out | bit) : (regs->out & ~bit); + regs->mode &= ~bit; + irq_restore(flag); + return (struct gpio_in){ .regs=regs, .bit=bit }; +fail: + shutdown("Not an input pin"); +} + +uint8_t +gpio_in_read(struct gpio_in g) +{ + return !!(g.regs->in & g.bit); +} + + +void +gpio_pwm_write(struct gpio_pwm g, uint8_t val) +{ + if (g.size8) { + *(volatile uint8_t*)g.reg = val; + } else { + uint8_t flag = irq_save(); + *(volatile uint16_t*)g.reg = val; + irq_restore(flag); + } +} + +struct gpio_pwm +gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) +{ + uint8_t chan; + for (chan=0; chanpin) != pin) + continue; + uint8_t flags = READP(p->flags), cs; + if (flags & GP_AFMT) { + switch (cycle_time) { + case 0 ... 8*510L - 1: cs = 1; break; + case 8*510L ... 32*510L - 1: cs = 2; break; + case 32*510L ... 64*510L - 1: cs = 3; break; + case 64*510L ... 128*510L - 1: cs = 4; break; + case 128*510L ... 256*510L - 1: cs = 5; break; + case 256*510L ... 1024*510L - 1: cs = 6; break; + default: cs = 7; break; + } + } else { + switch (cycle_time) { + case 0 ... 8*510L - 1: cs = 1; break; + case 8*510L ... 64*510L - 1: cs = 2; break; + case 64*510L ... 256*510L - 1: cs = 3; break; + case 256*510L ... 1024*510L - 1: cs = 4; break; + default: cs = 5; break; + } + } + volatile uint8_t *rega = READP(p->rega), *regb = READP(p->regb); + uint8_t en_bit = READP(p->en_bit); + struct gpio_digital_regs *regs = GPIO2REGS(pin); + uint8_t bit = GPIO2BIT(pin); + struct gpio_pwm g = (struct gpio_pwm) { + (void*)READP(p->ocr), flags & GP_8BIT }; + + // Setup PWM timer + uint8_t flag = irq_save(); + uint8_t old_cs = *regb & 0x07; + if (old_cs && old_cs != cs) + shutdown("PWM already programmed at different speed"); + *regb = cs; + + // Set default value and enable output + gpio_pwm_write(g, val); + *rega |= (1<mode |= bit; + irq_restore(flag); + + return g; + } + shutdown("Not a valid PWM pin"); +} + + +struct gpio_adc +gpio_adc_setup(uint8_t pin) +{ + uint8_t chan; + for (chan=0; chanpin) != pin) + continue; + + // Enable ADC + ADCSRA |= (1<= 8) + DIDR2 |= 1 << (chan & 0x07); + else +#endif + DIDR0 |= 1 << chan; + + return (struct gpio_adc){ chan }; + } + shutdown("Not a valid ADC pin"); +} + +uint32_t +gpio_adc_sample_time(void) +{ + return (13 + 1) * 128 + 200; +} + +enum { ADC_DUMMY=0xff }; +static uint8_t last_analog_read = ADC_DUMMY; + +uint8_t +gpio_adc_sample(struct gpio_adc g) +{ + if (ADCSRA & (1<> 3) & 0x01) << MUX5); +#endif + + ADMUX = ADMUX_DEFAULT | (g.chan & 0x07); + + // start the conversion + ADCSRA |= 1<