diff --git a/Makefile b/Makefile index 3aba519..72cc337 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ BINARY = main OBJS = $(BINARY).o OBJS += lib/usart.o OBJS += lib/iopins.o +OBJS += lib/spi.o # Dirs with header files INCL_DIRS = . lib/ diff --git a/avr-c-bp.pro b/avr-c-bp.pro index 830c741..8bfc897 100644 --- a/avr-c-bp.pro +++ b/avr-c-bp.pro @@ -18,12 +18,15 @@ DISTFILES += \ HEADERS += \ lib/calc.h \ lib/iopins.h \ - lib/usart.h + lib/usart.h \ + lib/nsdelay.h \ + lib/spi.h SOURCES += \ lib/iopins.c \ main.c \ - lib/usart.c + lib/usart.c \ + lib/spi.c # === Flags for the Clang code model=== # diff --git a/lib/calc.h b/lib/calc.h index 2d150e6..ff039e6 100644 --- a/lib/calc.h +++ b/lib/calc.h @@ -10,7 +10,7 @@ // --- Increment in range --- // when overflown, wraps within range. Lower bound < upper bound. // ..., upper bound excluded -#define inc_wrap(var, min, max) { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } +#define inc_wrap(var, min, max) do { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } while(0) // ..., upper bound included #define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1) @@ -18,7 +18,7 @@ // --- Decrement in range --- // when underflown, wraps within range. Lower bound < upper bound. // ..., upper bound excluded -#define dec_wrap(var, min, max) { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } +#define dec_wrap(var, min, max) do { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } while(0) // ..., upper bound included #define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1) @@ -26,31 +26,31 @@ // --- Bit manipulation -- // Set bit -#define sbi(reg, bit) { (reg) |= (1 << (uint8_t)(bit)); } +#define sbi(reg, bit) do { (reg) |= (1 << (uint8_t)(bit)); } while(0) // Clear bit -#define cbi(reg, bit) { (reg) &= ~(1 << (uint8_t)(bit)); } +#define cbi(reg, bit) do { (reg) &= ~(1 << (uint8_t)(bit)); } while(0) // Get n-th bit -#define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1) +#define get_bit(reg, bit) ((bool)((reg) >> (uint8_t)(bit))) -// Test n-th bit (Can't use bit_is_set, as it's redefined in sfr_def.h) +// Test n-th bit (Can't use bit_is_set, as it's defined in sfr_def.h) #define bit_is_high(reg, bit) get_bit(reg, bit) #define bit_is_low(reg, bit) (!get_bit(reg, bit)) // Write value to n-th bit -#define set_bit(reg, bit, value) { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } +#define set_bit(reg, bit, value) do { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(bool)(value)) << (uint8_t)(bit)); } while(0) // Invert n-th bit -#define toggle_bit(reg, bit) { (reg) ^= (1 << (uint8_t)(bit)); } +#define toggle_bit(reg, bit) do { (reg) ^= (1 << (uint8_t)(bit)); } while(0) // --- Bit manipulation with pointer to variable --- // Set n-th bit in pointee -#define sbi_p(reg_p, bit) { (*(reg_p)) |= (1 << (uint8_t)(bit)); } +#define sbi_p(reg_p, bit) do { (*(reg_p)) |= (1 << (uint8_t)(bit)); } while(0) // Clear n-th bit in pointee -#define cbi_p(reg_p, bit) { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } +#define cbi_p(reg_p, bit) do { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } while(0) // Get n-th bit in pointee #define get_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1) @@ -60,18 +60,18 @@ #define bit_is_low_p(reg_p, bit) (!get_bit_p(reg_p, bit)) // Write value to a bit in pointee -#define set_bit_p(reg_p, bit, value) { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bit) & 0x1))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } -#define toggle_bit_p(reg_p, bit) { *(reg_p) ^= (1 << (uint8_t)(bit)); } +#define set_bit_p(reg_p, bit, value) do { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bool)(bit)))) | (((uint8_t)(bool)(value)) << (uint8_t)(bit)); } while(0) +#define toggle_bit_p(reg_p, bit) do { *(reg_p) ^= (1 << (uint8_t)(bit)); } while(0) // --- Nibble manipulation --- // Replace nibble in a byte -#define set_low_nibble(reg, value) { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } -#define set_high_nibble(reg, value) { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } +#define set_low_nibble(reg, value) do { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0) +#define set_high_nibble(reg, value) do { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0) -#define set_low_nibble_p(reg_p, value) { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); } -#define set_high_nibble_p(reg_p, value) { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } +#define set_low_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0) +#define set_high_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0) #define low_nibble(x) ((uint8_t)(x) & 0xF) #define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4) diff --git a/lib/spi.c b/lib/spi.c new file mode 100644 index 0000000..4315fb1 --- /dev/null +++ b/lib/spi.c @@ -0,0 +1,62 @@ +#include +#include +#include + +#include "iopins.h" +#include "spi.h" + + +/** Init SPI (for SD card communication) */ +void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv) +{ + as_output(PIN_SS); // SS output - we control slave + as_output(PIN_MOSI); // MOSI output - we talk to slave + as_output(PIN_SCK); // SCK output - we're generating clock + // MISO is configured automatically as input + + SPCR = 0; + SPCR |= (1 << MSTR); + SPCR |= (order << DORD); + SPCR |= (cpol << CPOL); + SPCR |= (cpha << CPHA); + + // speed + SPCR |= (clkdiv & 0b11); + SPSR = (bool)(clkdiv & 0b100); // write SPI2X flag + + // enable SPI + SPCR |= (1 << SPE); +} + + +/** Init SPI (for SD card communication) */ +void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha) +{ + as_output(PIN_MISO); // we're listening to master + // MOSI, SS, SCK are configured automatically + + SPCR = 0; + SPCR |= (order << DORD); + SPCR |= (cpol << CPOL); + SPCR |= (cpha << CPHA); + + // enable SPI + SPCR |= (1 << SPE); +} + +/** Write a byte to SPI. Returns received byte. */ +uint8_t spi_send(uint8_t byte) +{ + SPDR = byte; + while (bit_is_low(SPSR, SPIF)); + + return SPDR; +} + +/** Receive (as slave). Blocking. */ +uint8_t spi_receive(uint8_t reply) +{ + SPDR = reply; + while (bit_is_low(SPSR, SPIF)); + return SPDR; +} diff --git a/lib/spi.h b/lib/spi.h new file mode 100644 index 0000000..0d9546d --- /dev/null +++ b/lib/spi.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include "calc.h" +#include "iopins.h" + +#define PIN_MISO 12 +#define PIN_MOSI 11 +#define PIN_SCK 13 +#define PIN_SS 10 + +/** Bit order */ +enum SPI_order { + SPI_LSB_FIRST = 0, + SPI_MSB_FIRST = 1 +}; + +/** Clock polarity */ +enum SPI_cpol { + CPOL_0 = 0, + CPOL_1 = 1 +}; + +/** Clock phase */ +enum SPI_cpha { + CPHA_0 = 0, + CPHA_1 = 1 +}; + +/** Clock prescaller */ +enum SPI_clk_div { + SPI_DIV_2 = 0b100, // 2x (master only, can't receive at this speed) + SPI_DIV_4 = 0b000, + SPI_DIV_8 = 0b101, // 2x + SPI_DIV_16 = 0b001, + SPI_DIV_32 = 0b110, // 2x + SPI_DIV_64 = 0b010, + SPI_DIV_128 = 0b011 +}; + + +/** Set SS to active state (LOW) */ +#define spi_ss_enable() pin_down(PIN_SS) + + +/** Set SS to disabled state (HIGH) */ +#define spi_ss_disable() pin_up(PIN_SS) + + +/** Enable SPI ISR */ +#define spi_isr_enable(yes) set_bit(SPCR, SPIE, yes) + + +/** Init SPI (for SD card communication) */ +void spi_init_master(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha, enum SPI_clk_div clkdiv); + + +/** Init SPI (for SD card communication) */ +void spi_init_slave(enum SPI_order order, enum SPI_cpol cpol, enum SPI_cpha cpha); + + +/** Write a byte to SPI. Returns received byte. */ +uint8_t spi_send(uint8_t byte); + + +/** Receive (as slave). Blocking. */ +uint8_t spi_receive(uint8_t reply); diff --git a/lib/usart.c b/lib/usart.c index 5137092..e9e020a 100644 --- a/lib/usart.c +++ b/lib/usart.c @@ -66,7 +66,7 @@ void usart_puts_P(const char* str) /** Clear receive buffer */ -void usart_clear_rx(void) +void usart_flush_rx(void) { uint8_t dummy; while (bit_is_high(UCSR0A, RXC0)) diff --git a/lib/usart.h b/lib/usart.h index 6807588..ae07420 100644 --- a/lib/usart.h +++ b/lib/usart.h @@ -32,28 +32,28 @@ enum { BAUD_1M = 0, }; -/** Init UART with a UBRR value */ +/** Init UART with a UBRR value - can use the BAUD_* constants for 16 MHz */ void usart_init(uint16_t ubrr); /** Check if there's a byte in the RX register */ -#define usart_rx_ready() (0 != (UCSR0A & (1 << RXC0))) +#define usart_rx_ready() bit_is_high(UCSR0A, RXC0) -/** Check if transmission of everything is done */ -#define usart_tx_ready() (0 != (UCSR0A & (1 << UDRE0))) +/** Check if USART is ready to accept new byte to send */ +#define usart_tx_ready() bit_is_low(UCSR0A, UDRE0) // ---- Enable UART interrupts ------------ /** Enable or disable RX ISR */ -#define usart_rx_isr_enable(yes) set_bit(UCSR0B, RXCIE0, (yes)) +#define usart_isr_rx_enable(yes) set_bit(UCSR0B, RXCIE0, (yes)) -/** Enable or disable TX ISR (1 byte is sent) */ -#define usart_tx_isr_enable(yes) set_bit(UCSR0B, TXCIE0, (yes)) +/** Enable or disable TX ISR (all data sent) */ +#define usart_isr_tx_enable(yes) set_bit(UCSR0B, TXCIE0, (yes)) -/** Enable or disable DRE ISR (all is sent) */ -#define usart_dre_isr_enable(yes) set_bit(UCSR0B, UDRIE0, (yes)) +/** Enable or disable DRE ISR (data register empty) */ +#define usart_isr_dre_enable(yes) set_bit(UCSR0B, UDRIE0, (yes)) // ---- Basic IO -------------------------- @@ -66,12 +66,8 @@ void usart_tx(uint8_t data); uint8_t usart_rx(void); -/** Send byte over UART */ -#define usart_putc(data) usart_tx((data)) - - /** Clear receive buffer */ -void usart_clear_rx(void); +void usart_flush_rx(void); // ---- Strings --------------------------- @@ -80,5 +76,5 @@ void usart_clear_rx(void); void usart_puts(const char* str); -/** Send progmem string over UART */ +/** Send progmem string `PSTR("foobar")` over UART */ void usart_puts_P(const char* str); diff --git a/main.c b/main.c index e096ee8..a158a6f 100644 --- a/main.c +++ b/main.c @@ -27,7 +27,7 @@ ISR(USART_RX_vect) void main() { usart_init(BAUD_9600); - usart_rx_isr_enable(true); // enable RX interrupt handler + usart_isr_rx_enable(true); // enable RX interrupt handler // configure pins as_output(LED);