aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2018-11-20 11:39:42 -0500
committerKevin O'Connor <kevin@koconnor.net>2018-11-20 12:58:33 -0500
commitbf92ffb5bff9eedb36df697397b9a6ae27bc168e (patch)
treefbbee2fdbfd88aeb047907a707b8e53deb55b784
parent2cc0313b721597238d0774a40a4bcee7e838558a (diff)
downloadkutter-bf92ffb5bff9eedb36df697397b9a6ae27bc168e.tar.gz
kutter-bf92ffb5bff9eedb36df697397b9a6ae27bc168e.tar.xz
kutter-bf92ffb5bff9eedb36df697397b9a6ae27bc168e.zip
avr: Split gpio.c into gpio.c, adc.c, hard_pwm.c, and spi.c
Split up gpio.c into multiple files in an effort to make the code a little more understandable. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--src/avr/Makefile6
-rw-r--r--src/avr/adc.c117
-rw-r--r--src/avr/gpio.c394
-rw-r--r--src/avr/hard_pwm.c154
-rw-r--r--src/avr/internal.h18
-rw-r--r--src/avr/spi.c120
6 files changed, 418 insertions, 391 deletions
diff --git a/src/avr/Makefile b/src/avr/Makefile
index c3f3ab34..5b95d792 100644
--- a/src/avr/Makefile
+++ b/src/avr/Makefile
@@ -8,7 +8,11 @@ dirs-y += src/avr src/generic
CFLAGS += -mmcu=$(CONFIG_MCU)
# Add avr source files
-src-y += avr/main.c avr/timer.c avr/gpio.c
+src-y += avr/main.c avr/timer.c
+src-$(CONFIG_HAVE_GPIO) += avr/gpio.c
+src-$(CONFIG_HAVE_GPIO_ADC) += avr/adc.c
+src-$(CONFIG_HAVE_GPIO_SPI) += avr/spi.c
+src-$(CONFIG_HAVE_GPIO_HARD_PWM) += avr/hard_pwm.c
src-$(CONFIG_AVR_WATCHDOG) += avr/watchdog.c
src-$(CONFIG_AVR_USBSERIAL) += avr/usbserial.c generic/usb_cdc.c
src-$(CONFIG_AVR_SERIAL) += avr/serial.c generic/serial_irq.c
diff --git a/src/avr/adc.c b/src/avr/adc.c
new file mode 100644
index 00000000..3087dce1
--- /dev/null
+++ b/src/avr/adc.c
@@ -0,0 +1,117 @@
+// Analog to Digital Converter support
+//
+// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "autoconf.h" // CONFIG_MACH_atmega644p
+#include "command.h" // shutdown
+#include "gpio.h" // gpio_adc_read
+#include "internal.h" // GPIO
+#include "pgm.h" // PROGMEM
+#include "sched.h" // sched_shutdown
+
+static const uint8_t adc_pins[] PROGMEM = {
+#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
+ GPIO('C', 0), GPIO('C', 1), GPIO('C', 2), GPIO('C', 3),
+ GPIO('C', 4), GPIO('C', 5), GPIO('E', 0), GPIO('E', 1),
+#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
+ GPIO('A', 0), GPIO('A', 1), GPIO('A', 2), GPIO('A', 3),
+ GPIO('A', 4), GPIO('A', 5), GPIO('A', 6), GPIO('A', 7),
+#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646
+ GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
+ GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
+#elif CONFIG_MACH_atmega32u4
+ GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
+ GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
+ GPIO('D', 4), GPIO('D', 6), GPIO('D', 7), GPIO('B', 4),
+#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
+ GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
+ GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
+ GPIO('K', 0), GPIO('K', 1), GPIO('K', 2), GPIO('K', 3),
+ GPIO('K', 4), GPIO('K', 5), GPIO('K', 6), GPIO('K', 7),
+#endif
+};
+
+enum { ADMUX_DEFAULT = 0x40 };
+enum { ADC_ENABLE = (1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADEN)|(1<<ADIF) };
+
+DECL_CONSTANT(ADC_MAX, 1023);
+
+struct gpio_adc
+gpio_adc_setup(uint8_t pin)
+{
+ // Find pin in adc_pins table
+ uint8_t chan;
+ for (chan=0; ; chan++) {
+ if (chan >= ARRAY_SIZE(adc_pins))
+ shutdown("Not a valid ADC pin");
+ if (READP(adc_pins[chan]) == pin)
+ break;
+ }
+
+ // Enable ADC
+ ADCSRA = ADC_ENABLE;
+
+ // Disable digital input for this pin
+#ifdef DIDR2
+ if (chan >= 8)
+ DIDR2 |= 1 << (chan & 0x07);
+ else
+#endif
+ DIDR0 |= 1 << chan;
+
+ return (struct gpio_adc){ chan };
+}
+
+enum { ADC_DUMMY=0xff };
+static uint8_t last_analog_read = ADC_DUMMY;
+
+// Try to sample a value. Returns zero if sample ready, otherwise
+// returns the number of clock ticks the caller should wait before
+// retrying this function.
+uint32_t
+gpio_adc_sample(struct gpio_adc g)
+{
+ if (ADCSRA & (1<<ADSC))
+ // Busy
+ goto need_delay;
+ if (last_analog_read == g.chan)
+ // Sample now ready
+ return 0;
+ if (last_analog_read != ADC_DUMMY)
+ // Sample on another channel in progress
+ goto need_delay;
+ last_analog_read = g.chan;
+
+ // Set the channel to sample
+#if defined(ADCSRB) && defined(MUX5)
+ // The MUX5 bit of ADCSRB selects whether we're reading from
+ // channels 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
+ ADCSRB = ((g.chan >> 3) & 0x01) << MUX5;
+#endif
+ ADMUX = ADMUX_DEFAULT | (g.chan & 0x07);
+
+ // Start the sample
+ ADCSRA = ADC_ENABLE | (1<<ADSC);
+
+ // Schedule next attempt after sample is likely to be complete
+need_delay:
+ return (13 + 1) * 128 + 200;
+}
+
+// Read a value; use only after gpio_adc_sample() returns zero
+uint16_t
+gpio_adc_read(struct gpio_adc g)
+{
+ last_analog_read = ADC_DUMMY;
+ return ADC;
+}
+
+// Cancel a sample that may have been started with gpio_adc_sample()
+void
+gpio_adc_cancel_sample(struct gpio_adc g)
+{
+ if (last_analog_read == g.chan)
+ last_analog_read = ADC_DUMMY;
+}
diff --git a/src/avr/gpio.c b/src/avr/gpio.c
index 15da3d79..c7ca1220 100644
--- a/src/avr/gpio.c
+++ b/src/avr/gpio.c
@@ -1,27 +1,18 @@
// GPIO functions on AVR.
//
-// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
-#include <stddef.h> // NULL
#include "autoconf.h" // CONFIG_MACH_atmega644p
#include "command.h" // shutdown
#include "gpio.h" // gpio_out_write
+#include "internal.h" // GPIO2REGS
#include "irq.h" // irq_save
#include "pgm.h" // PROGMEM
-#include "sched.h" // DECL_INIT
+#include "sched.h" // sched_shutdown
-
-/****************************************************************
- * General Purpose Input Output (GPIO) pins
- ****************************************************************/
-
-#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 = {
+volatile uint8_t * const digital_regs[] PROGMEM = {
#ifdef PINA
&PINA,
#else
@@ -36,14 +27,6 @@ static volatile uint8_t * const digital_regs[] PROGMEM = {
#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_out
gpio_out_setup(uint8_t pin, uint8_t val)
{
@@ -117,372 +100,3 @@ gpio_in_read(struct gpio_in g)
{
return !!(g.regs->in & g.bit);
}
-
-
-/****************************************************************
- * Hardware Pulse Width Modulation (PWM) pins
- ****************************************************************/
-
-struct gpio_pwm_info {
- volatile void *ocr;
- volatile uint8_t *rega, *regb;
- uint8_t en_bit, flags;
-};
-
-enum { GP_8BIT=1, GP_AFMT=2 };
-
-static const struct gpio_pwm_info pwm_regs[] PROGMEM = {
- { &OCR0A, &TCCR0A, &TCCR0B, 1<<COM0A1, GP_8BIT },
- { &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GP_8BIT },
- { &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, 0 },
- { &OCR1B, &TCCR1A, &TCCR1B, 1<<COM1B1, 0 },
-#ifdef OCR1C
- { &OCR1C, &TCCR1A, &TCCR1B, 1<<COM1C1, 0 },
-#endif
-#ifdef OCR2A
- { &OCR2A, &TCCR2A, &TCCR2B, 1<<COM2A1, GP_8BIT|GP_AFMT },
- { &OCR2B, &TCCR2A, &TCCR2B, 1<<COM2B1, GP_8BIT|GP_AFMT },
-#endif
-#ifdef OCR3A
- { &OCR3A, &TCCR3A, &TCCR3B, 1<<COM3A1, 0 },
- { &OCR3B, &TCCR3A, &TCCR3B, 1<<COM3B1, 0 },
-#endif
-#ifdef OCR3C
- { &OCR3C, &TCCR3A, &TCCR3B, 1<<COM3C1, 0 },
-#endif
-#ifdef OCR5A
- { &OCR4A, &TCCR4A, &TCCR4B, 1<<COM4A1, 0 },
- { &OCR4B, &TCCR4A, &TCCR4B, 1<<COM4B1, 0 },
- { &OCR4C, &TCCR4A, &TCCR4B, 1<<COM4C1, 0 },
- { &OCR5A, &TCCR5A, &TCCR5B, 1<<COM5A1, 0 },
- { &OCR5B, &TCCR5A, &TCCR5B, 1<<COM5B1, 0 },
- { &OCR5C, &TCCR5A, &TCCR5B, 1<<COM5C1, 0 },
-#endif
-};
-
-static const uint8_t pwm_pins[ARRAY_SIZE(pwm_regs)] PROGMEM = {
-#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
- GPIO('D', 6), GPIO('D', 5),
- GPIO('B', 1), GPIO('B', 2),
- GPIO('B', 3), GPIO('D', 3),
-#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
- GPIO('B', 3), GPIO('B', 4),
- GPIO('D', 5), GPIO('D', 4),
- GPIO('D', 7), GPIO('D', 6),
-# ifdef OCR3A
- GPIO('B', 6), GPIO('B', 7),
-# endif
-#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646
- GPIO('B', 7), GPIO('D', 0),
- GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
- GPIO('B', 4), GPIO('D', 1),
- GPIO('C', 6), GPIO('C', 5), GPIO('C', 4),
-#elif CONFIG_MACH_atmega32u4
- GPIO('B', 7), GPIO('D', 0),
- GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
- GPIO('C', 6), GPIO('C', 5), GPIO('C', 4),
-#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
- GPIO('B', 7), GPIO('G', 5),
- GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
- GPIO('B', 4), GPIO('H', 6),
- GPIO('E', 3), GPIO('E', 4), GPIO('E', 5),
- GPIO('H', 3), GPIO('H', 4), GPIO('H', 5),
- GPIO('L', 3), GPIO('L', 4), GPIO('L', 5),
-#endif
-};
-
-DECL_CONSTANT(PWM_MAX, 255);
-
-struct gpio_pwm
-gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val)
-{
- // Find pin in pwm_pins table
- uint8_t chan;
- for (chan=0; ; chan++) {
- if (chan >= ARRAY_SIZE(pwm_pins))
- shutdown("Not a valid PWM pin");
- if (READP(pwm_pins[chan]) == pin)
- break;
- }
-
- // Map cycle_time to pwm clock divisor
- const struct gpio_pwm_info *p = &pwm_regs[chan];
- uint8_t flags = READP(p->flags), cs;
- if (flags & GP_AFMT) {
- switch (cycle_time) {
- case 0 ... (1+8) * 510L / 2 - 1: cs = 1; break;
- case (1+8) * 510L / 2 ... (8+32) * 510L / 2 - 1: cs = 2; break;
- case (8+32) * 510L / 2 ... (32+64) * 510L / 2 - 1: cs = 3; break;
- case (32+64) * 510L / 2 ... (64+128) * 510L / 2 - 1: cs = 4; break;
- case (64+128) * 510L / 2 ... (128+256) * 510L / 2 - 1: cs = 5; break;
- case (128+256) * 510L / 2 ... (256+1024) * 510L / 2 - 1: cs = 6; break;
- default: cs = 7; break;
- }
- } else {
- switch (cycle_time) {
- case 0 ... (1+8) * 510L / 2 - 1: cs = 1; break;
- case (1+8) * 510L / 2 ... (8+64) * 510L / 2 - 1: cs = 2; break;
- case (8+64) * 510L / 2 ... (64+256) * 510L / 2 - 1: cs = 3; break;
- case (64+256) * 510L / 2 ... (256+1024) * 510L / 2 - 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 *gpio_regs = GPIO2REGS(pin);
- uint8_t gpio_bit = GPIO2BIT(pin);
- struct gpio_pwm g = (struct gpio_pwm) {
- (void*)READP(p->ocr), flags & GP_8BIT };
- if (rega == &TCCR1A)
- shutdown("Can not use timer1 for PWM; timer1 is used for timers");
-
- // Setup PWM timer
- irqstatus_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<<WGM00) | en_bit;
- gpio_regs->mode |= gpio_bit;
- irq_restore(flag);
-
- return g;
-}
-
-void
-gpio_pwm_write(struct gpio_pwm g, uint8_t val)
-{
- if (g.size8) {
- *(volatile uint8_t*)g.reg = val;
- } else {
- irqstatus_t flag = irq_save();
- *(volatile uint16_t*)g.reg = val;
- irq_restore(flag);
- }
-}
-
-
-/****************************************************************
- * Analog to Digital Converter (ADC) pins
- ****************************************************************/
-
-static const uint8_t adc_pins[] PROGMEM = {
-#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
- GPIO('C', 0), GPIO('C', 1), GPIO('C', 2), GPIO('C', 3),
- GPIO('C', 4), GPIO('C', 5), GPIO('E', 0), GPIO('E', 1),
-#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
- GPIO('A', 0), GPIO('A', 1), GPIO('A', 2), GPIO('A', 3),
- GPIO('A', 4), GPIO('A', 5), GPIO('A', 6), GPIO('A', 7),
-#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646
- GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
- GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
-#elif CONFIG_MACH_atmega32u4
- GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
- GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
- GPIO('D', 4), GPIO('D', 6), GPIO('D', 7), GPIO('B', 4),
-#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
- GPIO('F', 0), GPIO('F', 1), GPIO('F', 2), GPIO('F', 3),
- GPIO('F', 4), GPIO('F', 5), GPIO('F', 6), GPIO('F', 7),
- GPIO('K', 0), GPIO('K', 1), GPIO('K', 2), GPIO('K', 3),
- GPIO('K', 4), GPIO('K', 5), GPIO('K', 6), GPIO('K', 7),
-#endif
-};
-
-enum { ADMUX_DEFAULT = 0x40 };
-enum { ADC_ENABLE = (1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADEN)|(1<<ADIF) };
-
-DECL_CONSTANT(ADC_MAX, 1023);
-
-struct gpio_adc
-gpio_adc_setup(uint8_t pin)
-{
- // Find pin in adc_pins table
- uint8_t chan;
- for (chan=0; ; chan++) {
- if (chan >= ARRAY_SIZE(adc_pins))
- shutdown("Not a valid ADC pin");
- if (READP(adc_pins[chan]) == pin)
- break;
- }
-
- // Enable ADC
- ADCSRA = ADC_ENABLE;
-
- // Disable digital input for this pin
-#ifdef DIDR2
- if (chan >= 8)
- DIDR2 |= 1 << (chan & 0x07);
- else
-#endif
- DIDR0 |= 1 << chan;
-
- return (struct gpio_adc){ chan };
-}
-
-enum { ADC_DUMMY=0xff };
-static uint8_t last_analog_read = ADC_DUMMY;
-
-// Try to sample a value. Returns zero if sample ready, otherwise
-// returns the number of clock ticks the caller should wait before
-// retrying this function.
-uint32_t
-gpio_adc_sample(struct gpio_adc g)
-{
- if (ADCSRA & (1<<ADSC))
- // Busy
- goto need_delay;
- if (last_analog_read == g.chan)
- // Sample now ready
- return 0;
- if (last_analog_read != ADC_DUMMY)
- // Sample on another channel in progress
- goto need_delay;
- last_analog_read = g.chan;
-
- // Set the channel to sample
-#if defined(ADCSRB) && defined(MUX5)
- // The MUX5 bit of ADCSRB selects whether we're reading from
- // channels 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
- ADCSRB = ((g.chan >> 3) & 0x01) << MUX5;
-#endif
- ADMUX = ADMUX_DEFAULT | (g.chan & 0x07);
-
- // Start the sample
- ADCSRA = ADC_ENABLE | (1<<ADSC);
-
- // Schedule next attempt after sample is likely to be complete
-need_delay:
- return (13 + 1) * 128 + 200;
-}
-
-// Read a value; use only after gpio_adc_sample() returns zero
-uint16_t
-gpio_adc_read(struct gpio_adc g)
-{
- last_analog_read = ADC_DUMMY;
- return ADC;
-}
-
-// Cancel a sample that may have been started with gpio_adc_sample()
-void
-gpio_adc_cancel_sample(struct gpio_adc g)
-{
- if (last_analog_read == g.chan)
- last_analog_read = ADC_DUMMY;
-}
-
-
-/****************************************************************
- * Serial Peripheral Interface (SPI) hardware
- ****************************************************************/
-
-#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
-static const uint8_t SS = GPIO('B', 2), SCK = GPIO('B', 5);
-static const uint8_t MOSI = GPIO('B', 3), MISO = GPIO('B', 4);
-#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
-static const uint8_t SS = GPIO('B', 4), SCK = GPIO('B', 7);
-static const uint8_t MOSI = GPIO('B', 5), MISO = GPIO('B', 6);
-#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646 || CONFIG_MACH_atmega32u4 || CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
-static const uint8_t SS = GPIO('B', 0), SCK = GPIO('B', 1);
-static const uint8_t MOSI = GPIO('B', 2), MISO = GPIO('B', 3);
-#endif
-
-static void
-spi_init(void)
-{
- if (!(GPIO2REGS(SS)->mode & GPIO2BIT(SS)))
- // The SS pin must be an output pin (but is otherwise unused)
- gpio_out_setup(SS, 0);
- gpio_out_setup(SCK, 0);
- gpio_out_setup(MOSI, 0);
- gpio_in_setup(MISO, 0);
-
- SPCR = (1<<MSTR) | (1<<SPE);
- SPSR = 0;
-}
-
-struct spi_config
-spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
-{
- if (bus || mode > 3)
- shutdown("Invalid spi_setup parameters");
-
- // Make sure the SPI interface is enabled
- spi_init();
-
- // Setup rate
- struct spi_config config = {0, 0};
- if (rate >= (CONFIG_CLOCK_FREQ / 2)) {
- config.spsr = (1<<SPI2X);
- } else if (rate >= (CONFIG_CLOCK_FREQ / 4)) {
- config.spcr = 0;
- } else if (rate >= (CONFIG_CLOCK_FREQ / 8)) {
- config.spcr = 1;
- config.spsr = (1<<SPI2X);
- } else if (rate >= (CONFIG_CLOCK_FREQ / 16)) {
- config.spcr = 1;
- } else if (rate >= (CONFIG_CLOCK_FREQ / 32)) {
- config.spcr = 2;
- config.spsr = (1<<SPI2X);
- } else if (rate >= (CONFIG_CLOCK_FREQ / 64)) {
- config.spcr = 2;
- } else {
- config.spcr = 3;
- }
-
- // Setup mode
- config.spcr |= (1<<SPE) | (1<<MSTR);
- switch(mode) {
- case 0: {
- // MODE 0 - CPOL=0, CPHA=0
- break;
- }
- case 1: {
- // MODE 1 - CPOL=0, CPHA=1
- config.spcr |= (1<<CPHA);
- break;
- }
- case 2: {
- // MODE 2 - CPOL=1, CPHA=0
- config.spcr |= (1<<CPOL);
- break;
- }
- case 3: {
- // MODE 3 - CPOL=1, CPHA=1
- config.spcr |= (1<<CPOL) | (1<<CPHA);
- break;
- }
- }
-
- return config;
-}
-
-void
-spi_prepare(struct spi_config config)
-{
- SPCR = config.spcr;
- SPSR = config.spsr;
-}
-
-void
-spi_transfer(struct spi_config config, uint8_t receive_data
- , uint8_t len, uint8_t *data)
-{
- if (receive_data) {
- while (len--) {
- SPDR = *data;
- while (!(SPSR & (1<<SPIF)))
- ;
- *data++ = SPDR;
- }
- } else {
- while (len--) {
- SPDR = *data++;
- while (!(SPSR & (1<<SPIF)))
- ;
- }
- }
-}
diff --git a/src/avr/hard_pwm.c b/src/avr/hard_pwm.c
new file mode 100644
index 00000000..3348a664
--- /dev/null
+++ b/src/avr/hard_pwm.c
@@ -0,0 +1,154 @@
+// Hardware PWM pin support
+//
+// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "autoconf.h" // CONFIG_MACH_atmega644p
+#include "command.h" // shutdown
+#include "gpio.h" // gpio_pwm_write
+#include "internal.h" // GPIO2REGS
+#include "irq.h" // irq_save
+#include "pgm.h" // PROGMEM
+#include "sched.h" // sched_shutdown
+
+struct gpio_pwm_info {
+ volatile void *ocr;
+ volatile uint8_t *rega, *regb;
+ uint8_t en_bit, flags;
+};
+
+enum { GP_8BIT=1, GP_AFMT=2 };
+
+static const struct gpio_pwm_info pwm_regs[] PROGMEM = {
+ { &OCR0A, &TCCR0A, &TCCR0B, 1<<COM0A1, GP_8BIT },
+ { &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GP_8BIT },
+ { &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, 0 },
+ { &OCR1B, &TCCR1A, &TCCR1B, 1<<COM1B1, 0 },
+#ifdef OCR1C
+ { &OCR1C, &TCCR1A, &TCCR1B, 1<<COM1C1, 0 },
+#endif
+#ifdef OCR2A
+ { &OCR2A, &TCCR2A, &TCCR2B, 1<<COM2A1, GP_8BIT|GP_AFMT },
+ { &OCR2B, &TCCR2A, &TCCR2B, 1<<COM2B1, GP_8BIT|GP_AFMT },
+#endif
+#ifdef OCR3A
+ { &OCR3A, &TCCR3A, &TCCR3B, 1<<COM3A1, 0 },
+ { &OCR3B, &TCCR3A, &TCCR3B, 1<<COM3B1, 0 },
+#endif
+#ifdef OCR3C
+ { &OCR3C, &TCCR3A, &TCCR3B, 1<<COM3C1, 0 },
+#endif
+#ifdef OCR5A
+ { &OCR4A, &TCCR4A, &TCCR4B, 1<<COM4A1, 0 },
+ { &OCR4B, &TCCR4A, &TCCR4B, 1<<COM4B1, 0 },
+ { &OCR4C, &TCCR4A, &TCCR4B, 1<<COM4C1, 0 },
+ { &OCR5A, &TCCR5A, &TCCR5B, 1<<COM5A1, 0 },
+ { &OCR5B, &TCCR5A, &TCCR5B, 1<<COM5B1, 0 },
+ { &OCR5C, &TCCR5A, &TCCR5B, 1<<COM5C1, 0 },
+#endif
+};
+
+static const uint8_t pwm_pins[ARRAY_SIZE(pwm_regs)] PROGMEM = {
+#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
+ GPIO('D', 6), GPIO('D', 5),
+ GPIO('B', 1), GPIO('B', 2),
+ GPIO('B', 3), GPIO('D', 3),
+#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
+ GPIO('B', 3), GPIO('B', 4),
+ GPIO('D', 5), GPIO('D', 4),
+ GPIO('D', 7), GPIO('D', 6),
+# ifdef OCR3A
+ GPIO('B', 6), GPIO('B', 7),
+# endif
+#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646
+ GPIO('B', 7), GPIO('D', 0),
+ GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
+ GPIO('B', 4), GPIO('D', 1),
+ GPIO('C', 6), GPIO('C', 5), GPIO('C', 4),
+#elif CONFIG_MACH_atmega32u4
+ GPIO('B', 7), GPIO('D', 0),
+ GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
+ GPIO('C', 6), GPIO('C', 5), GPIO('C', 4),
+#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
+ GPIO('B', 7), GPIO('G', 5),
+ GPIO('B', 5), GPIO('B', 6), GPIO('B', 7),
+ GPIO('B', 4), GPIO('H', 6),
+ GPIO('E', 3), GPIO('E', 4), GPIO('E', 5),
+ GPIO('H', 3), GPIO('H', 4), GPIO('H', 5),
+ GPIO('L', 3), GPIO('L', 4), GPIO('L', 5),
+#endif
+};
+
+DECL_CONSTANT(PWM_MAX, 255);
+
+struct gpio_pwm
+gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val)
+{
+ // Find pin in pwm_pins table
+ uint8_t chan;
+ for (chan=0; ; chan++) {
+ if (chan >= ARRAY_SIZE(pwm_pins))
+ shutdown("Not a valid PWM pin");
+ if (READP(pwm_pins[chan]) == pin)
+ break;
+ }
+
+ // Map cycle_time to pwm clock divisor
+ const struct gpio_pwm_info *p = &pwm_regs[chan];
+ uint8_t flags = READP(p->flags), cs;
+ if (flags & GP_AFMT) {
+ switch (cycle_time) {
+ case 0 ... (1+8) * 510L / 2 - 1: cs = 1; break;
+ case (1+8) * 510L / 2 ... (8+32) * 510L / 2 - 1: cs = 2; break;
+ case (8+32) * 510L / 2 ... (32+64) * 510L / 2 - 1: cs = 3; break;
+ case (32+64) * 510L / 2 ... (64+128) * 510L / 2 - 1: cs = 4; break;
+ case (64+128) * 510L / 2 ... (128+256) * 510L / 2 - 1: cs = 5; break;
+ case (128+256) * 510L / 2 ... (256+1024) * 510L / 2 - 1: cs = 6; break;
+ default: cs = 7; break;
+ }
+ } else {
+ switch (cycle_time) {
+ case 0 ... (1+8) * 510L / 2 - 1: cs = 1; break;
+ case (1+8) * 510L / 2 ... (8+64) * 510L / 2 - 1: cs = 2; break;
+ case (8+64) * 510L / 2 ... (64+256) * 510L / 2 - 1: cs = 3; break;
+ case (64+256) * 510L / 2 ... (256+1024) * 510L / 2 - 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 *gpio_regs = GPIO2REGS(pin);
+ uint8_t gpio_bit = GPIO2BIT(pin);
+ struct gpio_pwm g = (struct gpio_pwm) {
+ (void*)READP(p->ocr), flags & GP_8BIT };
+ if (rega == &TCCR1A)
+ shutdown("Can not use timer1 for PWM; timer1 is used for timers");
+
+ // Setup PWM timer
+ irqstatus_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<<WGM00) | en_bit;
+ gpio_regs->mode |= gpio_bit;
+ irq_restore(flag);
+
+ return g;
+}
+
+void
+gpio_pwm_write(struct gpio_pwm g, uint8_t val)
+{
+ if (g.size8) {
+ *(volatile uint8_t*)g.reg = val;
+ } else {
+ irqstatus_t flag = irq_save();
+ *(volatile uint16_t*)g.reg = val;
+ irq_restore(flag);
+ }
+}
diff --git a/src/avr/internal.h b/src/avr/internal.h
new file mode 100644
index 00000000..ab800864
--- /dev/null
+++ b/src/avr/internal.h
@@ -0,0 +1,18 @@
+#ifndef __AVR_INTERNAL_H
+#define __AVR_INTERNAL_H
+// Local definitions for avr code
+
+#define GPIO(PORT, NUM) (((PORT)-'A') * 8 + (NUM))
+#define GPIO2PORT(PIN) ((PIN) / 8)
+#define GPIO2BIT(PIN) (1<<((PIN) % 8))
+
+struct gpio_digital_regs {
+ // gcc (pre v6) does better optimization when uint8_t are bitfields
+ volatile uint8_t in : 8, mode : 8, out : 8;
+};
+extern volatile uint8_t * const digital_regs[];
+
+#define GPIO2REGS(pin) \
+ ((struct gpio_digital_regs*)READP(digital_regs[GPIO2PORT(pin)]))
+
+#endif // internal.h
diff --git a/src/avr/spi.c b/src/avr/spi.c
new file mode 100644
index 00000000..bf0738c1
--- /dev/null
+++ b/src/avr/spi.c
@@ -0,0 +1,120 @@
+// Serial Peripheral Interface (SPI) support
+//
+// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "autoconf.h" // CONFIG_MACH_atmega644p
+#include "command.h" // shutdown
+#include "gpio.h" // spi_setup
+#include "internal.h" // GPIO
+#include "pgm.h" // READP
+#include "sched.h" // sched_shutdown
+
+#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
+static const uint8_t SS = GPIO('B', 2), SCK = GPIO('B', 5);
+static const uint8_t MOSI = GPIO('B', 3), MISO = GPIO('B', 4);
+#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
+static const uint8_t SS = GPIO('B', 4), SCK = GPIO('B', 7);
+static const uint8_t MOSI = GPIO('B', 5), MISO = GPIO('B', 6);
+#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646 || CONFIG_MACH_atmega32u4 || CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
+static const uint8_t SS = GPIO('B', 0), SCK = GPIO('B', 1);
+static const uint8_t MOSI = GPIO('B', 2), MISO = GPIO('B', 3);
+#endif
+
+static void
+spi_init(void)
+{
+ if (!(GPIO2REGS(SS)->mode & GPIO2BIT(SS)))
+ // The SS pin must be an output pin (but is otherwise unused)
+ gpio_out_setup(SS, 0);
+ gpio_out_setup(SCK, 0);
+ gpio_out_setup(MOSI, 0);
+ gpio_in_setup(MISO, 0);
+
+ SPCR = (1<<MSTR) | (1<<SPE);
+ SPSR = 0;
+}
+
+struct spi_config
+spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
+{
+ if (bus || mode > 3)
+ shutdown("Invalid spi_setup parameters");
+
+ // Make sure the SPI interface is enabled
+ spi_init();
+
+ // Setup rate
+ struct spi_config config = {0, 0};
+ if (rate >= (CONFIG_CLOCK_FREQ / 2)) {
+ config.spsr = (1<<SPI2X);
+ } else if (rate >= (CONFIG_CLOCK_FREQ / 4)) {
+ config.spcr = 0;
+ } else if (rate >= (CONFIG_CLOCK_FREQ / 8)) {
+ config.spcr = 1;
+ config.spsr = (1<<SPI2X);
+ } else if (rate >= (CONFIG_CLOCK_FREQ / 16)) {
+ config.spcr = 1;
+ } else if (rate >= (CONFIG_CLOCK_FREQ / 32)) {
+ config.spcr = 2;
+ config.spsr = (1<<SPI2X);
+ } else if (rate >= (CONFIG_CLOCK_FREQ / 64)) {
+ config.spcr = 2;
+ } else {
+ config.spcr = 3;
+ }
+
+ // Setup mode
+ config.spcr |= (1<<SPE) | (1<<MSTR);
+ switch(mode) {
+ case 0: {
+ // MODE 0 - CPOL=0, CPHA=0
+ break;
+ }
+ case 1: {
+ // MODE 1 - CPOL=0, CPHA=1
+ config.spcr |= (1<<CPHA);
+ break;
+ }
+ case 2: {
+ // MODE 2 - CPOL=1, CPHA=0
+ config.spcr |= (1<<CPOL);
+ break;
+ }
+ case 3: {
+ // MODE 3 - CPOL=1, CPHA=1
+ config.spcr |= (1<<CPOL) | (1<<CPHA);
+ break;
+ }
+ }
+
+ return config;
+}
+
+void
+spi_prepare(struct spi_config config)
+{
+ SPCR = config.spcr;
+ SPSR = config.spsr;
+}
+
+void
+spi_transfer(struct spi_config config, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ if (receive_data) {
+ while (len--) {
+ SPDR = *data;
+ while (!(SPSR & (1<<SPIF)))
+ ;
+ *data++ = SPDR;
+ }
+ } else {
+ while (len--) {
+ SPDR = *data++;
+ while (!(SPSR & (1<<SPIF)))
+ ;
+ }
+ }
+}