mid-kid
4 years ago
5 changed files with 263 additions and 5 deletions
@ -1 +0,0 @@ |
|||||
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.c |
|
@ -0,0 +1,145 @@ |
|||||
|
#include "serial.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdint.h> |
||||
|
#include <string.h> |
||||
|
#include <avr/io.h> |
||||
|
#include <avr/interrupt.h> |
||||
|
|
||||
|
#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); } |
@ -1 +0,0 @@ |
|||||
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.h |
|
@ -0,0 +1,37 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
#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); |
@ -1 +0,0 @@ |
|||||
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.c |
|
@ -0,0 +1,32 @@ |
|||||
|
#include "spi.h" |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <avr/io.h> |
||||
|
|
||||
|
#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; |
||||
|
} |
@ -1 +0,0 @@ |
|||||
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.h |
|
@ -0,0 +1,20 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
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); |
@ -1 +0,0 @@ |
|||||
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/utils.h |
|
@ -0,0 +1,29 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <avr/io.h> |
||||
|
|
||||
|
#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) |
Loading…
Reference in new issue