mirror of
https://github.com/Klipper3d/klipper.git
synced 2025-07-25 07:34:05 -06:00
Initial commit of source code.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
37a91e9c10
commit
f582a36e4d
71 changed files with 9950 additions and 0 deletions
64
src/avr/Kconfig
Normal file
64
src/avr/Kconfig
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Kconfig settings for AVR processors
|
||||
|
||||
if MACH_AVR
|
||||
|
||||
config BOARD_DIRECTORY
|
||||
string
|
||||
default "avr"
|
||||
|
||||
choice
|
||||
prompt "Processor model"
|
||||
config MACH_atmega168
|
||||
bool "atmega168"
|
||||
config MACH_atmega644p
|
||||
bool "atmega644p"
|
||||
config MACH_atmega1280
|
||||
bool "atmega1280"
|
||||
config MACH_atmega2560
|
||||
bool "atmega2560"
|
||||
endchoice
|
||||
|
||||
config MCU
|
||||
string
|
||||
default "atmega168" if MACH_atmega168
|
||||
default "atmega644p" if MACH_atmega644p
|
||||
default "atmega1280" if MACH_atmega1280
|
||||
default "atmega2560" if MACH_atmega2560
|
||||
|
||||
choice
|
||||
prompt "Processor speed"
|
||||
config AVR_FREQ_8000000
|
||||
bool "8Mhz"
|
||||
config AVR_FREQ_16000000
|
||||
bool "16Mhz"
|
||||
config AVR_FREQ_20000000
|
||||
bool "20Mhz"
|
||||
endchoice
|
||||
|
||||
config CLOCK_FREQ
|
||||
int
|
||||
default 8000000 if AVR_FREQ_8000000
|
||||
default 16000000 if AVR_FREQ_16000000
|
||||
default 20000000 if AVR_FREQ_20000000
|
||||
|
||||
config AVR_STACK_SIZE
|
||||
int
|
||||
default 256 if MACH_atmega2560
|
||||
default 128
|
||||
|
||||
config AVR_WATCHDOG
|
||||
bool "Support for automated reset on watchdog timeout"
|
||||
default y
|
||||
config AVR_SERIAL
|
||||
bool
|
||||
default y
|
||||
config SERIAL_BAUD
|
||||
depends on AVR_SERIAL
|
||||
int "Baud rate for serial port"
|
||||
default 250000
|
||||
config SERIAL_BAUD_U2X
|
||||
depends on AVR_SERIAL
|
||||
bool "Use AVR Baud 2X mode"
|
||||
default y
|
||||
|
||||
endif
|
19
src/avr/Makefile
Normal file
19
src/avr/Makefile
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Additional avr build rules
|
||||
|
||||
# Use the avr toolchain
|
||||
CROSS_PREFIX=avr-
|
||||
|
||||
CFLAGS-y += -mmcu=$(CONFIG_MCU) -DF_CPU=$(CONFIG_CLOCK_FREQ)
|
||||
LDFLAGS-y += -Wl,--relax
|
||||
|
||||
# Add avr source files
|
||||
src-y += avr/main.c avr/timer.c avr/gpio.c avr/alloc.c
|
||||
src-$(CONFIG_AVR_WATCHDOG) += avr/watchdog.c
|
||||
src-$(CONFIG_AVR_SERIAL) += avr/serial.c
|
||||
|
||||
# Build the additional hex output file
|
||||
target-y += $(OUT)klipper.elf.hex
|
||||
|
||||
$(OUT)klipper.elf.hex: $(OUT)klipper.elf
|
||||
@echo " Creating hex file $@"
|
||||
$(Q)$(OBJCOPY) -j .text -j .data -O ihex $< $@
|
25
src/avr/alloc.c
Normal file
25
src/avr/alloc.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
// AVR allocation checking code.
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <avr/io.h> // AVR_STACK_POINTER_REG
|
||||
#include <stdlib.h> // __malloc_heap_end
|
||||
#include "autoconf.h" // CONFIG_AVR_STACK_SIZE
|
||||
#include "compiler.h" // ALIGN
|
||||
#include "misc.h" // alloc_maxsize
|
||||
|
||||
size_t
|
||||
alloc_maxsize(size_t reqsize)
|
||||
{
|
||||
uint16_t memend = ALIGN(AVR_STACK_POINTER_REG, 256);
|
||||
__malloc_heap_end = (void*)memend - CONFIG_AVR_STACK_SIZE;
|
||||
extern char *__brkval;
|
||||
int16_t maxsize = __malloc_heap_end - __brkval - 2;
|
||||
if (maxsize < 0)
|
||||
return 0;
|
||||
if (reqsize < maxsize)
|
||||
return reqsize;
|
||||
return maxsize;
|
||||
}
|
337
src/avr/gpio.c
Normal file
337
src/avr/gpio.c
Normal file
|
@ -0,0 +1,337 @@
|
|||
// GPIO functions on AVR.
|
||||
//
|
||||
// Copyright (C) 2016 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 "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<<COM0A1, GPIO('D', 6), GP_8BIT },
|
||||
{ &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GPIO('D', 5), GP_8BIT },
|
||||
// { &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, GPIO('B', 1), 0 },
|
||||
// { &OCR1B, &TCCR1A, &TCCR1B, 1<<COM1B1, GPIO('B', 2), 0 },
|
||||
{ &OCR2A, &TCCR2A, &TCCR2B, 1<<COM2A1, GPIO('B', 3), GP_8BIT|GP_AFMT },
|
||||
{ &OCR2B, &TCCR2A, &TCCR2B, 1<<COM2B1, GPIO('D', 3), GP_8BIT|GP_AFMT },
|
||||
#elif CONFIG_MACH_atmega644p
|
||||
{ &OCR0A, &TCCR0A, &TCCR0B, 1<<COM0A1, GPIO('B', 3), GP_8BIT },
|
||||
{ &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GPIO('B', 4), GP_8BIT },
|
||||
// { &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, GPIO('D', 5), 0 },
|
||||
// { &OCR1B, &TCCR1A, &TCCR1B, 1<<COM1B1, GPIO('D', 4), 0 },
|
||||
{ &OCR2A, &TCCR2A, &TCCR2B, 1<<COM2A1, GPIO('D', 7), GP_8BIT|GP_AFMT },
|
||||
{ &OCR2B, &TCCR2A, &TCCR2B, 1<<COM2B1, GPIO('D', 6), GP_8BIT|GP_AFMT },
|
||||
#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
|
||||
{ &OCR0A, &TCCR0A, &TCCR0B, 1<<COM0A1, GPIO('B', 7), GP_8BIT },
|
||||
{ &OCR0B, &TCCR0A, &TCCR0B, 1<<COM0B1, GPIO('G', 5), GP_8BIT },
|
||||
// { &OCR1A, &TCCR1A, &TCCR1B, 1<<COM1A1, GPIO('B', 5), 0 },
|
||||
// { &OCR1B, &TCCR1A, &TCCR1B, 1<<COM1B1, GPIO('B', 6), 0 },
|
||||
// { &OCR1C, &TCCR1A, &TCCR1B, 1<<COM1C1, GPIO('B', 7), 0 },
|
||||
{ &OCR2A, &TCCR2A, &TCCR2B, 1<<COM2A1, GPIO('B', 4), GP_8BIT|GP_AFMT },
|
||||
{ &OCR2B, &TCCR2A, &TCCR2B, 1<<COM2B1, GPIO('H', 6), GP_8BIT|GP_AFMT },
|
||||
{ &OCR3A, &TCCR3A, &TCCR3B, 1<<COM3A1, GPIO('E', 3), 0 },
|
||||
{ &OCR3B, &TCCR3A, &TCCR3B, 1<<COM3B1, GPIO('E', 4), 0 },
|
||||
{ &OCR3C, &TCCR3A, &TCCR3B, 1<<COM3C1, GPIO('E', 5), 0 },
|
||||
{ &OCR4A, &TCCR4A, &TCCR4B, 1<<COM4A1, GPIO('H', 3), 0 },
|
||||
{ &OCR4B, &TCCR4A, &TCCR4B, 1<<COM4B1, GPIO('H', 4), 0 },
|
||||
{ &OCR4C, &TCCR4A, &TCCR4B, 1<<COM4C1, GPIO('H', 5), 0 },
|
||||
{ &OCR5A, &TCCR5A, &TCCR5B, 1<<COM5A1, GPIO('L', 3), 0 },
|
||||
{ &OCR5B, &TCCR5A, &TCCR5B, 1<<COM5B1, GPIO('L', 4), 0 },
|
||||
{ &OCR5C, &TCCR5A, &TCCR5B, 1<<COM5C1, GPIO('L', 5), 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
struct gpio_adc_info {
|
||||
uint8_t pin;
|
||||
};
|
||||
|
||||
static const struct gpio_adc_info adc_pins[] PROGMEM = {
|
||||
#if CONFIG_MACH_atmega168
|
||||
{ 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
|
||||
{ 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_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
|
||||
};
|
||||
|
||||
#if CONFIG_MACH_atmega168
|
||||
static const uint8_t SS = GPIO('B', 2), SCK = GPIO('B', 5), MOSI = GPIO('B', 3);
|
||||
#elif CONFIG_MACH_atmega644p
|
||||
static const uint8_t SS = GPIO('B', 4), SCK = GPIO('B', 7), MOSI = GPIO('B', 5);
|
||||
#elif CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
|
||||
static const uint8_t SS = GPIO('B', 0), SCK = GPIO('B', 1), MOSI = GPIO('B', 2);
|
||||
#endif
|
||||
|
||||
static const uint8_t ADMUX_DEFAULT = 0x40;
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* gpio functions
|
||||
****************************************************************/
|
||||
|
||||
struct gpio_out
|
||||
gpio_out_setup(uint8_t pin, uint8_t val)
|
||||
{
|
||||
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 = 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; chan<ARRAY_SIZE(pwm_regs); chan++) {
|
||||
const struct gpio_pwm_info *p = &pwm_regs[chan];
|
||||
if (READP(p->pin) != 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<<WGM00) | en_bit;
|
||||
regs->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; chan<ARRAY_SIZE(adc_pins); chan++) {
|
||||
const struct gpio_adc_info *a = &adc_pins[chan];
|
||||
if (READP(a->pin) != pin)
|
||||
continue;
|
||||
|
||||
// Enable ADC
|
||||
ADCSRA |= (1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADEN);
|
||||
|
||||
// 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 };
|
||||
}
|
||||
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<<ADSC))
|
||||
// Busy
|
||||
return 1;
|
||||
if (last_analog_read == g.chan)
|
||||
// Sample now ready
|
||||
return 0;
|
||||
if (last_analog_read != ADC_DUMMY)
|
||||
// Sample on another channel in progress
|
||||
return 1;
|
||||
last_analog_read = g.chan;
|
||||
|
||||
#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 = (ADCSRB & ~(1 << MUX5)) | (((g.chan >> 3) & 0x01) << MUX5);
|
||||
#endif
|
||||
|
||||
ADMUX = ADMUX_DEFAULT | (g.chan & 0x07);
|
||||
|
||||
// start the conversion
|
||||
ADCSRA |= 1<<ADSC;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
gpio_adc_clear_sample(struct gpio_adc g)
|
||||
{
|
||||
if (last_analog_read == g.chan)
|
||||
last_analog_read = ADC_DUMMY;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
gpio_adc_read(struct gpio_adc g)
|
||||
{
|
||||
last_analog_read = ADC_DUMMY;
|
||||
return ADC;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
spi_config(void)
|
||||
{
|
||||
gpio_out_setup(SS, 1);
|
||||
gpio_out_setup(SCK, 0);
|
||||
gpio_out_setup(MOSI, 0);
|
||||
SPCR = (1<<MSTR) | (1<<SPE);
|
||||
}
|
||||
|
||||
void
|
||||
spi_transfer(char *data, uint8_t len)
|
||||
{
|
||||
while (len--) {
|
||||
SPDR = *data;
|
||||
while (!(SPSR & (1<<SPIF)))
|
||||
;
|
||||
*data++ = SPDR;
|
||||
}
|
||||
}
|
42
src/avr/gpio.h
Normal file
42
src/avr/gpio.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef __AVR_GPIO_H
|
||||
#define __AVR_GPIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "compiler.h" // __always_inline
|
||||
|
||||
struct gpio_out {
|
||||
struct gpio_digital_regs *regs;
|
||||
// gcc (pre v6) does better optimization when uint8_t are bitfields
|
||||
uint8_t bit : 8;
|
||||
};
|
||||
struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val);
|
||||
void gpio_out_toggle(struct gpio_out g);
|
||||
void gpio_out_write(struct gpio_out g, uint8_t val);
|
||||
|
||||
struct gpio_in {
|
||||
struct gpio_digital_regs *regs;
|
||||
uint8_t bit;
|
||||
};
|
||||
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
|
||||
uint8_t gpio_in_read(struct gpio_in g);
|
||||
|
||||
struct gpio_pwm {
|
||||
void *reg;
|
||||
uint8_t size8;
|
||||
};
|
||||
struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val);
|
||||
void gpio_pwm_write(struct gpio_pwm g, uint8_t val);
|
||||
|
||||
struct gpio_adc {
|
||||
uint8_t chan;
|
||||
};
|
||||
struct gpio_adc gpio_adc_setup(uint8_t pin);
|
||||
uint32_t gpio_adc_sample_time(void);
|
||||
uint8_t gpio_adc_sample(struct gpio_adc g);
|
||||
void gpio_adc_clear_sample(struct gpio_adc g);
|
||||
uint16_t gpio_adc_read(struct gpio_adc g);
|
||||
|
||||
void spi_config(void);
|
||||
void spi_transfer(char *data, uint8_t len);
|
||||
|
||||
#endif // gpio.h
|
29
src/avr/irq.h
Normal file
29
src/avr/irq.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef __AVR_IRQ_H
|
||||
#define __AVR_IRQ_H
|
||||
// Definitions for irq enable/disable on AVR
|
||||
|
||||
#include <avr/interrupt.h> // cli
|
||||
#include "compiler.h" // barrier
|
||||
|
||||
static inline void irq_disable(void) {
|
||||
cli();
|
||||
barrier();
|
||||
}
|
||||
|
||||
static inline void irq_enable(void) {
|
||||
barrier();
|
||||
sei();
|
||||
}
|
||||
|
||||
static inline uint8_t irq_save(void) {
|
||||
uint8_t flag = SREG;
|
||||
irq_disable();
|
||||
return flag;
|
||||
}
|
||||
|
||||
static inline void irq_restore(uint8_t flag) {
|
||||
barrier();
|
||||
SREG = flag;
|
||||
}
|
||||
|
||||
#endif // irq.h
|
17
src/avr/main.c
Normal file
17
src/avr/main.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Main starting point for AVR boards.
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include "irq.h" // irq_enable
|
||||
#include "sched.h" // sched_main
|
||||
|
||||
// Main entry point for avr code.
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
irq_enable();
|
||||
sched_main();
|
||||
return 0;
|
||||
}
|
25
src/avr/misc.h
Normal file
25
src/avr/misc.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef __AVR_MISC_H
|
||||
#define __AVR_MISC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <util/crc16.h>
|
||||
|
||||
// alloc.c
|
||||
size_t alloc_maxsize(size_t reqsize);
|
||||
|
||||
// console.c
|
||||
char *console_get_input(uint8_t *plen);
|
||||
void console_pop_input(uint8_t len);
|
||||
char *console_get_output(uint8_t len);
|
||||
void console_push_output(uint8_t len);
|
||||
|
||||
// Optimized crc16_ccitt for the avr processor
|
||||
#define HAVE_OPTIMIZED_CRC 1
|
||||
static inline uint16_t _crc16_ccitt(char *buf, uint8_t len) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
while (len--)
|
||||
crc = _crc_ccitt_update(crc, *buf++);
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif // misc.h
|
25
src/avr/pgm.h
Normal file
25
src/avr/pgm.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef __AVR_PGM_H
|
||||
#define __AVR_PGM_H
|
||||
// This header provides the avr/pgmspace.h definitions for "PROGMEM"
|
||||
// on AVR platforms.
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#define READP(VAR) ({ \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-to-pointer-cast\""); \
|
||||
typeof(VAR) __val = \
|
||||
__builtin_choose_expr(sizeof(VAR) == 1, \
|
||||
(typeof(VAR))pgm_read_byte(&(VAR)), \
|
||||
__builtin_choose_expr(sizeof(VAR) == 2, \
|
||||
(typeof(VAR))pgm_read_word(&(VAR)), \
|
||||
__builtin_choose_expr(sizeof(VAR) == 4, \
|
||||
(typeof(VAR))pgm_read_dword(&(VAR)), \
|
||||
__force_link_error__unknown_type))); \
|
||||
_Pragma("GCC diagnostic pop"); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
extern void __force_link_error__unknown_type(void);
|
||||
|
||||
#endif // pgm.h
|
137
src/avr/serial.c
Normal file
137
src/avr/serial.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
// AVR serial port code.
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <avr/interrupt.h> // USART0_RX_vect
|
||||
#include <string.h> // memmove
|
||||
#include "autoconf.h" // CONFIG_SERIAL_BAUD
|
||||
#include "sched.h" // DECL_INIT
|
||||
#include "irq.h" // irq_save
|
||||
#include "misc.h" // console_get_input
|
||||
|
||||
#define SERIAL_BUFFER_SIZE 96
|
||||
static char receive_buf[SERIAL_BUFFER_SIZE];
|
||||
static uint8_t receive_pos;
|
||||
static char transmit_buf[SERIAL_BUFFER_SIZE];
|
||||
static uint8_t transmit_pos, transmit_max;
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Serial hardware
|
||||
****************************************************************/
|
||||
|
||||
static void
|
||||
serial_init(void)
|
||||
{
|
||||
if (CONFIG_SERIAL_BAUD_U2X) {
|
||||
UCSR0A = 1<<U2X0;
|
||||
UBRR0 = DIV_ROUND_CLOSEST(F_CPU, 8UL * CONFIG_SERIAL_BAUD) - 1UL;
|
||||
} else {
|
||||
UCSR0A = 0;
|
||||
UBRR0 = DIV_ROUND_CLOSEST(F_CPU, 16UL * CONFIG_SERIAL_BAUD) - 1UL;
|
||||
}
|
||||
|
||||
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
|
||||
UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0) | (1<<UDRIE0);
|
||||
}
|
||||
DECL_INIT(serial_init);
|
||||
|
||||
#ifdef USART_RX_vect
|
||||
#define USART0_RX_vect USART_RX_vect
|
||||
#define USART0_UDRE_vect USART_UDRE_vect
|
||||
#endif
|
||||
|
||||
// Rx interrupt - data available to be read.
|
||||
ISR(USART0_RX_vect)
|
||||
{
|
||||
uint8_t data = UDR0;
|
||||
if (receive_pos >= sizeof(receive_buf))
|
||||
// Serial overflow - ignore it as crc error will force retransmit
|
||||
return;
|
||||
receive_buf[receive_pos++] = data;
|
||||
}
|
||||
|
||||
// Tx interrupt - data can be written to serial.
|
||||
ISR(USART0_UDRE_vect)
|
||||
{
|
||||
if (transmit_pos >= transmit_max)
|
||||
UCSR0B &= ~(1<<UDRIE0);
|
||||
else
|
||||
UDR0 = transmit_buf[transmit_pos++];
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Console access functions
|
||||
****************************************************************/
|
||||
|
||||
// Return a buffer (and length) containing any incoming messages
|
||||
char *
|
||||
console_get_input(uint8_t *plen)
|
||||
{
|
||||
*plen = readb(&receive_pos);
|
||||
return receive_buf;
|
||||
}
|
||||
|
||||
// Remove from the receive buffer the given number of bytes
|
||||
void
|
||||
console_pop_input(uint8_t len)
|
||||
{
|
||||
uint8_t copied = 0;
|
||||
for (;;) {
|
||||
uint8_t rpos = readb(&receive_pos);
|
||||
uint8_t needcopy = rpos - len;
|
||||
if (needcopy) {
|
||||
memmove(&receive_buf[copied], &receive_buf[copied + len]
|
||||
, needcopy - copied);
|
||||
copied = needcopy;
|
||||
}
|
||||
uint8_t flag = irq_save();
|
||||
if (rpos != readb(&receive_pos)) {
|
||||
// Raced with irq handler - retry
|
||||
irq_restore(flag);
|
||||
continue;
|
||||
}
|
||||
receive_pos = needcopy;
|
||||
irq_restore(flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return an output buffer that the caller may fill with transmit messages
|
||||
char *
|
||||
console_get_output(uint8_t len)
|
||||
{
|
||||
uint8_t tpos = readb(&transmit_pos), tmax = readb(&transmit_max);
|
||||
if (tpos == tmax) {
|
||||
tpos = tmax = 0;
|
||||
writeb(&transmit_max, 0);
|
||||
writeb(&transmit_pos, 0);
|
||||
}
|
||||
if (tmax + len <= sizeof(transmit_buf))
|
||||
return &transmit_buf[tmax];
|
||||
if (tmax - tpos + len > sizeof(transmit_buf))
|
||||
return NULL;
|
||||
// Disable TX irq and move buffer
|
||||
writeb(&transmit_max, 0);
|
||||
barrier();
|
||||
tpos = readb(&transmit_pos);
|
||||
tmax -= tpos;
|
||||
memmove(&transmit_buf[0], &transmit_buf[tpos], tmax);
|
||||
writeb(&transmit_pos, 0);
|
||||
barrier();
|
||||
writeb(&transmit_max, tmax);
|
||||
UCSR0B |= 1<<UDRIE0;
|
||||
return &transmit_buf[tmax];
|
||||
}
|
||||
|
||||
// Accept the given number of bytes added to the transmit buffer
|
||||
void
|
||||
console_push_output(uint8_t len)
|
||||
{
|
||||
writeb(&transmit_max, readb(&transmit_max) + len);
|
||||
// enable TX interrupt
|
||||
UCSR0B |= 1<<UDRIE0;
|
||||
}
|
171
src/avr/timer.c
Normal file
171
src/avr/timer.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
// AVR timer interrupt scheduling code.
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <avr/interrupt.h> // TCNT1
|
||||
#include "command.h" // shutdown
|
||||
#include "irq.h" // irq_save
|
||||
#include "sched.h" // sched_timer_kick
|
||||
#include "timer.h" // timer_from_ms
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Low level timer code
|
||||
****************************************************************/
|
||||
|
||||
// Return the number of clock ticks for a given number of milliseconds
|
||||
uint32_t
|
||||
timer_from_ms(uint32_t ms)
|
||||
{
|
||||
return ms * (F_CPU / 1000);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
timer_get(void)
|
||||
{
|
||||
return TCNT1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
timer_set(uint16_t next)
|
||||
{
|
||||
OCR1A = next;
|
||||
}
|
||||
|
||||
static inline void
|
||||
timer_set_clear(uint16_t next)
|
||||
{
|
||||
OCR1A = next;
|
||||
TIFR1 = 1<<OCF1A;
|
||||
}
|
||||
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
sched_timer_kick();
|
||||
}
|
||||
|
||||
static void
|
||||
timer_init(void)
|
||||
{
|
||||
// no outputs
|
||||
TCCR1A = 0;
|
||||
// Normal Mode
|
||||
TCCR1B = 1<<CS10;
|
||||
// enable interrupt
|
||||
TIMSK1 = 1<<OCIE1A;
|
||||
}
|
||||
DECL_INIT(timer_init);
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* 32bit timer wrappers
|
||||
****************************************************************/
|
||||
|
||||
static uint32_t timer_last;
|
||||
|
||||
// Return the 32bit current time given the 16bit current time.
|
||||
static __always_inline uint32_t
|
||||
calc_time(uint32_t last, uint16_t cur)
|
||||
{
|
||||
union u32_u16_u calc;
|
||||
calc.val = last;
|
||||
if (cur < calc.lo)
|
||||
calc.hi++;
|
||||
calc.lo = cur;
|
||||
return calc.val;
|
||||
}
|
||||
|
||||
// Called by main code once every millisecond. (IRQs disabled.)
|
||||
void
|
||||
timer_periodic(void)
|
||||
{
|
||||
timer_last = calc_time(timer_last, timer_get());
|
||||
}
|
||||
|
||||
// Return the current time (in absolute clock ticks).
|
||||
uint32_t
|
||||
timer_read_time(void)
|
||||
{
|
||||
uint8_t flag = irq_save();
|
||||
uint16_t cur = timer_get();
|
||||
uint32_t last = timer_last;
|
||||
irq_restore(flag);
|
||||
return calc_time(last, cur);
|
||||
}
|
||||
|
||||
#define TIMER_MIN_TICKS 100
|
||||
|
||||
// Set the next timer wake time (in absolute clock ticks). Caller
|
||||
// must disable irqs. The caller should not schedule a time more than
|
||||
// a few milliseconds in the future.
|
||||
uint8_t
|
||||
timer_set_next(uint32_t next)
|
||||
{
|
||||
uint16_t cur = timer_get();
|
||||
if ((int16_t)(OCR1A - cur) < 0 && !(TIFR1 & (1<<OCF1A)))
|
||||
// Already processing timer irqs
|
||||
try_shutdown("timer_set_next called during timer dispatch");
|
||||
uint32_t mintime = calc_time(timer_last, cur + TIMER_MIN_TICKS);
|
||||
if (sched_is_before(mintime, next)) {
|
||||
timer_set_clear(next);
|
||||
return 0;
|
||||
}
|
||||
timer_set_clear(mintime);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t timer_repeat;
|
||||
#define TIMER_MAX_REPEAT 40
|
||||
#define TIMER_MAX_NEXT_REPEAT 15
|
||||
|
||||
#define TIMER_MIN_TRY_TICKS 60 // 40 ticks to exit irq; 20 ticks of progress
|
||||
#define TIMER_DEFER_REPEAT_TICKS 200
|
||||
|
||||
// Similar to timer_set_next(), but wait for the given time if it is
|
||||
// in the near future.
|
||||
uint8_t
|
||||
timer_try_set_next(uint32_t target)
|
||||
{
|
||||
uint16_t next = target, now = timer_get();
|
||||
int16_t diff = next - now;
|
||||
if (diff > TIMER_MIN_TRY_TICKS)
|
||||
// Schedule next timer normally.
|
||||
goto done;
|
||||
|
||||
// Next timer is in the past or near future - can't reschedule to it
|
||||
uint8_t tr = timer_repeat-1;
|
||||
if (likely(tr)) {
|
||||
irq_enable();
|
||||
timer_repeat = tr;
|
||||
irq_disable();
|
||||
while (diff >= 0) {
|
||||
// Next timer is in the near future - wait for time to occur
|
||||
now = timer_get();
|
||||
irq_enable();
|
||||
diff = next - now;
|
||||
irq_disable();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Too many repeat timers from a single interrupt - force a pause
|
||||
timer_repeat = TIMER_MAX_NEXT_REPEAT;
|
||||
next = now + TIMER_DEFER_REPEAT_TICKS;
|
||||
if (diff < (int16_t)(-timer_from_ms(1)))
|
||||
goto fail;
|
||||
|
||||
done:
|
||||
timer_set(next);
|
||||
return 1;
|
||||
fail:
|
||||
shutdown("Rescheduled timer in the past");
|
||||
}
|
||||
|
||||
static void
|
||||
timer_task(void)
|
||||
{
|
||||
timer_repeat = TIMER_MAX_REPEAT;
|
||||
}
|
||||
DECL_TASK(timer_task);
|
12
src/avr/timer.h
Normal file
12
src/avr/timer.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef __AVR_TIMER_H
|
||||
#define __AVR_TIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t timer_from_ms(uint32_t ms);
|
||||
void timer_periodic(void);
|
||||
uint32_t timer_read_time(void);
|
||||
uint8_t timer_set_next(uint32_t next);
|
||||
uint8_t timer_try_set_next(uint32_t next);
|
||||
|
||||
#endif // timer.h
|
38
src/avr/watchdog.c
Normal file
38
src/avr/watchdog.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Initialization of AVR watchdog timer.
|
||||
//
|
||||
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include <avr/interrupt.h> // WDT_vect
|
||||
#include <avr/wdt.h> // wdt_enable
|
||||
#include "command.h" // shutdown
|
||||
#include "sched.h" // DECL_TASK
|
||||
|
||||
static uint8_t watchdog_shutdown;
|
||||
|
||||
ISR(WDT_vect)
|
||||
{
|
||||
watchdog_shutdown = 1;
|
||||
shutdown("Watchdog timer!");
|
||||
}
|
||||
|
||||
static void
|
||||
watchdog_reset(void)
|
||||
{
|
||||
wdt_reset();
|
||||
if (watchdog_shutdown) {
|
||||
WDTCSR |= 1<<WDIE;
|
||||
watchdog_shutdown = 0;
|
||||
}
|
||||
}
|
||||
DECL_TASK(watchdog_reset);
|
||||
|
||||
static void
|
||||
watchdog_init(void)
|
||||
{
|
||||
// 0.5s timeout, interrupt and system reset
|
||||
wdt_enable(WDTO_500MS);
|
||||
WDTCSR |= 1<<WDIE;
|
||||
}
|
||||
DECL_INIT(watchdog_init);
|
Loading…
Add table
Add a link
Reference in a new issue