diff --git a/lib/calc.h b/lib/calc.h index 58f27bf..9b74562 100644 --- a/lib/calc.h +++ b/lib/calc.h @@ -8,7 +8,7 @@ // --- Increment in range --- // when overflown, wraps within range. Lower bound < upper bound. // ..., upper bound excluded -#define inc_wrap(var, min, max) do { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } while(0) +#define inc_wrap(var, min, max) { if ((var) >= (max - 1)) { (var) = (min); } else { (var)++; } } // ..., upper bound included #define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1) @@ -16,7 +16,7 @@ // --- Decrement in range --- // when underflown, wraps within range. Lower bound < upper bound. // ..., upper bound excluded -#define dec_wrap(var, min, max) do { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } while(0) +#define dec_wrap(var, min, max) { if ((var) <= (min)) { (var) = (max) - 1; } else { (var)--; } } // ..., upper bound included #define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1) @@ -24,10 +24,10 @@ // --- Bit manipulation -- // Set bit -#define sbi(reg, bit) do { (reg) |= (1 << (uint8_t)(bit)); } while(0) +#define sbi(reg, bit) { (reg) |= (1 << (uint8_t)(bit)); } // Clear bit -#define cbi(reg, bit) do { (reg) &= ~(1 << (uint8_t)(bit)); } while(0) +#define cbi(reg, bit) { (reg) &= ~(1 << (uint8_t)(bit)); } // Get n-th bit #define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1) @@ -37,18 +37,18 @@ #define bit_is_low(reg, bit) (!get_bit(reg, bit)) // Write value to n-th bit -#define set_bit(reg, bit, value) do { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0) +#define set_bit(reg, bit, value) { (reg) = ((reg) & ~(1 << (uint8_t)(bit))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } // Invert n-th bit -#define toggle_bit(reg, bit) do { (reg) ^= (1 << (uint8_t)(bit)); } while(0) +#define toggle_bit(reg, bit) { (reg) ^= (1 << (uint8_t)(bit)); } // --- Bit manipulation with pointer to variable --- // Set n-th bit in pointee -#define sbi_p(reg_p, bit) do { (*(reg_p)) |= (1 << (uint8_t)(bit)); } while(0) +#define sbi_p(reg_p, bit) { (*(reg_p)) |= (1 << (uint8_t)(bit)); } // Clear n-th bit in pointee -#define cbi_p(reg_p, bit) do { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } while(0) +#define cbi_p(reg_p, bit) { (*(reg_p)) &= ~(1 << (uint8_t)(bit)); } // Get n-th bit in pointee #define get_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1) @@ -58,18 +58,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) do { *(reg_p) = (*(reg_p) & ~(1 << ((uint8_t)(bit) & 0x1))) | (((uint8_t)(value) & 0x1) << (uint8_t)(bit)); } while(0) -#define toggle_bit_p(reg_p, bit) do { *(reg_p) ^= (1 << (uint8_t)(bit)); } while(0) +#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)); } // --- Nibble manipulation --- // Replace nibble in a byte -#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(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_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 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 low_nibble(x) ((uint8_t)(x) & 0xF) #define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4) diff --git a/lib/fat16.h b/lib/fat16.h index 477305a..ff5c19e 100644 --- a/lib/fat16.h +++ b/lib/fat16.h @@ -1,5 +1,12 @@ #pragma once +// +// Simple FAT16 library. +// +// To use it, implement BLOCKDEV functions +// and attach them to it's instance. +// + #include #include diff --git a/lib/sd.c b/lib/sd.c new file mode 100644 index 0000000..1fb8462 --- /dev/null +++ b/lib/sd.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +#include "iopins.h" +#include "spi.h" +#include "sd.h" + +#define SD_RESET 0x40 // used to make card enter SPI mode +#define SD_GET_STATUS 0x41 // used to check if card left IDLE - should return 0 +#define SD_SET_BLOCKLEN 0x50 // used to check if card left IDLE - should return 0 +#define SD_READ_BLOCK 0x51 // read single block +#define SD_WRITE_BLOCK 0x58 // write single block + + +bool sd_init() +{ + uint8_t i; + spi_init(); + + spi_ss_disable(); // needed for init sequence, first command will enable it again + + // idle for 10 bytes / 80 clocks + for (i = 0; i < 10; i++) + { + spi_write(0xFF); + } + + + // Send "Go to SPI mode" command, which should return "1" + for (i = 0; i < 100 && sd_command(SD_RESET, 0) != 1; i++) + _delay_ms(10); + + if (i == 100) + return false; // timeout + + + // CMD1 until card comes out of "idle" mode + for (i = 0; i < 100 && sd_command(SD_GET_STATUS, 0) != 0; i++) + _delay_ms(10); + + if (i == 100) + return false; // timeout + + + // Set block size to 512 bytes (SD card default) + sd_command(SD_SET_BLOCKLEN, 512); + + + return true; +} + + +uint8_t sd_command(const uint8_t cmd, const uint32_t arg) +{ + spi_ss_enable(); + + spi_write(cmd); + spi_write(arg >> 24); + spi_write(arg >> 16); + spi_write(arg >> 8); + spi_write(arg); + spi_write(0x95); // CRC for the "init" command, later is ignored + + // Send 8 bytes of 0xFF + // SD card replies with non-0xFF once it's done processing the command + uint8_t i, tmp, ret = 0xFF; + for (i = 0; i < 8; i++) + { + tmp = spi_write(0xFF); + if (tmp != 0xFF) + ret = tmp; + } + + spi_ss_disable(); + + return ret; +} + + +bool sd_read(const uint32_t sector, const uint16_t read_at, uint8_t * buffer, const uint16_t write_at, const uint16_t len) +{ + if (read_at + len > 512) return false; + + uint16_t i; + + spi_ss_enable(); + spi_write(SD_READ_BLOCK); + spi_write(sector >> 15); // sector * 512 >> 24 + spi_write(sector >> 7); // sector * 512 >> 16 + spi_write(sector << 1); // sector * 512 >> 8 + spi_write(0); // sector * 512 + spi_write(0xFF); + + // wait for 0 (ready) + for (i = 0; i < 100 && spi_write(0xFF) != 0x00; i++); + if (i == 100) + { + spi_ss_disable(); + return false; // timeout + } + + + // wait for 0xFE (data start) + for (i = 0; i < 100 && spi_write(0xFF) != 0xFE; i++); + if (i == 100) + { + spi_ss_disable(); + return false; // timeout + } + + // skip "offset" bytes + for (i = 0; i < read_at; i++) + spi_write(0xFF); + + // read "len" bytes + for (i = write_at; i < write_at + len; i++) + buffer[i] = spi_write(0xFF); + + // skip remaining bytes in the sector + for (i = read_at + len; i < 512; i++) + spi_write(0xFF); + + // skip checksum + spi_write(0xFF); + spi_write(0xFF); + + spi_ss_disable(); + + return true; +} + + + +bool sd_write(const uint32_t sector, const uint8_t * buffer512) +{ + uint16_t i; + + spi_ss_enable(); + + spi_write(SD_WRITE_BLOCK); + spi_write(sector >> 15); // sector * 512 >> 24 + spi_write(sector >> 7); // sector * 512 >> 16 + spi_write(sector << 1); // sector * 512 >> 8 + spi_write(0); // sector * 512 + spi_write(0xFF); + + // wait for 0 (ready) + for (i = 0; i < 100 && spi_write(0xFF) != 0x00; i++); + if (i == 100) + { + spi_ss_disable(); + return false; // timeout + } + + // Start of data + spi_write(0xFE); + + // Data + for (i = 0; i < 512; i++) + { + spi_write(buffer512[i]); + } + + // Fake CRC + spi_write(0xFF); + spi_write(0xFF); + + // Should contain flag that data was accepted + uint8_t resp = spi_write(0xFF); + + if ((resp & 0x0F) != 0x05) + { + // Data not accepted + spi_ss_disable(); + return false; + } + else + { + // Data accepted, wait for write complete + for (i = 0; i < 0xFFFF && spi_write(0xFF) == 0x00; i++); + if (i == 0xFFFF) + { + spi_ss_disable(); + return false; // timeout + } + } + + spi_write(0xFF); // 8 clocks + spi_ss_disable(); + + return true; +} diff --git a/lib/sd.h b/lib/sd.h new file mode 100644 index 0000000..cba9f2b --- /dev/null +++ b/lib/sd.h @@ -0,0 +1,55 @@ +#pragma once + +// +// SD card low-level I/O utilities +// +// Inspired by: +// http://www.avrfreaks.net/forum/tutc-simple-fat-and-sd-tutorial +// + +#include +#include +#include +#include +#include + +#include "iopins.h" +#include "spi.h" +#include "uart.h" +#include "stream.h" + +/** Init SD card on SPI */ +bool sd_init(); + + +/** + * Send a command to the SD card + * + * @param cmd command to send + * @param arg command argument + * @return return value on success, 0xFF if nothing received back. + */ +uint8_t sd_command(const uint8_t cmd, const uint32_t arg); + + +/** + * Read from a sector into a buffer memory structure. + * + * @param sector sector to read (512 bytes long each) + * @param read_at offset within the sector + * @param buffer target buffer + * @param write_at target starting address + * @param len number of bytes to read + * @return true on success + */ +bool sd_read(const uint32_t sector, const uint16_t read_at, uint8_t * buffer, const uint16_t write_at, const uint16_t len); + + +/** + * Write bytes from a buffer into a sector. + * + * @param sector sector to write (512 bytes long each) + * @param buffer512 source buffer + * @return true on success + */ +bool sd_write(const uint32_t sector, const uint8_t * buffer512); diff --git a/lib/spi.c b/lib/spi.c new file mode 100644 index 0000000..f6b3608 --- /dev/null +++ b/lib/spi.c @@ -0,0 +1,30 @@ +#include +#include +#include + +#include "iopins.h" +#include "spi.h" + +/** Init SPI (for SD card communication) */ +void spi_init() +{ + // Pin configuration + as_output(PIN_SS); + as_output(PIN_MOSI); + as_output(PIN_SCK); + as_input_pu(PIN_MISO); + + // Enable SPI, master, clock = F_CPU/128 + SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0) | _BV(SPR1); +} + + +/** Write a byte to SPI. Returns received byte. */ +uint8_t spi_write(const uint8_t b) +{ + SPDR = b; + while (!(SPSR & _BV(SPIF))); + + return SPDR; +} + diff --git a/lib/spi.h b/lib/spi.h new file mode 100644 index 0000000..c2feb4f --- /dev/null +++ b/lib/spi.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "iopins.h" + +#define PIN_MISO 12 +#define PIN_MOSI 11 +#define PIN_SCK 13 +#define PIN_SS 10 + +/** Set SS to active state (LOW) */ +#define spi_ss_enable() pin_low(PIN_SS) + +/** Set SS to disabled state (HIGH) */ +#define spi_ss_disable() pin_high(PIN_SS) + + +/** Init SPI (for SD card communication) */ +void spi_init(); + + +/** + * Write / read a byte to SPI. + * + * @param ch the written byte + * @return received byte + */ +uint8_t spi_write(const uint8_t b); + diff --git a/lib/stream.c b/lib/stream.c index c5b0c65..9b88638 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -6,7 +6,16 @@ #include "calc.h" -static char tmpstr[20]; // buffer for number rendering +static char tmpstr[12]; // buffer for number rendering + + +void put_bytes(const STREAM *p, const uint8_t* str, const uint16_t len) +{ + for (uint16_t i = 0; i < len; i++) + { + p->tx(str[i]); + } +} void put_str(const STREAM *p, char* str) diff --git a/lib/stream.h b/lib/stream.h index 95a8a09..9d7ff0f 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -30,6 +30,10 @@ typedef struct } STREAM; +/** Send bytes to stream */ +void put_bytes(const STREAM *p, const uint8_t* str, const uint16_t len); + + /** Print string into a stream */ void put_str(const STREAM *p, char* str);