aboutsummaryrefslogtreecommitdiffstats
path: root/src/avr/hard_pwm.c
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 /src/avr/hard_pwm.c
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>
Diffstat (limited to 'src/avr/hard_pwm.c')
-rw-r--r--src/avr/hard_pwm.c154
1 files changed, 154 insertions, 0 deletions
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);
+ }
+}