diff --git a/arduino/source/serial.c b/arduino/source/serial.c deleted file mode 120000 index 3f397ae..0000000 --- a/arduino/source/serial.c +++ /dev/null @@ -1 +0,0 @@ -/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.c \ No newline at end of file diff --git a/arduino/source/serial.c b/arduino/source/serial.c new file mode 100644 index 0000000..7474512 --- /dev/null +++ b/arduino/source/serial.c @@ -0,0 +1,145 @@ +#include "serial.h" + +#include +#include +#include +#include +#include + +#include "utils.h" + +#define SERIAL_BUFFER_SIZE 0x40 + +struct serial_buffer { + volatile unsigned char buffer[SERIAL_BUFFER_SIZE]; + volatile unsigned head; + volatile unsigned tail; +}; + +static volatile struct serial_buffer serial_rx; +static volatile struct serial_buffer serial_tx; + +__attribute__((always_inline)) +static inline int serial_buffer_isempty(volatile struct serial_buffer *buffer) +{ + return buffer->head == buffer->tail; +} + +__attribute__((always_inline)) +static inline int serial_buffer_isfull(volatile struct serial_buffer *buffer) +{ + return ((buffer->head + 1) % SERIAL_BUFFER_SIZE) == buffer->tail; +} + +__attribute__((always_inline)) +static inline void serial_buffer_put(volatile struct serial_buffer *buffer, char c) +{ + volatile unsigned head = buffer->head; + buffer->buffer[head++] = c; + buffer->head = head % SERIAL_BUFFER_SIZE; +} + +__attribute__((always_inline)) +static inline char serial_buffer_get(volatile struct serial_buffer *buffer) +{ + volatile unsigned tail = buffer->tail; + char c = buffer->buffer[tail++]; + buffer->tail = tail % SERIAL_BUFFER_SIZE; + return c; +} + +static void serial_transmit(void) +{ + // Called when UDR0 is ready to receive new data + + UDR0 = serial_buffer_get(&serial_tx); + + // If we've sent everything, disable the interrupt + if (serial_buffer_isempty(&serial_tx)) cbi(UCSR0B, UDRIE0); +} + +static void serial_receive(void) +{ + // Called when UDR0 contains new data + + // Discard the byte if a parity error has occurred or the buffer is full + if (bit_is_set(UCSR0A, UPE0) || serial_buffer_isfull(&serial_rx)) { + UDR0; + return; + } + + // There's not much we can do with a full buffer, unfortunately. + // Delaying the read will only cause it to be replaced with further data. + + serial_buffer_put(&serial_rx, UDR0); +} + +ISR(USART_UDRE_vect) { serial_transmit(); } +ISR(USART_RX_vect) { serial_receive(); } + +__attribute__((always_inline)) +inline int serial_putchar_inline(const char c) +{ + // If the data register and buffer are empty, just send it straight away + if (bit_is_set(UCSR0A, UDRE0) && serial_buffer_isempty(&serial_tx)) { + UDR0 = c; + return 1; + } + + // Make sure there's space in the buffer and put the character + while (serial_buffer_isfull(&serial_tx)); + serial_buffer_put(&serial_tx, c); + + // Enable the interrupt to transmit as soon as we can + sbi(UCSR0B, UDRIE0); + + return 1; +} + +int serial_putchar(const char c) { return serial_putchar_inline(c); } + +__attribute__((always_inline)) +inline int serial_getchar_inline(void) +{ + // Wait until there's something in the buffer + while (serial_buffer_isempty(&serial_rx)); + + // Read the next character + return serial_buffer_get(&serial_rx); +} + +int serial_getchar(void) { return serial_getchar_inline(); } + +static int stdio_serial_putchar(const char c, __attribute__((unused)) FILE *stream) +{ return serial_putchar(c); } +static int stdio_serial_getchar(__attribute__((unused)) FILE *stream) +{ return serial_getchar(); } +static FILE serial = FDEV_SETUP_STREAM(stdio_serial_putchar, stdio_serial_getchar, _FDEV_SETUP_RW); + +unsigned serial_available(void) +{ + return ((unsigned)(SERIAL_BUFFER_SIZE + serial_rx.head - serial_rx.tail)) % SERIAL_BUFFER_SIZE; +} + +void serial_drain(void) +{ + while (bit_is_set(UCSR0B, UDRIE0) || bit_is_set(UCSR0A, RXC0)); +} + +void serial_init_config(const unsigned long bauds, const uint8_t config) +{ + // Calculate UBRRn value as per the datasheet + uint16_t ubrr = F_CPU / 4 / 2 / bauds - 1; + UBRR0H = (ubrr >> 8) & 0xFF; + UBRR0L = ubrr & 0xFF; + + // Configure the serial + UCSR0A = _BV(U2X0); // Double speed + UCSR0B = _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); // Enable it and the receive interrupt + UCSR0C = config; + + // Setup stdio streams + stdout = stderr = stdin = &serial; +} + +void serial_init(const unsigned long bauds) { serial_init_config(bauds, SERIAL_8N1); } diff --git a/arduino/source/serial.h b/arduino/source/serial.h deleted file mode 120000 index b56b899..0000000 --- a/arduino/source/serial.h +++ /dev/null @@ -1 +0,0 @@ -/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.h \ No newline at end of file diff --git a/arduino/source/serial.h b/arduino/source/serial.h new file mode 100644 index 0000000..e06f032 --- /dev/null +++ b/arduino/source/serial.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#define SERIAL_5N1 0 +#define SERIAL_6N1 _BV(UCPHA0) +#define SERIAL_7N1 _BV(UDORD0) +#define SERIAL_8N1 _BV(UCPHA0) | _BV(UDORD0) +#define SERIAL_5N2 _BV(USBS0) +#define SERIAL_6N2 _BV(USBS0) | _BV(UCPHA0) +#define SERIAL_7N2 _BV(USBS0) | _BV(UDORD0) +#define SERIAL_8N2 _BV(USBS0) | _BV(UDORD0) | _BV(UCPHA0) +#define SERIAL_5E1 _BV(UPM01) +#define SERIAL_6E1 _BV(UPM01) | _BV(UCPHA0) +#define SERIAL_7E1 _BV(UPM01) | _BV(UDORD0) +#define SERIAL_8E1 _BV(UPM01) | _BV(UCPHA0) | _BV(UDORD0) +#define SERIAL_5E2 _BV(UPM01) | _BV(USBS0) +#define SERIAL_6E2 _BV(UPM01) | _BV(USBS0) | _BV(UCPHA0) +#define SERIAL_7E2 _BV(UPM01) | _BV(USBS0) | _BV(UDORD0) +#define SERIAL_8E2 _BV(UPM01) | _BV(USBS0) | _BV(UDORD0) | _BV(UCPHA0) +#define SERIAL_5O1 _BV(UPM01) | _BV(UPM00) +#define SERIAL_6O1 _BV(UPM01) | _BV(UPM00) | _BV(UCPHA0) +#define SERIAL_7O1 _BV(UPM01) | _BV(UPM00) | _BV(UDORD0) +#define SERIAL_8O1 _BV(UPM01) | _BV(UPM00) | _BV(UCPHA0) | _BV(UDORD0) +#define SERIAL_5O2 _BV(UPM01) | _BV(UPM00) | _BV(USBS0) +#define SERIAL_6O2 _BV(UPM01) | _BV(UPM00) | _BV(USBS0) | _BV(UCPHA0) +#define SERIAL_7O2 _BV(UPM01) | _BV(UPM00) | _BV(USBS0) | _BV(UDORD0) +#define SERIAL_8O2 _BV(UPM01) | _BV(UPM00) | _BV(USBS0) | _BV(UDORD0) | _BV(UCPHA0) + +int serial_putchar_inline(const char c); +int serial_putchar(const char c); +int serial_getchar_inline(void); +int serial_getchar(void); +unsigned serial_available(void); +void serial_drain(void); +void serial_init_config(const unsigned long bauds, const uint8_t config); +void serial_init(const unsigned long bauds); diff --git a/arduino/source/spi.c b/arduino/source/spi.c deleted file mode 120000 index ff5bb1a..0000000 --- a/arduino/source/spi.c +++ /dev/null @@ -1 +0,0 @@ -/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.c \ No newline at end of file diff --git a/arduino/source/spi.c b/arduino/source/spi.c new file mode 100644 index 0000000..36ffaf9 --- /dev/null +++ b/arduino/source/spi.c @@ -0,0 +1,32 @@ +#include "spi.h" + +#include +#include + +#include "utils.h" +#include "pins.h" + +void spi_init(const enum spi_divider divider) +{ + DDRB |= pin(PIN_SPI_SS) | pin(PIN_SPI_MOSI) | pin(PIN_SPI_SCK); // Outputs + DDRB &= ~pin(PIN_SPI_MISO); // Inputs + PORTB |= pin(PIN_SPI_MISO); // Pull it up + + // Configure SPI as master + SPCR = _BV(SPE) | _BV(MSTR) | (divider & 3); + SPSR = (divider & 4) >> 2; +} + +void spi_off(void) +{ + SPCR = 0; + SPSR = 1; +} + +uint8_t spi_transfer(const uint8_t data) +{ + SPDR = data; + __asm__ volatile ("nop"); + while (!(SPSR & _BV(SPIF))) __asm__ volatile ("nop"); + return SPDR; +} diff --git a/arduino/source/spi.h b/arduino/source/spi.h deleted file mode 120000 index bfdf735..0000000 --- a/arduino/source/spi.h +++ /dev/null @@ -1 +0,0 @@ -/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.h \ No newline at end of file diff --git a/arduino/source/spi.h b/arduino/source/spi.h new file mode 100644 index 0000000..77fa438 --- /dev/null +++ b/arduino/source/spi.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +enum spi_divider { + SPI_DIV4, + SPI_DIV16, + SPI_DIV64, + SPI_DIV128, + + // Double speed + SPI_DIV2, + SPI_DIV8, + SPI_DIV32, + SPI_DIV64_2 +}; + +void spi_init(const enum spi_divider divider); +void spi_off(void); +uint8_t spi_transfer(const uint8_t data); diff --git a/arduino/source/utils.h b/arduino/source/utils.h deleted file mode 120000 index 7a808f2..0000000 --- a/arduino/source/utils.h +++ /dev/null @@ -1 +0,0 @@ -/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/utils.h \ No newline at end of file diff --git a/arduino/source/utils.h b/arduino/source/utils.h new file mode 100644 index 0000000..f3420a4 --- /dev/null +++ b/arduino/source/utils.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#define cycles(us) (F_CPU * (long long)(us) / 1000000) + +#define cbi(sfr, bit) (sfr &= ~_BV(bit)) +#define sbi(sfr, bit) (sfr |= _BV(bit)) + +// Stupid definitions for handling pins. + +#define _pin(port, pin) PORT ## port ## pin +#define _port(port, pin) PORT ## port +#define pin(pin) _BV(_pin(pin)) +#define port(pin) _port(pin) + +#define LOW 0 +#define HIGH 1 +#define OUTPUT 1 +#define INPUT 0 + +#define _pinmode(port, pin, x) if (x) { sbi(DDR ## port, DD ## port ## pin); } else { cbi(DDR ## port, DD ## port ## pin); } +#define pinmode(pin, x) _pinmode(pin, x) + +#define _writepin(port, pin, x) if (x) { sbi(PORT ## port, PORT ## port ## pin); } else { cbi(PORT ## port, PORT ## port ## pin); } +#define writepin(pin, x) _writepin(pin, x) + +#define _readpin(port, pin) ((PIN ## port >> PORT ## port ## pin) & 1) +#define readpin(pin) _readpin(pin)