added basic SPI/SD support. To be expanded.
This commit is contained in:
+14
-14
@@ -8,7 +8,7 @@
|
|||||||
// --- Increment in range ---
|
// --- Increment in range ---
|
||||||
// when overflown, wraps within range. Lower bound < upper bound.
|
// when overflown, wraps within range. Lower bound < upper bound.
|
||||||
// ..., upper bound excluded
|
// ..., 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
|
// ..., upper bound included
|
||||||
#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1)
|
#define inc_wrapi(var, min, max) inc_wrap((var), (min), (max) + 1)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
// --- Decrement in range ---
|
// --- Decrement in range ---
|
||||||
// when underflown, wraps within range. Lower bound < upper bound.
|
// when underflown, wraps within range. Lower bound < upper bound.
|
||||||
// ..., upper bound excluded
|
// ..., 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
|
// ..., upper bound included
|
||||||
#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
|
#define dec_wrapi(var, min, max) dec_wrap((var), (min), (max) + 1)
|
||||||
|
|
||||||
@@ -24,10 +24,10 @@
|
|||||||
// --- Bit manipulation --
|
// --- Bit manipulation --
|
||||||
|
|
||||||
// Set bit
|
// 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
|
// 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
|
// Get n-th bit
|
||||||
#define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1)
|
#define get_bit(reg, bit) (((reg) >> (uint8_t)(bit)) & 0x1)
|
||||||
@@ -37,18 +37,18 @@
|
|||||||
#define bit_is_low(reg, bit) (!get_bit(reg, bit))
|
#define bit_is_low(reg, bit) (!get_bit(reg, bit))
|
||||||
|
|
||||||
// Write value to n-th 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
|
// 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 ---
|
// --- Bit manipulation with pointer to variable ---
|
||||||
|
|
||||||
// Set n-th bit in pointee
|
// 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
|
// 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
|
// Get n-th bit in pointee
|
||||||
#define get_bit_p(reg_p, bit) ((*(reg_p) >> (uint8_t)(bit)) & 0x1)
|
#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))
|
#define bit_is_low_p(reg_p, bit) (!get_bit_p(reg_p, bit))
|
||||||
|
|
||||||
// Write value to a bit in pointee
|
// 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 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) do { *(reg_p) ^= (1 << (uint8_t)(bit)); } while(0)
|
#define toggle_bit_p(reg_p, bit) { *(reg_p) ^= (1 << (uint8_t)(bit)); }
|
||||||
|
|
||||||
|
|
||||||
// --- Nibble manipulation ---
|
// --- Nibble manipulation ---
|
||||||
|
|
||||||
// Replace nibble in a byte
|
// Replace nibble in a byte
|
||||||
#define set_low_nibble(reg, value) do { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); } while(0)
|
#define set_low_nibble(reg, value) { (reg) = ((reg) & 0xF0) | ((uint8_t)(value) & 0xF); }
|
||||||
#define set_high_nibble(reg, value) do { (reg) = ((reg) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
|
#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_low_nibble_p(reg_p, value) { *(reg_p) = (*(reg_p) & 0xF0) | ((uint8_t)(value) & 0xF); }
|
||||||
#define set_high_nibble_p(reg_p, value) do { *(reg_p) = (*(reg_p) & 0x0F) | (((uint8_t)(value) & 0xF) << 4); } while(0)
|
#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 low_nibble(x) ((uint8_t)(x) & 0xF)
|
||||||
#define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4)
|
#define high_nibble(x) (((uint8_t)(x) & 0xF0) >> 4)
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Simple FAT16 library.
|
||||||
|
//
|
||||||
|
// To use it, implement BLOCKDEV functions
|
||||||
|
// and attach them to it's instance.
|
||||||
|
//
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -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 <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <avr/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
+10
-1
@@ -6,7 +6,16 @@
|
|||||||
#include "calc.h"
|
#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)
|
void put_str(const STREAM *p, char* str)
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ typedef struct
|
|||||||
} STREAM;
|
} STREAM;
|
||||||
|
|
||||||
|
|
||||||
|
/** Send bytes to stream */
|
||||||
|
void put_bytes(const STREAM *p, const uint8_t* str, const uint16_t len);
|
||||||
|
|
||||||
|
|
||||||
/** Print string into a stream */
|
/** Print string into a stream */
|
||||||
void put_str(const STREAM *p, char* str);
|
void put_str(const STREAM *p, char* str);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user