From 7b70f64d847bf6d279cb2d1a68d06db17004e7f9 Mon Sep 17 00:00:00 2001 From: Hinnerk van Bruinehsen Date: Tue, 15 Jul 2014 12:09:50 +0200 Subject: [PATCH] cpu: atmega2560: Initial import --- cpu/atmega2560/Makefile | 5 + cpu/atmega2560/Makefile.include | 28 ++ cpu/atmega2560/cpu.c | 29 ++ cpu/atmega2560/doc.txt | 10 + cpu/atmega2560/hwtimer_arch.c | 73 +++ cpu/atmega2560/include/cpu-conf.h | 52 +++ cpu/atmega2560/include/hwtimer_cpu.h | 32 ++ cpu/atmega2560/io_arch.c | 35 ++ cpu/atmega2560/lpm_arch.c | 53 +++ cpu/atmega2560/periph/Makefile | 2 + cpu/atmega2560/periph/gpio.c | 71 +++ cpu/atmega2560/periph/timer.c | 672 +++++++++++++++++++++++++++ cpu/atmega2560/periph/uart.c | 317 +++++++++++++ cpu/atmega2560/reboot_arch.c | 40 ++ cpu/atmega2560/startup.c | 74 +++ 15 files changed, 1493 insertions(+) create mode 100644 cpu/atmega2560/Makefile create mode 100644 cpu/atmega2560/Makefile.include create mode 100644 cpu/atmega2560/cpu.c create mode 100644 cpu/atmega2560/doc.txt create mode 100644 cpu/atmega2560/hwtimer_arch.c create mode 100644 cpu/atmega2560/include/cpu-conf.h create mode 100644 cpu/atmega2560/include/hwtimer_cpu.h create mode 100644 cpu/atmega2560/io_arch.c create mode 100644 cpu/atmega2560/lpm_arch.c create mode 100644 cpu/atmega2560/periph/Makefile create mode 100644 cpu/atmega2560/periph/gpio.c create mode 100644 cpu/atmega2560/periph/timer.c create mode 100644 cpu/atmega2560/periph/uart.c create mode 100644 cpu/atmega2560/reboot_arch.c create mode 100644 cpu/atmega2560/startup.c diff --git a/cpu/atmega2560/Makefile b/cpu/atmega2560/Makefile new file mode 100644 index 0000000000..5148810cfa --- /dev/null +++ b/cpu/atmega2560/Makefile @@ -0,0 +1,5 @@ +# define the module that is build +MODULE = cpu +# add a list of subdirectories, that should also be build +DIRS = periph $(ATMEGA_COMMON) +include $(RIOTBASE)/Makefile.base diff --git a/cpu/atmega2560/Makefile.include b/cpu/atmega2560/Makefile.include new file mode 100644 index 0000000000..13c5aa49e4 --- /dev/null +++ b/cpu/atmega2560/Makefile.include @@ -0,0 +1,28 @@ + +# this CPU implementation is using the new core/CPU interface +export CFLAGS += -DCOREIF_NG=1 + +# tell the build system that the CPU depends on the atmega common files +export USEMODULE += atmega_common + +# define path to atmega common module, which is needed for this CPU +export ATMEGA_COMMON = $(RIOTCPU)/atmega_common/ + +# define the linker script to use for this CPU +#export LINKERSCRIPT = $(RIOTCPU)/$(CPU)/atmega2560_linkerscript.ld + +# include CPU specific includes +export INCLUDES += -I$(RIOTCPU)/$(CPU)/include + +# explicitly tell the linker to link the syscalls and startup code. +# Without this the interrupt vectors will not be linked correctly! +export UNDEF += $(BINDIR)cpu/startup.o + +# export the peripheral drivers to be linked into the final binary +export USEMODULE += periph + +# the uart implementation uses ringbuffer and therefore needs lib +export USEMODULE += lib + +# CPU depends on the atmega common module, so include it +include $(ATMEGA_COMMON)Makefile.include diff --git a/cpu/atmega2560/cpu.c b/cpu/atmega2560/cpu.c new file mode 100644 index 0000000000..0f1f9fb255 --- /dev/null +++ b/cpu/atmega2560/cpu.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU initialization + * + * @author Hinnerk van Bruinehsen + * @} + */ + +#include "cpu.h" + +/** + * @brief Initialize the CPU, set IRQ priorities + */ +void cpu_init(void) +{ + /* Right now we need to do nothing here */ + ; +} diff --git a/cpu/atmega2560/doc.txt b/cpu/atmega2560/doc.txt new file mode 100644 index 0000000000..e825cd72ef --- /dev/null +++ b/cpu/atmega2560/doc.txt @@ -0,0 +1,10 @@ +/** + * @defgroup cpu_atmega2560 Atmel ATmega2560 + * @ingroup cpu + * @brief Implementation of Atmel's ATmega2560 MCU + */ + +/** + * @defgroup cpu_atmega2560_definitions Atmel ATmega2560 Definitions + * @ingroup cpu_atmega2560 + */ diff --git a/cpu/atmega2560/hwtimer_arch.c b/cpu/atmega2560/hwtimer_arch.c new file mode 100644 index 0000000000..44188727b7 --- /dev/null +++ b/cpu/atmega2560/hwtimer_arch.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file hwtimer_arch.c + * @brief Implementation of the kernels hwtimer interface + * + * The hardware timer implementation uses the ATmega2560 build-in system timer as back-end. + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include "arch/hwtimer_arch.h" +#include "board.h" +#include "periph/timer.h" +#include "thread.h" + + +void irq_handler(int channel); +void (*timeout_handler)(int); + + +void hwtimer_arch_init(void (*handler)(int), uint32_t fcpu) +{ + timeout_handler = handler; + timer_init(HW_TIMER, 1, &irq_handler); +} + +void hwtimer_arch_enable_interrupt(void) +{ + timer_irq_enable(HW_TIMER); +} + +void hwtimer_arch_disable_interrupt(void) +{ + timer_irq_disable(HW_TIMER); +} + +void hwtimer_arch_set(unsigned long offset, short timer) +{ + timer_set(HW_TIMER, timer, offset); +} + +void hwtimer_arch_set_absolute(unsigned long value, short timer) +{ + timer_set_absolute(HW_TIMER, timer, value); +} + +void hwtimer_arch_unset(short timer) +{ + timer_clear(HW_TIMER, timer); +} + +unsigned long hwtimer_arch_now(void) +{ + return timer_read(HW_TIMER); +} + +void irq_handler(int channel) +{ + timeout_handler((short)(channel)); +} diff --git a/cpu/atmega2560/include/cpu-conf.h b/cpu/atmega2560/include/cpu-conf.h new file mode 100644 index 0000000000..d5f9b0f9c1 --- /dev/null +++ b/cpu/atmega2560/include/cpu-conf.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Implementation specific CPU configuration options + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + */ + +#ifndef __CPU_CONF_H +#define __CPU_CONF_H + + + +/** + * @name Kernel configuration + * + * Since printf seems to get memory allocated by the linker/avr-libc the stack + * size tested sucessfully even with pretty small stacks.k + * @{ + */ +#define KERNEL_CONF_STACKSIZE_PRINTF (128) + +#ifndef KERNEL_CONF_STACKSIZE_DEFAULT +#define KERNEL_CONF_STACKSIZE_DEFAULT (256) +#endif + +#define KERNEL_CONF_STACKSIZE_IDLE (128) +/** @} */ + +/** + * @name UART0 buffer size definition for compatibility reasons + * + * TODO: remove once the remodeling of the uart0 driver is done + * @{ + */ +#ifndef UART0_BUFSIZE +#define UART0_BUFSIZE (128) +#endif +/** @} */ + +#endif /* __CPU_CONF_H */ +/** @} */ diff --git a/cpu/atmega2560/include/hwtimer_cpu.h b/cpu/atmega2560/include/hwtimer_cpu.h new file mode 100644 index 0000000000..ed88022c03 --- /dev/null +++ b/cpu/atmega2560/include/hwtimer_cpu.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file + * @brief CPU specific hwtimer configuration options + * + * @author Hinnerk van Bruinehsen + */ + +#ifndef __HWTIMER_CPU_H +#define __HWTIMER_CPU_H + +/** + * @name Hardware timer configuration + * @{ + */ +#define HWTIMER_MAXTIMERS 3 /**< the CPU implementation supports 3 HW timers */ +#define HWTIMER_SPEED 1000000 /**< the HW timer runs with 1MHz */ +#define HWTIMER_MAXTICKS (0xFFFF) /**< 16-bit timer */ +/** @} */ + +#endif /* __HWTIMER_CPU_H */ +/** @} */ diff --git a/cpu/atmega2560/io_arch.c b/cpu/atmega2560/io_arch.c new file mode 100644 index 0000000000..437bde8041 --- /dev/null +++ b/cpu/atmega2560/io_arch.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file io_arch.c + * @brief Implementation of the kernel's architecture dependent IO interface + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include + +#include "arch/io_arch.h" + +int io_arch_puts(char *data, int size) +{ + int i = 0; + + for (; i < size; i++) { + putchar(data[i]); + } + + return i; +} diff --git a/cpu/atmega2560/lpm_arch.c b/cpu/atmega2560/lpm_arch.c new file mode 100644 index 0000000000..85a1722751 --- /dev/null +++ b/cpu/atmega2560/lpm_arch.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file lpm_arch.c + * @brief Implementation of the kernels power management interface + * + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include "arch/lpm_arch.h" + +void lpm_arch_init(void) +{ + /* TODO */ +} + +enum lpm_mode lpm_arch_set(enum lpm_mode target) +{ + /* TODO */ + return 0; +} + +enum lpm_mode lpm_arch_get(void) +{ + /* TODO */ + return 0; +} + +void lpm_arch_awake(void) +{ + /* TODO */ +} + +void lpm_arch_begin_awake(void) +{ + /* TODO */ +} + +void lpm_arch_end_awake(void) +{ + /* TODO */ +} diff --git a/cpu/atmega2560/periph/Makefile b/cpu/atmega2560/periph/Makefile new file mode 100644 index 0000000000..87a55ed684 --- /dev/null +++ b/cpu/atmega2560/periph/Makefile @@ -0,0 +1,2 @@ +MODULE = periph +include $(RIOTBASE)/Makefile.base diff --git a/cpu/atmega2560/periph/gpio.c b/cpu/atmega2560/periph/gpio.c new file mode 100644 index 0000000000..e41d7d8e88 --- /dev/null +++ b/cpu/atmega2560/periph/gpio.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_periph + * @{ + * + * @file gpio.c + * @brief Low-level GPIO driver implementation + * + * @author Hauke Petersen + * + * @} + */ + +/* + * TODO: Implement GPIO interface + */ + +#include "cpu.h" +#include "periph/gpio.h" +#include "periph_conf.h" + +typedef struct { + void (*cb)(void); +} gpio_state_t; + +/*static gpio_state_t config[GPIO_NUMOF]; */ + + + +int gpio_init_out(gpio_t dev, gpio_pp_t pushpull) +{ + return -1; +} + +int gpio_init_in(gpio_t dev, gpio_pp_t pushpull) +{ + return -1; +} + +int gpio_init_int(gpio_t dev, gpio_pp_t pushpull, gpio_flank_t flank, gpio_cb_t cb, void *arg) +{ + return -1; +} + +int gpio_read(gpio_t dev) +{ + return -1; +} + +void gpio_set(gpio_t dev) +{ +} + +void gpio_clear(gpio_t dev) +{ +} + +void gpio_toggle(gpio_t dev) +{ +} + +void gpio_write(gpio_t dev, int value) +{ +} diff --git a/cpu/atmega2560/periph/timer.c b/cpu/atmega2560/periph/timer.c new file mode 100644 index 0000000000..a8616c4245 --- /dev/null +++ b/cpu/atmega2560/periph/timer.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_periph + * @{ + * + * @file timer.c + * @brief Low-level timer driver implementation for the ATmega2560 CPU + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include +#include + +#include + +#include "board.h" +#include "cpu.h" + +#include "periph/timer.h" +#include "periph_conf.h" + +static inline int __set_timer(tim_t dev, + int channel, + unsigned int timeout, + unsigned int interval + ); + +#define IRQ_DISABLED 0x00 + +typedef struct { + void (*cb)(int); + volatile uint8_t ctr_a; + volatile uint8_t ctr_b; + volatile uint8_t ctr_c; + uint8_t limit; + uint16_t timeout_a; + uint16_t timeout_b; + uint16_t timeout_c; +} timer_conf_t; + +/** + * @brief Timer state memory + */ +timer_conf_t config[TIMER_NUMOF]; + +/** + * @brief Setup the given timer + * + */ +int timer_init(tim_t dev, unsigned int ticks_per_us, void (*callback)(int)) +{ + /* reject impossible ticks_per_us values */ + if ((ticks_per_us > 16) && (ticks_per_us == 0)) { + return -1; + } + + config[dev].limit = 16 / ticks_per_us; + config[dev].ctr_a = 0x00; + config[dev].ctr_b = 0x00; + config[dev].ctr_c = 0x00; + + /* select the timer and enable the timer specific peripheral clocks */ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + TIMER0_COUNTER = 0; + TIMER0_CONTROL_B |= TIMER0_FREQ_16MHZ; + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + TIMER1_COUNTER = 0; + TIMER1_CONTROL_B |= TIMER1_FREQ_16MHZ; + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + TIMER2_COUNTER = 0; + TIMER2_CONTROL_B |= TIMER2_FREQ_16MHZ; + break; +#endif + + case TIMER_UNDEFINED: + default: + return -1; + } + + /* save callback */ + config[dev].cb = callback; + + return 0; +} + +int timer_set(tim_t dev, int channel, unsigned int timeout) +{ + return __set_timer(dev, channel, timer_read(dev) + timeout, timeout); +} + +int timer_set_absolute(tim_t dev, int channel, unsigned int timeout) +{ + return __set_timer(dev, channel, timeout, timeout); +} + +int timer_clear(tim_t dev, int channel) +{ + + switch (dev) { +#if TIMER_0_DIS + + case TIMER_0: + switch (channel) { + case 0: + TIMER0_IRQ_FLAG_REG &= ~(1 << TIMER0_COMP_A_FLAG); + config[dev].timeout_a = IRQ_DISABLED; + break; + + case 1: + TIMER0_IRQ_FLAG_REG &= ~(1 << TIMER0_COMP_B_FLAG); + config[dev].timeout_b = IRQ_DISABLED; + break; + + case 2: + TIMER0_IRQ_FLAG_REG &= ~(1 << TIMER0_COMP_C_FLAG); + config[dev].timeout_c = IRQ_DISABLED; + break; + + default: + return -1; + } + + break; +#endif +#if TIMER_1_DIS + + case TIMER_1: + switch (channel) { + case 0: + TIMER1_IRQ_FLAG_REG &= ~(1 << TIMER1_COMP_A_FLAG); + config[dev].timeout_a = IRQ_DISABLED; + break; + + case 1: + TIMER1_IRQ_FLAG_REG &= ~(1 << TIMER1_COMP_B_FLAG); + config[dev].timeout_b = IRQ_DISABLED; + break; + + case 2: + TIMER1_IRQ_FLAG_REG &= ~(1 << TIMER1_COMP_C_FLAG); + config[dev].timeout_c = IRQ_DISABLED; + break; + + default: + return -1; + break; + } + + break; +#endif +#if TIMER_2_DIS + + case TIMER_2: + switch (channel) { + case 0: + TIMER2_IRQ_FLAG_REG &= ~(1 << TIMER2_COMP_A_FLAG); + config[dev].timeout_a = IRQ_DISABLED; + break; + + case 1: + TIMER2_IRQ_FLAG_REG &= ~(1 << TIMER2_COMP_B_FLAG); + config[dev].timeout_b = IRQ_DISABLED; + break; + + case 2: + TIMER2_IRQ_FLAG_REG &= ~(1 << TIMER2_COMP_C_FLAG); + config[dev].timeout_c = IRQ_DISABLED; + break; + + default: + return -1; + break; + } + + break; +#endif + + case TIMER_UNDEFINED: + default: + return -1; + } + + timer_irq_disable(dev); + return 1; +} + +unsigned int timer_read(tim_t dev) +{ + uint16_t value; + /* + * Disabling interrupts globally because read from 16 Bit register can + * otherwise be messed up + */ + disableIRQ(); + + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + value = TIMER0_COUNTER; + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + value = TIMER1_COUNTER; + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + value = TIMER2_COUNTER; + break; +#endif + + case TIMER_UNDEFINED: + default: + value = 0; + enableIRQ(); + } + + enableIRQ(); + return value; +} + +void timer_stop(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + TIMER0_CONTROL_B = TIMER0_FREQ_DISABLE; + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + TIMER1_CONTROL_B = TIMER1_FREQ_DISABLE; + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + TIMER2_CONTROL_B = TIMER2_FREQ_DISABLE; + break; +#endif + + case TIMER_UNDEFINED: + break; + } +} + +void timer_start(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + TIMER0_CONTROL_B |= TIMER0_FREQ_16MHZ; + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + TIMER1_CONTROL_B |= TIMER1_FREQ_16MHZ; + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + TIMER1_CONTROL_B |= TIMER1_FREQ_16MHZ; + break; +#endif + + case TIMER_UNDEFINED: + break; + } +} + +void timer_irq_enable(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + if (config[dev].timeout_a != IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG |= (1 << TIMER0_COMP_A_EN); + } + + if (config[dev].timeout_b != IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG |= (1 << TIMER0_COMP_B_EN); + } + + if (config[dev].timeout_c != IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG |= (1 << TIMER0_COMP_C_EN); + } + + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + if (config[dev].timeout_a != IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG |= (1 << TIMER1_COMP_A_EN); + } + + if (config[dev].timeout_b != IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG |= (1 << TIMER1_COMP_B_EN); + } + + if (config[dev].timeout_c != IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG |= (1 << TIMER1_COMP_C_EN); + } + + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + if (config[dev].timeout_a != IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG |= (1 << TIMER2_COMP_A_EN); + } + + if (config[dev].timeout_b != IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG |= (1 << TIMER2_COMP_B_EN); + } + + if (config[dev].timeout_c != IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG |= (1 << TIMER2_COMP_C_EN); + } + + break; +#endif + + case TIMER_UNDEFINED: + break; + } + + enableIRQ(); +} + +void timer_irq_disable(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + if (config[dev].timeout_a == IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG &= ~(1 << TIMER0_COMP_A_EN); + } + + if (config[dev].timeout_b == IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG &= ~(1 << TIMER0_COMP_B_EN); + } + + if (config[dev].timeout_c == IRQ_DISABLED) { + TIMER0_IRQ_MASK_REG &= ~(1 << TIMER0_COMP_C_EN); + } + + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + if (config[dev].timeout_a == IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG &= ~(1 << TIMER1_COMP_A_EN); + } + + if (config[dev].timeout_b == IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG &= ~(1 << TIMER1_COMP_B_EN); + } + + if (config[dev].timeout_c == IRQ_DISABLED) { + TIMER1_IRQ_MASK_REG &= ~(1 << TIMER1_COMP_C_EN); + } + + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + if (config[dev].timeout_a == IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG &= ~(1 << TIMER2_COMP_A_EN); + } + + if (config[dev].timeout_b == IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG &= ~(1 << TIMER2_COMP_B_EN); + } + + if (config[dev].timeout_c == IRQ_DISABLED) { + TIMER2_IRQ_MASK_REG &= ~(1 << TIMER2_COMP_C_EN); + } + + break; +#endif + + case TIMER_UNDEFINED: + break; + } +} + +void timer_reset(tim_t dev) +{ + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + TIMER0_COUNTER = 0; + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + TIMER1_COUNTER = 0; + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + TIMER2_COUNTER = 0; + break; +#endif + + case TIMER_UNDEFINED: + break; + } +} + +inline int __set_timer(tim_t dev, int channel, unsigned int timeout, unsigned int interval) +{ + /* + * Disabling interrupts globally because write to 16 Bit register can + * otherwise be messed up + */ + disableIRQ(); + + switch (dev) { +#if TIMER_0_EN + + case TIMER_0: + switch (channel) { + case 0: + TIMER0_COMP_A = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_a = interval; + break; + + case 1: + TIMER0_COMP_B = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_b = interval; + break; + + case 2: + TIMER0_COMP_C = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_c = interval; + break; + + default: + enableIRQ(); + return -1; + } + + break; +#endif +#if TIMER_1_EN + + case TIMER_1: + switch (channel) { + case 0: + TIMER1_COMP_A = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_a = interval; + break; + + case 1: + TIMER1_COMP_B = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_b = interval; + break; + + case 2: + TIMER1_COMP_C = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_c = interval; + break; + + default: + enableIRQ(); + return -1; + } + + break; +#endif +#if TIMER_2_EN + + case TIMER_2: + switch (channel) { + case 0: + TIMER2_COMP_A = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_a = interval; + break; + + case 1: + TIMER2_COMP_B = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_b = interval; + break; + + case 2: + TIMER2_COMP_C = (uint16_t) timeout * config[dev].limit; + config[dev].timeout_c = interval; + break; + + default: + enableIRQ(); + return -1; + } + + break; +#endif + + case TIMER_UNDEFINED: + default: + enableIRQ(); + return -1; + } + + /* enable interrupts for given timer */ + timer_irq_enable(dev); + enableIRQ(); + + return 1; +} +#if TIMER_0_EN +ISR(TIMER0_COMPA_ISR, ISR_BLOCK) +{ + config[TIMER_0].ctr_a++; + + if (config[TIMER_0].ctr_a >= config[TIMER_0].limit) { + config[TIMER_0].limit = 0; + config[TIMER_0].cb(0); + TIMER0_COMP_A = TIMER0_COMP_A + config[TIMER_0].timeout_a * config[TIMER_0].limit; + } +} +ISR(TIMER0_COMPB_ISR, ISR_BLOCK) +{ + config[TIMER_0].ctr_b++; + + if (config[TIMER_0].ctr_b >= config[TIMER_0].limit) { + config[TIMER_0].limit = 0; + config[TIMER_0].cb(1); + TIMER0_COMP_B = TIMER0_COMP_B + config[TIMER_0].timeout_b * config[TIMER_0].limit; + } +} +ISR(TIMER0_COMPC_ISR, ISR_BLOCK) +{ + config[TIMER_0].ctr_c++; + + if (config[TIMER_0].ctr_c >= config[TIMER_0].limit) { + config[TIMER_0].limit = 0; + config[TIMER_0].cb(2); + TIMER0_COMP_C = TIMER0_COMP_C + config[TIMER_0].timeout_c * config[TIMER_0].limit; + } +} +#endif /* TIMER_0_EN */ + +#if TIMER_1_EN +ISR(TIMER1_COMPA_ISR, ISR_BLOCK) +{ + config[TIMER_1].ctr_a++; + + if (config[TIMER_1].ctr_a >= config[TIMER_1].limit) { + config[TIMER_1].limit = 0; + config[TIMER_1].cb(0); + TIMER1_COMP_A = TIMER1_COMP_A + config[TIMER_1].timeout_a * config[TIMER_1].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +ISR(TIMER1_COMPB_ISR, ISR_BLOCK) +{ + config[TIMER_1].ctr_b++; + + if (config[TIMER_1].ctr_b >= config[TIMER_1].limit) { + config[TIMER_1].limit = 0; + config[TIMER_1].cb(1); + TIMER1_COMP_B = TIMER1_COMP_B + config[TIMER_1].timeout_b * config[TIMER_1].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +ISR(TIMER1_COMPC_ISR, ISR_BLOCK) +{ + config[TIMER_1].ctr_c++; + + if (config[TIMER_1].ctr_c >= config[TIMER_1].limit) { + config[TIMER_1].limit = 0; + config[TIMER_1].cb(2); + TIMER1_COMP_C = TIMER1_COMP_C + config[TIMER_1].timeout_c * config[TIMER_1].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* TIMER_1_EN */ + +#if TIMER_2_EN +ISR(TIMER2_COMPA_ISR, ISR_BLOCK) +{ + config[TIMER_2].ctr_a++; + + if (config[TIMER_2].ctr_a >= config[TIMER_2].limit) { + config[TIMER_2].limit = 0; + config[TIMER_2].cb(0); + TIMER2_COMP_A = TIMER2_COMP_A + config[TIMER_2].timeout_a * config[TIMER_2].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +ISR(TIMER2_COMPB_ISR, ISR_BLOCK) +{ + config[TIMER_2].ctr_b++; + + if (config[TIMER_2].ctr_b >= config[TIMER_2].limit) { + config[TIMER_2].limit = 0; + config[TIMER_2].cb(1); + TIMER2_COMP_B = TIMER2_COMP_B + config[TIMER_2].timeout_b * config[TIMER_2].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +ISR(TIMER2_COMPC_ISR, ISR_BLOCK) +{ + config[TIMER_2].ctr_c++; + + if (config[TIMER_2].ctr_c >= config[TIMER_2].limit) { + config[TIMER_2].limit = 0; + config[TIMER_2].cb(2); + TIMER2_COMP_C = TIMER2_COMP_C + config[TIMER_2].timeout_c * config[TIMER_2].limit; + } + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* TIMER_2_EN */ diff --git a/cpu/atmega2560/periph/uart.c b/cpu/atmega2560/periph/uart.c new file mode 100644 index 0000000000..ae08130287 --- /dev/null +++ b/cpu/atmega2560/periph/uart.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_periph + * @{ + * + * @file uart.c + * @brief Low-level UART driver implementation + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include "board.h" +#include "cpu.h" +#include "thread.h" +#include "sched.h" + +#include "periph/uart.h" +#include "periph_conf.h" + + +/** + * @brief Each UART device has to store two callbacks. + */ +typedef struct { + uart_rx_cb_t rx_cb; + uart_tx_cb_t tx_cb; + void *arg; +} uart_conf_t; + +/** + * @brief Allocate memory to store the callback functions. + */ +static uart_conf_t config[UART_NUMOF]; + +int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, uart_tx_cb_t tx_cb, void *arg) +{ + /* initialize basic functionality */ + int res = uart_init_blocking(uart, baudrate); + + if (res != 0) { + return res; + } + + /* register callbacks */ + config[uart].rx_cb = rx_cb; + config[uart].tx_cb = tx_cb; + config[uart].arg = arg; + + /* configure interrupts and enable RX interrupt */ + switch (uart) { +#if UART_0_EN + + case UART_0: + UART0_RX_IRQ_EN; + break; +#endif /* UART_0_EN */ +#if UART_1_EN + + case UART_1: + UART1_RX_IRQ_EN; + break; +#endif /* UART_1_EN */ +#if UART_2_EN + + case UART_2: + UART2_RX_IRQ_EN; + break; +#endif /* UART_2_EN */ +#if UART_3_EN + + case UART_3: + UART3_RX_IRQ_EN; + break; +#endif /* UART_3_EN */ + } + + return 0; +} + +int uart_init_blocking(uart_t uart, uint32_t baudrate) +{ + uint16_t clock_divider = F_CPU / (16 * baudrate); + + switch (uart) { +#if UART_0_EN + + case UART_0: + /* enable RX and TX */ + UART0_RX_TX_EN; + /* use 8 Bit characters */ + UART0_SET_8BIT_SIZE; + + /* set clock divider */ + UART0_BAUD_RATE_L = clock_divider; + UART0_BAUD_RATE_H = (clock_divider >> 8); + break; +#endif /* UART_0 */ +#if UART_1_EN + + case UART_1: + /* enable RX and TX */ + UART1_RX_TX_EN; + /* use 8 Bit characters */ + UART1_SET_8BIT_SIZE; + + /* set clock divider */ + UART1_BAUD_RATE_L = clock_divider; + UART1_BAUD_RATE_H = (clock_divider >> 8); + break; +#endif /* UART_1 */ +#if UART_2_EN + + case UART_2: + /* enable RX and TX */ + UART2_RX_TX_EN; + /* use 8 Bit characters */ + UART2_SET_8BIT_SIZE; + + /* set clock divider */ + UART2_BAUD_RATE_L = clock_divider; + UART2_BAUD_RATE_H = (clock_divider >> 8); + break; +#endif /* UART_2 */ +#if UART_3_EN + + case UART_3: + /* enable RX and TX */ + UART3_RX_TX_EN; + /* use 8 Bit characters */ + UART3_SET_8BIT_SIZE; + + /* set clock divider */ + UART3_BAUD_RATE_L = clock_divider; + UART3_BAUD_RATE_H = (clock_divider >> 8); + break; +#endif /* UART_3 */ + } + + return 0; +} + +void uart_tx_begin(uart_t uart) +{ + +} + +void uart_tx_end(uart_t uart) +{ + +} + +int uart_write(uart_t uart, char data) +{ + switch (uart) { +#if UART_0_EN + + case UART_0: + UART0_DATA_REGISTER = data; + break; +#endif /* UART_0_EN */ +#if UART_1_EN + + case UART_1: + UART1_DATA_REGISTER = data; + break; +#endif /* UART_1_EN */ +#if UART_2_EN + + case UART_2: + UART2_DATA_REGISTER = data; + break; +#endif /* UART_2_EN */ +#if UART_3_EN + + case UART_3: + UART3_DATA_REGISTER = data; + break; +#endif /* UART_3_EN */ + } + + return 1; +} + +int uart_read_blocking(uart_t uart, char *data) +{ + switch (uart) { +#if UART_0_EN + + case UART_0: + while (!UART0_RECEIVED_DATA); + + *data = (char) UART0_DATA_REGISTER; + break; +#endif /* UART_0_EN */ +#if UART_1_EN + + case UART_1: + while (!UART1_RECEIVED_DATA); + + *data = (char) UART1_DATA_REGISTER; + break; +#endif /* UART_1_EN */ +#if UART_2_EN + + case UART_2: + while (!UART2_RECEIVED_DATA); + + *data = (char) UART2_DATA_REGISTER; + break; +#endif /* UART_2_EN */ +#if UART_3_EN + + case UART_3: + while (!UART3_RECEIVED_DATA); + + *data = (char) UART3_DATA_REGISTER; + break; +#endif /* UART_3_EN */ + } + + return 1; +} + +int uart_write_blocking(uart_t uart, char data) +{ + switch (uart) { +#if UART_0_EN + + case UART_0: + while (!UART0_DTREG_EMPTY); + + UART0_DATA_REGISTER = data; + break; +#endif /* UART_0_EN */ +#if UART_1_EN + + case UART_1: + while (!UART1_DTREG_EMPTY); + + UART1_DATA_REGISTER = data; + break; +#endif /* UART_1_EN */ +#if UART_2_EN + + case UART_2: + while (!UART2_DTREG_EMPTY); + + UART2_DATA_REGISTER = data; + break; +#endif /* UART_2_EN */ +#if UART_3_EN + + case UART_3: + while (!UART3_DTREG_EMPTY); + + UART3_DATA_REGISTER = data; + break; +#endif /* UART_3_EN */ + } + + return 1; +} + + +#if UART_0_EN +ISR(USART0_RX_vect, ISR_BLOCK) +{ + config[UART_0].rx_cb(config[UART_0].arg, UART0_DATA_REGISTER); + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* UART_0_EN */ + +#if UART_1_EN +ISR(USART1_RX_vect, ISR_BLOCK) +{ + config[UART_1].rx_cb(config[UART_1].arg, UART0_DATA_REGISTER); + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* UART_1_EN */ + +#if UART_1_EN +ISR(USART2_RX_vect, ISR_BLOCK) +{ + config[UART_2].rx_cb(config[UART_2].arg, UART0_DATA_REGISTER); + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* UART_2_EN */ + +#if UART_2_EN +ISR(USART2_RX_vect, ISR_BLOCK) +{ + config[UART_3].rx_cb(config[UART_3].arg, UART0_DATA_REGISTER); + + if (sched_context_switch_request) { + thread_yield(); + } +} +#endif /* UART_3_EN */ diff --git a/cpu/atmega2560/reboot_arch.c b/cpu/atmega2560/reboot_arch.c new file mode 100644 index 0000000000..b34098aefc --- /dev/null +++ b/cpu/atmega2560/reboot_arch.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file reboot_arch.c + * @brief Implementation of the kernels reboot interface + * + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include +#include + +#include "arch/reboot_arch.h" +#include "cpu.h" + + +int reboot_arch(int mode) +{ + printf("Going into reboot, mode %i\n", mode); + + /* + * Since the AVR doesn't support a real software reset, we set the Watchdog + * Timer on a 250ms timeout. Consider this a kludge. + */ + wdt_enable(WDTO_250MS); + + + return 0; +} diff --git a/cpu/atmega2560/startup.c b/cpu/atmega2560/startup.c new file mode 100644 index 0000000000..e2796bd625 --- /dev/null +++ b/cpu/atmega2560/startup.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega2560 + * @{ + * + * @file startup.c + * @brief Startup code and interrupt vector definition + * + * @author Hinnerk van Bruinehsen + * + * @} + */ + +#include +#include +#include + +/* For Catchall-Loop */ +#include "board.h" +#include +#include + + +/** + * @brief functions for initializing the board, std-lib and kernel + */ +extern void board_init(void); +extern void kernel_init(void); +extern void __libc_init_array(void); + +/** + * @brief This pair of functions hook circumvent the call to main + * + * avr-libc normally uses the .init9 section for a call to main. This call + * seems to be not replaceable without hacking inside the library. We + * circumvent the call to main by using section .init7 to call the function + * reset_handler which therefore is the real entry point and section .init8 + * which should never be reached but just in case jumps to exit. + * This way there should be no way to call main directly. + */ +void init7_ovr(void) __attribute__((naked)) __attribute__((section(".init7"))); +void init8_ovr(void) __attribute__((naked)) __attribute__((section(".init8"))); + + +void init7_ovr(void) +{ + asm("call reset_handler"); +} + +void init8_ovr(void) +{ + asm("jmp exit"); +} +/** + * @brief This function is the entry point after a system reset + * + * After a system reset, the following steps are necessary and carried out: + * 1. initialize the board (sync clock, setup std-IO) + * 2. initialize and start RIOTs kernel + */ +void reset_handler(void) +{ + /* initialize the board and startup the kernel */ + board_init(); + /* startup the kernel */ + kernel_init(); +}