sam3x8e: Add initial support for Arduino Due boards
This adds basic support for running on the Atmel SAM3x8e micro-controllers that are found in the Arudino Due boards. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
31c04261c1
commit
cc62a3dbf3
|
@ -6,23 +6,24 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
def avr_pins(port_count):
|
def port_pins(port_count, bit_count=8):
|
||||||
pins = {}
|
pins = {}
|
||||||
for port in range(port_count):
|
for port in range(port_count):
|
||||||
portchr = chr(65 + port)
|
portchr = chr(65 + port)
|
||||||
if portchr == 'I':
|
if portchr == 'I':
|
||||||
continue
|
continue
|
||||||
for portbit in range(8):
|
for portbit in range(bit_count):
|
||||||
pins['P%c%d' % (portchr, portbit)] = port * 8 + portbit
|
pins['P%c%d' % (portchr, portbit)] = port * bit_count + portbit
|
||||||
return pins
|
return pins
|
||||||
|
|
||||||
PINS_atmega164 = avr_pins(4)
|
PINS_atmega164 = port_pins(4)
|
||||||
PINS_atmega1280 = avr_pins(12)
|
PINS_atmega1280 = port_pins(12)
|
||||||
|
|
||||||
MCU_PINS = {
|
MCU_PINS = {
|
||||||
"atmega168": PINS_atmega164, "atmega644p": PINS_atmega164,
|
"atmega168": PINS_atmega164, "atmega644p": PINS_atmega164,
|
||||||
"at90usb1286": avr_pins(5),
|
"at90usb1286": port_pins(5),
|
||||||
"atmega1280": PINS_atmega1280, "atmega2560": PINS_atmega1280,
|
"atmega1280": PINS_atmega1280, "atmega2560": PINS_atmega1280,
|
||||||
|
"sam3x8e": port_pins(4, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
def mcu_to_pins(mcu):
|
def mcu_to_pins(mcu):
|
||||||
|
|
|
@ -6,11 +6,14 @@ choice
|
||||||
prompt "Micro-controller Architecture"
|
prompt "Micro-controller Architecture"
|
||||||
config MACH_AVR
|
config MACH_AVR
|
||||||
bool "Atmega AVR"
|
bool "Atmega AVR"
|
||||||
|
config MACH_SAM3X8E
|
||||||
|
bool "SAM3x8e (Arduino Due)"
|
||||||
config MACH_SIMU
|
config MACH_SIMU
|
||||||
bool "Host simulator"
|
bool "Host simulator"
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
source "src/avr/Kconfig"
|
source "src/avr/Kconfig"
|
||||||
|
source "src/sam3x8e/Kconfig"
|
||||||
source "src/simulator/Kconfig"
|
source "src/simulator/Kconfig"
|
||||||
|
|
||||||
# The HAVE_GPIO_x options allow boards to disable support for some
|
# The HAVE_GPIO_x options allow boards to disable support for some
|
||||||
|
@ -35,5 +38,4 @@ config NO_UNSTEP_DELAY
|
||||||
config INLINE_STEPPER_HACK
|
config INLINE_STEPPER_HACK
|
||||||
# Enables gcc to inline stepper_event() into the main timer irq handler
|
# Enables gcc to inline stepper_event() into the main timer irq handler
|
||||||
bool
|
bool
|
||||||
default y if MACH_AVR
|
default y
|
||||||
default n
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Kconfig settings for SAM3x8e processors
|
||||||
|
|
||||||
|
if MACH_SAM3X8E
|
||||||
|
|
||||||
|
config BOARD_DIRECTORY
|
||||||
|
string
|
||||||
|
default "sam3x8e"
|
||||||
|
|
||||||
|
config MCU
|
||||||
|
string
|
||||||
|
default "sam3x8e"
|
||||||
|
|
||||||
|
config CLOCK_FREQ
|
||||||
|
int
|
||||||
|
default 42000000 # 84000000/2
|
||||||
|
|
||||||
|
config SERIAL
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
config SERIAL_BAUD
|
||||||
|
depends on SERIAL
|
||||||
|
int "Baud rate for serial port"
|
||||||
|
default 250000
|
||||||
|
|
||||||
|
endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Additional sam3x8e build rules
|
||||||
|
|
||||||
|
# Setup the toolchain
|
||||||
|
CROSS_PREFIX=arm-none-eabi-
|
||||||
|
|
||||||
|
dirs-y += src/sam3x8e src/generic
|
||||||
|
dirs-y += lib/cmsis-sam3x8e/source lib/cmsis-sam3x8e/source/gcc
|
||||||
|
|
||||||
|
CFLAGS-y += -mthumb -mcpu=cortex-m3
|
||||||
|
CFLAGS-y += -Ilib/cmsis-sam3x8e/include -Ilib/cmsis-sam3x8e/cmsis-include
|
||||||
|
CFLAGS-y += -D__SAM3X8E__
|
||||||
|
|
||||||
|
LDFLAGS-y += -Llib/cmsis-sam3x8e/source/gcc
|
||||||
|
LDFLAGS-y += -T lib/cmsis-sam3x8e/source/gcc/sam3x8e_flash.ld
|
||||||
|
LDFLAGS-y += --specs=nano.specs --specs=nosys.specs
|
||||||
|
|
||||||
|
# Add source files
|
||||||
|
src-y += sam3x8e/main.c sam3x8e/timer.c sam3x8e/gpio.c generic/crc16_ccitt.c
|
||||||
|
src-y += ../lib/cmsis-sam3x8e/source/system_sam3xa.c
|
||||||
|
src-y += ../lib/cmsis-sam3x8e/source/gcc/startup_sam3xa.c
|
||||||
|
src-$(CONFIG_SERIAL) += sam3x8e/serial.c
|
||||||
|
|
||||||
|
# Build the additional hex output file
|
||||||
|
target-y += $(OUT)klipper.bin
|
||||||
|
|
||||||
|
$(OUT)klipper.bin: $(OUT)klipper.elf
|
||||||
|
@echo " Creating hex file $@"
|
||||||
|
$(Q)$(OBJCOPY) -O binary $< $@
|
|
@ -0,0 +1,115 @@
|
||||||
|
// GPIO functions on sam3x8e
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
#include "command.h" // shutdown
|
||||||
|
#include "compiler.h" // ARRAY_SIZE
|
||||||
|
#include "gpio.h" // gpio_out_setup
|
||||||
|
#include "irq.h" // irq_save
|
||||||
|
#include "sam3x8e.h" // Pio
|
||||||
|
#include "sched.h" // sched_shutdown
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Pin mappings
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM))
|
||||||
|
#define GPIO2PORT(PIN) ((PIN) / 32)
|
||||||
|
#define GPIO2BIT(PIN) (1<<((PIN) % 32))
|
||||||
|
|
||||||
|
static Pio * const digital_regs[] = {
|
||||||
|
PIOA, PIOB, PIOC, PIOD
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* gpio functions
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
gpio_peripheral(char bank, uint32_t bit, char ptype, uint32_t pull_up)
|
||||||
|
{
|
||||||
|
Pio *regs = digital_regs[bank - 'A'];
|
||||||
|
if (ptype == 'A')
|
||||||
|
regs->PIO_ABSR &= ~bit;
|
||||||
|
else
|
||||||
|
regs->PIO_ABSR |= bit;
|
||||||
|
if (pull_up)
|
||||||
|
regs->PIO_PUER = bit;
|
||||||
|
else
|
||||||
|
regs->PIO_PUDR = bit;
|
||||||
|
regs->PIO_PDR = bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct gpio_out
|
||||||
|
gpio_out_setup(uint8_t pin, uint8_t val)
|
||||||
|
{
|
||||||
|
if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
|
||||||
|
goto fail;
|
||||||
|
Pio *regs = digital_regs[GPIO2PORT(pin)];
|
||||||
|
uint32_t bit = GPIO2BIT(pin);
|
||||||
|
irqstatus_t flag = irq_save();
|
||||||
|
if (val)
|
||||||
|
regs->PIO_SODR = bit;
|
||||||
|
else
|
||||||
|
regs->PIO_CODR = bit;
|
||||||
|
regs->PIO_OER = bit;
|
||||||
|
regs->PIO_OWER = bit;
|
||||||
|
regs->PIO_PER = 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)
|
||||||
|
{
|
||||||
|
Pio *regs = g.regs;
|
||||||
|
regs->PIO_ODSR ^= g.bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gpio_out_write(struct gpio_out g, uint8_t val)
|
||||||
|
{
|
||||||
|
Pio *regs = g.regs;
|
||||||
|
if (val)
|
||||||
|
regs->PIO_SODR = g.bit;
|
||||||
|
else
|
||||||
|
regs->PIO_CODR = g.bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct gpio_in
|
||||||
|
gpio_in_setup(uint8_t pin, int8_t pull_up)
|
||||||
|
{
|
||||||
|
if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
|
||||||
|
goto fail;
|
||||||
|
uint32_t port = GPIO2PORT(pin);
|
||||||
|
Pio *regs = digital_regs[port];
|
||||||
|
uint32_t bit = GPIO2BIT(pin);
|
||||||
|
irqstatus_t flag = irq_save();
|
||||||
|
PMC->PMC_PCER0 = 1 << (ID_PIOA + port);
|
||||||
|
if (pull_up)
|
||||||
|
regs->PIO_PUER = bit;
|
||||||
|
else
|
||||||
|
regs->PIO_PUDR = bit;
|
||||||
|
regs->PIO_ODR = bit;
|
||||||
|
regs->PIO_PER = bit;
|
||||||
|
irq_restore(flag);
|
||||||
|
return (struct gpio_in){ .regs=regs, .bit=bit };
|
||||||
|
fail:
|
||||||
|
shutdown("Not an output pin");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
gpio_in_read(struct gpio_in g)
|
||||||
|
{
|
||||||
|
Pio *regs = g.regs;
|
||||||
|
return !!(regs->PIO_PDSR & g.bit);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef __SAM3X8E_GPIO_H
|
||||||
|
#define __SAM3X8E_GPIO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void gpio_peripheral(char bank, uint32_t bit, char ptype, uint32_t pull_up);
|
||||||
|
|
||||||
|
struct gpio_out {
|
||||||
|
void *regs;
|
||||||
|
uint32_t bit;
|
||||||
|
};
|
||||||
|
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 {
|
||||||
|
void *regs;
|
||||||
|
uint32_t bit;
|
||||||
|
};
|
||||||
|
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
|
||||||
|
uint8_t gpio_in_read(struct gpio_in g);
|
||||||
|
|
||||||
|
#endif // gpio.h
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef __SAM3X8E_IRQ_H
|
||||||
|
#define __SAM3X8E_IRQ_H
|
||||||
|
// Definitions for irq enable/disable on ARM Cortex-M
|
||||||
|
|
||||||
|
static inline void irq_disable(void) {
|
||||||
|
asm volatile("cpsid i" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void irq_enable(void) {
|
||||||
|
asm volatile("cpsie i" ::: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef unsigned long irqstatus_t;
|
||||||
|
|
||||||
|
static inline irqstatus_t irq_save(void) {
|
||||||
|
irqstatus_t flag;
|
||||||
|
asm volatile("mrs %0, primask" : "=r" (flag) :: "memory");
|
||||||
|
irq_disable();
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void irq_restore(irqstatus_t flag) {
|
||||||
|
asm volatile("msr primask, %0" :: "r" (flag) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // irq.h
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Main starting point for SAM3x8e boards.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include "board/misc.h" // console_get_input
|
||||||
|
#include "sam3x8e.h" // WDT
|
||||||
|
#include "sched.h" // sched_main
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* watchdog handler
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
watchdog_reset(void)
|
||||||
|
{
|
||||||
|
WDT->WDT_CR = 0xA5000001;
|
||||||
|
}
|
||||||
|
DECL_TASK(watchdog_reset);
|
||||||
|
|
||||||
|
static void
|
||||||
|
watchdog_init(void)
|
||||||
|
{
|
||||||
|
uint32_t timeout = 32768 / 2; // 500ms timeout
|
||||||
|
WDT->WDT_MR = WDT_MR_WDRSTEN | WDT_MR_WDV(timeout) | WDT_MR_WDD(timeout);
|
||||||
|
}
|
||||||
|
DECL_INIT(watchdog_init);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* irq clearing on fault
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
// Clear the active irq if a shutdown happened in an irq handler
|
||||||
|
static void
|
||||||
|
clear_active_irq(void)
|
||||||
|
{
|
||||||
|
uint32_t psr;
|
||||||
|
asm volatile("mrs %0, psr" : "=r" (psr));
|
||||||
|
if (!(psr & 0x1ff))
|
||||||
|
// Shutdown did not occur in an irq - nothing to do.
|
||||||
|
return;
|
||||||
|
// Clear active irq status
|
||||||
|
psr &= ~0x1ff;
|
||||||
|
psr |= 1<<24; // T-bit
|
||||||
|
uint32_t temp;
|
||||||
|
asm volatile(
|
||||||
|
" push { %1 }\n"
|
||||||
|
" adr %0, 1f\n"
|
||||||
|
" push { %0 }\n"
|
||||||
|
" push { r0, r1, r2, r3, r12, lr }\n"
|
||||||
|
" bx %2\n"
|
||||||
|
"1:\n"
|
||||||
|
: "=&r"(temp) : "r"(psr), "r"(0xfffffff9));
|
||||||
|
}
|
||||||
|
DECL_SHUTDOWN(clear_active_irq);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* misc functions
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
size_t
|
||||||
|
alloc_maxsize(size_t reqsize)
|
||||||
|
{
|
||||||
|
return reqsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * __visible
|
||||||
|
_sbrk(int nbytes)
|
||||||
|
{
|
||||||
|
extern char _end;
|
||||||
|
static void *heap_ptr = (void *)&_end;
|
||||||
|
void *pos = heap_ptr;
|
||||||
|
heap_ptr = pos + nbytes;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
SystemInit();
|
||||||
|
sched_main();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
// sam3x8e serial port
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <string.h> // memmove
|
||||||
|
#include "autoconf.h" // CONFIG_SERIAL_BAUD
|
||||||
|
#include "board/gpio.h" // gpio_peripheral
|
||||||
|
#include "board/io.h" // readb
|
||||||
|
#include "board/misc.h" // console_get_input
|
||||||
|
#include "sam3x8e.h" // UART
|
||||||
|
#include "sched.h" // DECL_INIT
|
||||||
|
#include "irq.h" // irq_save
|
||||||
|
|
||||||
|
#define SERIAL_BUFFER_SIZE 96
|
||||||
|
static char receive_buf[SERIAL_BUFFER_SIZE];
|
||||||
|
static uint32_t receive_pos;
|
||||||
|
static char transmit_buf[SERIAL_BUFFER_SIZE];
|
||||||
|
static uint32_t transmit_pos, transmit_max;
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Serial hardware
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
serial_init(void)
|
||||||
|
{
|
||||||
|
gpio_peripheral('A', PIO_PA8A_URXD, 'A', 1);
|
||||||
|
gpio_peripheral('A', PIO_PA9A_UTXD, 'A', 0);
|
||||||
|
|
||||||
|
// Reset uart
|
||||||
|
PMC->PMC_PCER0 = 1 << ID_UART;
|
||||||
|
UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
|
||||||
|
UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
|
||||||
|
UART->UART_IDR = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// Enable uart
|
||||||
|
UART->UART_MR = (US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO
|
||||||
|
| UART_MR_CHMODE_NORMAL);
|
||||||
|
UART->UART_BRGR = SystemCoreClock / (16 * CONFIG_SERIAL_BAUD);
|
||||||
|
UART->UART_IER = UART_IER_RXRDY;
|
||||||
|
NVIC_EnableIRQ(UART_IRQn);
|
||||||
|
NVIC_SetPriority(UART_IRQn, 0);
|
||||||
|
UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
|
||||||
|
}
|
||||||
|
DECL_INIT(serial_init);
|
||||||
|
|
||||||
|
void __visible
|
||||||
|
UART_Handler(void)
|
||||||
|
{
|
||||||
|
uint32_t status = UART->UART_SR;
|
||||||
|
if (status & UART_SR_RXRDY) {
|
||||||
|
uint8_t data = UART->UART_RHR;
|
||||||
|
if (receive_pos >= sizeof(receive_buf))
|
||||||
|
// Serial overflow - ignore it as crc error will force retransmit
|
||||||
|
return;
|
||||||
|
receive_buf[receive_pos++] = data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status & UART_SR_TXRDY) {
|
||||||
|
if (transmit_pos >= transmit_max)
|
||||||
|
UART->UART_IDR = UART_IDR_TXRDY;
|
||||||
|
else
|
||||||
|
UART->UART_THR = transmit_buf[transmit_pos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable tx interrupts
|
||||||
|
static void
|
||||||
|
enable_tx_irq(void)
|
||||||
|
{
|
||||||
|
UART->UART_IER = UART_IDR_TXRDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
uint32_t copied = 0;
|
||||||
|
for (;;) {
|
||||||
|
uint32_t rpos = readb(&receive_pos);
|
||||||
|
uint32_t needcopy = rpos - len;
|
||||||
|
if (needcopy) {
|
||||||
|
memmove(&receive_buf[copied], &receive_buf[copied + len]
|
||||||
|
, needcopy - copied);
|
||||||
|
copied = needcopy;
|
||||||
|
}
|
||||||
|
irqstatus_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)
|
||||||
|
{
|
||||||
|
uint32_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);
|
||||||
|
enable_tx_irq();
|
||||||
|
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_irq();
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
// SAM3x8e timer interrupt scheduling
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include "autoconf.h" // CONFIG_CLOCK_FREQ
|
||||||
|
#include "board/misc.h" // timer_from_us
|
||||||
|
#include "command.h" // shutdown
|
||||||
|
#include "irq.h" // irq_disable
|
||||||
|
#include "sam3x8e.h" // TC0
|
||||||
|
#include "sched.h" // sched_timer_kick
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Low level timer code
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
// Return the number of clock ticks for a given number of microseconds
|
||||||
|
uint32_t
|
||||||
|
timer_from_us(uint32_t us)
|
||||||
|
{
|
||||||
|
return us * (CONFIG_CLOCK_FREQ / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IRQ handler
|
||||||
|
void __visible
|
||||||
|
TC0_Handler(void)
|
||||||
|
{
|
||||||
|
TC0->TC_CHANNEL[0].TC_SR; // clear irq pending
|
||||||
|
irq_disable();
|
||||||
|
sched_timer_kick();
|
||||||
|
irq_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
timer_set(uint32_t value)
|
||||||
|
{
|
||||||
|
TC0->TC_CHANNEL[0].TC_RA = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
timer_init(void)
|
||||||
|
{
|
||||||
|
TcChannel *tc = &TC0->TC_CHANNEL[0];
|
||||||
|
// Reset the timer
|
||||||
|
tc->TC_CCR = TC_CCR_CLKDIS;
|
||||||
|
tc->TC_IDR = 0xFFFFFFFF;
|
||||||
|
tc->TC_SR;
|
||||||
|
// Enable it
|
||||||
|
PMC->PMC_PCER0 = 1 << ID_TC0;
|
||||||
|
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1;
|
||||||
|
tc->TC_IER = TC_IER_CPAS;
|
||||||
|
NVIC_SetPriority(TC0_IRQn, 1);
|
||||||
|
NVIC_EnableIRQ(TC0_IRQn);
|
||||||
|
timer_set(1);
|
||||||
|
tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
|
||||||
|
}
|
||||||
|
DECL_INIT(timer_init);
|
||||||
|
|
||||||
|
// Called by main code once every millisecond. (IRQs disabled.)
|
||||||
|
void
|
||||||
|
timer_periodic(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the current time (in absolute clock ticks).
|
||||||
|
uint32_t
|
||||||
|
timer_read_time(void)
|
||||||
|
{
|
||||||
|
return TC0->TC_CHANNEL[0].TC_CV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
uint32_t cur = timer_read_time();
|
||||||
|
uint32_t mintime = cur + TIMER_MIN_TICKS;
|
||||||
|
if (sched_is_before(mintime, next)) {
|
||||||
|
timer_set(next);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
timer_set(mintime);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t timer_repeat;
|
||||||
|
#define TIMER_MAX_REPEAT 400
|
||||||
|
#define TIMER_MAX_NEXT_REPEAT 150
|
||||||
|
|
||||||
|
#define TIMER_MIN_TRY_TICKS timer_from_us(1)
|
||||||
|
#define TIMER_DEFER_REPEAT_TICKS timer_from_us(2)
|
||||||
|
|
||||||
|
// 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 next)
|
||||||
|
{
|
||||||
|
uint32_t now = timer_read_time();
|
||||||
|
int32_t diff = next - now;
|
||||||
|
if (diff > (int32_t)TIMER_MIN_TRY_TICKS)
|
||||||
|
// Schedule next timer normally.
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
// Next timer is in the past or near future - can't reschedule to it
|
||||||
|
uint32_t tr = timer_repeat-1;
|
||||||
|
if (likely(tr)) {
|
||||||
|
irq_enable();
|
||||||
|
timer_repeat = tr;
|
||||||
|
while (diff >= 0) {
|
||||||
|
// Next timer is in the near future - wait for time to occur
|
||||||
|
now = timer_read_time();
|
||||||
|
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 < (int32_t)(-timer_from_us(1000)))
|
||||||
|
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);
|
Loading…
Reference in New Issue