diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2016-06-14 14:27:30 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2016-06-14 14:27:30 -0400 |
commit | cc62a3dbf387dffe2d7e836c64cb5f5169971f05 (patch) | |
tree | 2ac823250c391a4883e7374616adf6d8eba33982 /src/sam3x8e/serial.c | |
parent | 31c04261c14b7ab7cca2d191d294c3efa326c8f1 (diff) | |
download | kutter-cc62a3dbf387dffe2d7e836c64cb5f5169971f05.tar.gz kutter-cc62a3dbf387dffe2d7e836c64cb5f5169971f05.tar.xz kutter-cc62a3dbf387dffe2d7e836c64cb5f5169971f05.zip |
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>
Diffstat (limited to 'src/sam3x8e/serial.c')
-rw-r--r-- | src/sam3x8e/serial.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/sam3x8e/serial.c b/src/sam3x8e/serial.c new file mode 100644 index 00000000..42b92efe --- /dev/null +++ b/src/sam3x8e/serial.c @@ -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(); +} |