diff --git a/Makefile b/Makefile index 0201ce5..4e7e320 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ MAIN = avrrfm.c SRC = bitmaps.h colorspace.c dejavu.c display.c font.c i2c.c mcp9808.c rfm69.c \ - spi.c tft.c unifont.c usart.c + sdcard.c spi.c tft.c unifont.c usart.c CC = avr-gcc OBJCOPY = avr-objcopy @@ -48,8 +48,8 @@ OBJ = $(SRC:.S=.o) $(TARGET).elf: bitmaps.h colorspace.h dejavu.h display.h font.h i2c.h \ - mcp9808.h pins.h rfm69.h spi.h tft.h types.h unifont.h usart.h utils.h \ - Makefile + mcp9808.h pins.h rfm69.h sdcard.h spi.h tft.h types.h unifont.h \ + usart.h utils.h Makefile all: $(TARGET).hex diff --git a/avrrfm.c b/avrrfm.c index 7fc454f..d261db1 100644 --- a/avrrfm.c +++ b/avrrfm.c @@ -28,6 +28,7 @@ #include "utils.h" #include "rfm69.h" #include "mcp9808.h" +#include "sdcard.h" #include "tft.h" #include "display.h" #include "dejavu.h" @@ -38,6 +39,7 @@ #define RED 0xf800 #define WHITE 0xffff +#define NODE0 0x12 #define NODE1 0x24 #define NODE2 0x42 @@ -85,6 +87,9 @@ DDR_RFM |= (1 << PIN_RCS); DDR_RFM |= (1 << PIN_RRST); + // set SD card CS as output pin + DDR_SDC |= (1 << PIN_SDCS); + // set display CS, D/C and RST pin as output pin DDR_DISP |= (1 << PIN_DCS); DDR_DISP |= (1 << PIN_DDC); @@ -92,6 +97,7 @@ // drive output pins high PORT_RFM |= (1 << PIN_RCS); + PORT_SDC |= (1 << PIN_SDCS); PORT_DISP |= (1 << PIN_DCS); PORT_DISP |= (1 << PIN_DDC); PORT_DISP |= (1 << PIN_DRST); @@ -101,6 +107,7 @@ * Enables SPI master mode. */ static void initSPI(void) { + // default fOSC/4 SPCR |= (1 << MSTR); SPCR |= (1 << SPE); } @@ -179,7 +186,7 @@ /** * Converts the raw temperature to °C and lets it float around the display. - * + * * @param rssi RSSI value * @param crc CRC result * @param temp temperature + info @@ -214,7 +221,7 @@ /** * Reads and returns the raw temperature + other info. - * + * * @return temperature + info */ static Temperature readTemp(void) { @@ -230,7 +237,7 @@ /** * Handles the payload received from the transmitter. - * + * * @param flags */ static void handlePayload(PayloadFlags flags) { @@ -258,23 +265,67 @@ } } +/** + * Receives data read from SD card. + * + * @param flags + */ +static void receiveData(PayloadFlags flags) { + uint8_t payload[MESSAGE_SIZE + 1]; // + address byte + uint8_t len = readPayload(payload, sizeof (payload)); + + char buf[MESSAGE_SIZE + 1]; + snprintf(buf, len, "%s", payload); + printString(buf); + _delay_ms(10); + + startReceive(); +} + +/** + * Transmits data read from SD card. + */ +static void transmitData(void) { + uint8_t block[SD_BLOCK_SIZE]; + bool read = readSingleBlock(0, block); + if (read) { + void *start = █ + div_t packets = div(SD_BLOCK_SIZE, MESSAGE_SIZE); + for (size_t i = 0; i < packets.quot; i++) { + transmitPayload(start, MESSAGE_SIZE, NODE0); + start += MESSAGE_SIZE; + // a little break in between packets for now + _delay_ms(100); + } + if (packets.rem > 0) { + transmitPayload(start, packets.rem, NODE0); + } + } +} + int main(void) { initUSART(); initPins(); initSPI(); initI2C(); initRadioInt(); + bool sdcard = false; + + printString("Hello Radio!\r\n"); + if (!RECEIVER) { // used only for tx initWatchdog(); initTimer(); + // TODO with the radio breakout on the same SPI bus, + // first SD card init fails, second succeeds always + sdcard = initSDCard(); + if (!sdcard) sdcard = initSDCard(); } // enable global interrupts sei(); - printString("Hello Radio!\r\n"); - uint8_t node = RECEIVER ? NODE1 : NODE2; initRadio(868600, node); if (RECEIVER) { @@ -295,8 +346,12 @@ enableSPI(); wakeTSens(); wakeRadio(); - transmitTemp(NODE1); - waitResponse(); + if (sdcard) { + transmitData(); + } else { + transmitTemp(NODE1); + waitResponse(); + } sleepTSens(); sleepRadio(); disableSPI(); @@ -305,6 +360,7 @@ PayloadFlags flags = payloadReady(); if (flags.ready) { handlePayload(flags); + // receiveData(flags); } } diff --git a/nbproject/Makefile-Custom.mk b/nbproject/Makefile-Custom.mk index 3ff2c88..0ffb8db 100644 --- a/nbproject/Makefile-Custom.mk +++ b/nbproject/Makefile-Custom.mk @@ -36,9 +36,19 @@ # Object Files OBJECTFILES= \ ${OBJECTDIR}/_ext/48b9ad18/avrrfm.o \ + ${OBJECTDIR}/_ext/48b9ad18/bitmaps.o \ + ${OBJECTDIR}/_ext/48b9ad18/colorspace.o \ + ${OBJECTDIR}/_ext/48b9ad18/dejavu.o \ + ${OBJECTDIR}/_ext/48b9ad18/display.o \ + ${OBJECTDIR}/_ext/48b9ad18/font.o \ + ${OBJECTDIR}/_ext/48b9ad18/i2c.o \ ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o \ ${OBJECTDIR}/_ext/48b9ad18/rfm69.o \ - ${OBJECTDIR}/_ext/48b9ad18/spi.o + ${OBJECTDIR}/_ext/48b9ad18/sdcard.o \ + ${OBJECTDIR}/_ext/48b9ad18/spi.o \ + ${OBJECTDIR}/_ext/48b9ad18/tft.o \ + ${OBJECTDIR}/_ext/48b9ad18/unifont.o \ + ${OBJECTDIR}/_ext/48b9ad18/usart.o # C Compiler Flags @@ -69,6 +79,30 @@ ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -std=c99 -o ${OBJECTDIR}/_ext/48b9ad18/avrrfm.o /home/dode/dev/avrrfm/avrrfm.c +${OBJECTDIR}/_ext/48b9ad18/bitmaps.o: /home/dode/dev/avrrfm/bitmaps.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/bitmaps.o /home/dode/dev/avrrfm/bitmaps.c + +${OBJECTDIR}/_ext/48b9ad18/colorspace.o: /home/dode/dev/avrrfm/colorspace.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/colorspace.o /home/dode/dev/avrrfm/colorspace.c + +${OBJECTDIR}/_ext/48b9ad18/dejavu.o: /home/dode/dev/avrrfm/dejavu.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/dejavu.o /home/dode/dev/avrrfm/dejavu.c + +${OBJECTDIR}/_ext/48b9ad18/display.o: /home/dode/dev/avrrfm/display.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/display.o /home/dode/dev/avrrfm/display.c + +${OBJECTDIR}/_ext/48b9ad18/font.o: /home/dode/dev/avrrfm/font.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/font.o /home/dode/dev/avrrfm/font.c + +${OBJECTDIR}/_ext/48b9ad18/i2c.o: /home/dode/dev/avrrfm/i2c.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/i2c.o /home/dode/dev/avrrfm/i2c.c + ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o: /home/dode/dev/avrrfm/mcp9808.c ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/mcp9808.o /home/dode/dev/avrrfm/mcp9808.c @@ -77,10 +111,26 @@ ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/rfm69.o /home/dode/dev/avrrfm/rfm69.c +${OBJECTDIR}/_ext/48b9ad18/sdcard.o: /home/dode/dev/avrrfm/sdcard.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/sdcard.o /home/dode/dev/avrrfm/sdcard.c + ${OBJECTDIR}/_ext/48b9ad18/spi.o: /home/dode/dev/avrrfm/spi.c ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -std=c99 -o ${OBJECTDIR}/_ext/48b9ad18/spi.o /home/dode/dev/avrrfm/spi.c +${OBJECTDIR}/_ext/48b9ad18/tft.o: /home/dode/dev/avrrfm/tft.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/tft.o /home/dode/dev/avrrfm/tft.c + +${OBJECTDIR}/_ext/48b9ad18/unifont.o: /home/dode/dev/avrrfm/unifont.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/unifont.o /home/dode/dev/avrrfm/unifont.c + +${OBJECTDIR}/_ext/48b9ad18/usart.o: /home/dode/dev/avrrfm/usart.c + ${MKDIR} -p ${OBJECTDIR}/_ext/48b9ad18 + $(COMPILE.c) -g -DBAUD=38400 -DDRIVER=1 -DF_CPU=16000000UL -D__AVR_ATmega328P__ -D__flash=volatile -I. -o ${OBJECTDIR}/_ext/48b9ad18/usart.o /home/dode/dev/avrrfm/usart.c + # Subprojects .build-subprojects: diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 1b55900..39dfecd 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -3,11 +3,21 @@ avrrfm.c + bitmaps.c + colorspace.c + dejavu.c + display.c + font.c + i2c.c mcp9808.c mcp9808.h rfm69.c rfm69.h + sdcard.c spi.c + tft.c + unifont.c + usart.c + + + + + + + + + + + + @@ -53,8 +75,16 @@ + + + + + + + + diff --git a/pins.h b/pins.h index 4c23d66..c8cf6ce 100644 --- a/pins.h +++ b/pins.h @@ -33,6 +33,11 @@ #define PIN_RCS PB1 // radio chip select #define PIN_RRST PB0 // radio reset +/* SD Card */ +#define DDR_SDC DDRD +#define PORT_SDC PORTD +#define PIN_SDCS PD4 // sdcard chip select + /* Display */ #define DDR_DISP DDRD #define PORT_DISP PORTD diff --git a/rfm69.c b/rfm69.c index 26d594a..f751713 100644 --- a/rfm69.c +++ b/rfm69.c @@ -1,4 +1,4 @@ -/* +/* * File: rfm69.c * Author: torsten.roemer@luniks.net * @@ -33,7 +33,7 @@ /** * Writes the given value to the given register. - * + * * @param reg * @param value */ @@ -46,7 +46,7 @@ /** * Reads and returns the value of the given register. - * + * * @param reg * @return value */ @@ -76,7 +76,7 @@ /** * Enables timeout (sets timeout interrupt flag on expiration). - * + * * @param enable */ static void timeoutEnable(bool enable) { @@ -89,7 +89,7 @@ */ static void timeout(void) { irqFlags1 |= (1 << 2); - if (timeoutCount++ > MAX_TIMEOUTS) { + if (++timeoutCount > MAX_TIMEOUTS) { measureInts = TRANSMIT_SLOW; timeoutCount = 0; } @@ -124,7 +124,7 @@ // regWrite(BITRATE_MSB, 0x0d); // regWrite(BITRATE_LSB, 0x05); - // frequency deviation (default 5 kHz) - increasing to 10 kHz + // frequency deviation (default 5 kHz) - increasing to 10 kHz // completely removes susceptibility to temperature changes // RX_BW must be increased accordingly regWrite(FDEV_MSB, 0x00); @@ -150,19 +150,19 @@ // regWrite(TEST_LNA, 0x2d); // freq of DC offset canceller and channel filter bandwith (default 10.4 kHz) - // increasing to 20.8 kHz in connection with setting FDEV_*SB to 10 kHz + // increasing to 20.8 kHz in connection with setting FDEV_*SB to 10 kHz // completely removes susceptibility to temperature changes regWrite(RX_BW, 0x54); // RX_BW during AFC (default 0x8b) regWrite(AFC_BW, 0x54); - + // AFC auto on // regWrite(AFC_FEI, 0x04); // RSSI threshold (default, POR 0xff) regWrite(RSSI_THRESH, 0xe4); - + // Preamble size regWrite(PREAMB_MSB, 0x00); regWrite(PREAMB_LSB, 0x0f); @@ -206,13 +206,17 @@ // set TX start condition to "at least one byte in FIFO" regWrite(FIFO_THRESH, 0x8f); - + // Fading Margin Improvement, improved margin, use if AfcLowBetaOn=0 regWrite(TEST_DAGC, 0x30); printString("Radio init done\r\n"); } +void setNodeAddress(uint8_t address) { + regWrite(NODE_ADDR, address); +} + void setOutputPower(uint8_t rssi) { uint8_t pa = 0x40; // -18 dBm with PA1 // adjust power from -2 to +13 dBm @@ -231,10 +235,10 @@ bool wouldTransmit(void) { if (watchdogInts % measureInts == 0) { watchdogInts = 0; - + return true; } - + return false; } @@ -279,7 +283,7 @@ } size_t readPayload(uint8_t *payload, size_t size) { - size_t len = min(regRead(FIFO), FIFO_SIZE) - 1; + size_t len = regRead(FIFO); len = min(len, size); // TODO assume and ignore address for now @@ -292,7 +296,6 @@ } spiDes(); - // FIXME this is not the actual length of the payload received return len; } @@ -304,7 +307,7 @@ do {} while (!(irqFlags2 & (1 << 2)) && !(irqFlags1 & (1 << 2))); bool ready = irqFlags2 & (1 << 2); bool timedout = irqFlags1 & (1 << 2); - + clearIrqFlags(); if (ready) { timeoutEnable(false); @@ -324,8 +327,8 @@ } size_t transmitPayload(uint8_t *payload, size_t size, uint8_t node) { - // payload + address byte - size_t len = min(size, FIFO_SIZE) + 1; + // message + address byte + size_t len = min(size, MESSAGE_SIZE) + 1; spiSel(); transmit(FIFO | 0x80); @@ -343,8 +346,8 @@ loop_until_bit_is_set(irqFlags2, 3); clearIrqFlags(); - + setMode(MODE_STDBY); - + return len; } diff --git a/rfm69.h b/rfm69.h index d0c9c71..bb470e2 100644 --- a/rfm69.h +++ b/rfm69.h @@ -54,6 +54,7 @@ #define SYNC_VAL7 0x35 #define SYNC_VAL8 0x36 #define PCK_CFG1 0x37 +#define PAYLOAD_LEN 0x38 #define NODE_ADDR 0x39 #define CAST_ADDR 0x3a #define AUTO_MODES 0x3b @@ -76,14 +77,14 @@ #define PA_MIN 16 #define PA_MAX 31 -#define FIFO_SIZE 64 +#define MESSAGE_SIZE 63 #define F_STEP 6103515625ULL #define CAST_ADDRESS 0x84 -#define TRANSMIT_FAST 1 // 4 ~ 32 seconds -#define TRANSMIT_SLOW 9 // 38 ~ 5 minutes -#define TIMEOUT_INTS 3 // about 100 milliseconds -#define MAX_TIMEOUTS 9 // slow down tx attempts after so many timeouts +#define TRANSMIT_FAST 4 // 4 ~ 32 seconds +#define TRANSMIT_SLOW 38 // 38 ~ 5 minutes +#define TIMEOUT_INTS 3 // about 100 milliseconds +#define MAX_TIMEOUTS 9 // slow down tx attempts after so many timeouts /** * Initializes the radio module with the given carrier frequency in kilohertz @@ -92,6 +93,13 @@ void initRadio(uint64_t freq, uint8_t node); /** + * Sets the node address. + * + * @param address + */ +void setNodeAddress(uint8_t address); + +/** * Sets the output power based on the given receiver RSSI. * * @param rssi diff --git a/sdcard.c b/sdcard.c new file mode 100644 index 0000000..9411336 --- /dev/null +++ b/sdcard.c @@ -0,0 +1,380 @@ +/* + * File: sdcard.c + * Author: torsten.roemer@luniks.net + * + * Thanks to http://rjhcoding.com/avrc-sd-interface-1.php ff. + * + * Created on 24. Februar 2024, 00:13 + */ + +#include "sdcard.h" + +/** + * Transmits the given command, argument and CRC value. + * + * @param command + * @param argument + * @param crc + */ +static void command(uint8_t command, uint32_t argument, uint8_t crc) { + // 6-bit command, start bit is always 0, transmitter bit is 1 (host command) + transmit(command | 0x40); + + transmit(argument >> 24); + transmit(argument >> 16); + transmit(argument >> 8); + transmit(argument); + + // 7-bit crc, end bit is always 1 + transmit(crc | 0x01); +} + +/** + * Reads an R1 response and returns it. + * + * @return R1 + */ +static uint8_t readR1(void) { + uint8_t i = 0; + uint8_t response; + + // poll up to 8 times for response + while ((response = transmit(0xff)) == 0xff) { + i++; + if (i > 8) { + break; + } + } + + return response; +} + +/** + * Reads an R3/R7 response into the given array of 5 bytes. + * + * @param R3/R7 + */ +static void readR3_7(uint8_t *response) { + // read R1 + response[0] = readR1(); + + // stop reading if R1 indicates an error + if (response[0] > 0x01) { + return; + } + + response[1] = transmit(0xff); + response[2] = transmit(0xff); + response[3] = transmit(0xff); + response[4] = transmit(0xff); +} + +/** + * Selects the SD card to talk to via SPI. + */ +static void sdCardSel(void) { + PORT_SDC &= ~(1 << PIN_SDCS); +} + +/** + * Deselects the SD card to talk to via SPI. + */ +static void sdCardDes(void) { + PORT_SDC |= (1 << PIN_SDCS); +} + +/** + * SPI selects the SD card with extra clocks before and after. + */ +static void select(void) { + transmit(0xff); + sdCardSel(); + transmit(0xff); +} + +/** + * SPI deselects the SD card with extra clocks before and after. + */ +static void deselect(void) { + transmit(0xff); + sdCardDes(); + transmit(0xff); +} + +/** + * Supplies ramp up time. + */ +static void powerOn(void) { + _delay_ms(100); + deselect(); + + // supply at least 74 clocks + for (uint8_t i = 0; i < 10; i++) { + transmit(0xff); + } +} + +/** + * Sends CMD0 to set idle SPI mode and returns the R1 response. + * + * @return R1 + */ +static uint8_t sendIdle(void) { + select(); + + command(CMD0, CMD0_ARG, CMD0_CRC); + uint8_t response = readR1(); + + deselect(); + + return response; +} + +/** + * Sends CMD8 to check version and voltage and reads the R7 response + * into the given array of 5 bytes. + * + * @param R7 + */ +static void sendIfCond(uint8_t *response) { + select(); + + command(CMD8, CMD8_ARG, CMD8_CRC); + readR3_7(response); + + deselect(); +} + +/** + * Sends CMD55 to tell that an app command is next and returns the R1 response. + * + * @return R1 + */ +static uint8_t sendApp(void) { + select(); + + command(CMD55, CMD55_ARG, CMD55_CRC); + uint8_t response = readR1(); + + deselect(); + + return response; +} + +/** + * Sends CMD58 to read the OCR register and reads the R3 response + * into the given array of 5 bytes. + * + * @param R3 + */ +static void sendOCR(uint8_t *response) { + select(); + + command(CMD58, CMD58_ARG, CMD58_CRC); + readR3_7(response); + + deselect(); +} + +/** + * Sends ACMD41 to start initialization and returns the R1 response. + * + * @return R1 + */ +static uint8_t sendOpCond(void) { + select(); + + command(ACMD41, ACMD41_ARG, ACMD41_CRC); + uint8_t response = readR1(); + + deselect(); + + return response; +} + +bool initSDCard(void) { + uint8_t response[5]; + + // power on + powerOn(); + + // CMD0 - go to idle state + response[0] = sendIdle(); + if (response[0] > 0x01) { + printString("SD card error 0\r\n"); + return false; + } + + // CMD8 - send interface condition + sendIfCond(response); + if (response[0] & (1 << SD_CMD_ILLEGAL)) { + printString("SD card is V1.x or not SD card\r\n"); + return false; + } else if (response[0] > 0x01) { + printString("SD card error 8\r\n"); + return false; + } else if (response[4] != 0xaa) { + printString("SD card echo pattern mismatch\r\n"); + return false; + } + + // initialize to ready state + uint8_t attempts = 0; + do { + if (attempts > 100) { + printString("SD card did not become ready\r\n"); + return false; + } + + // CMD55 - send app command + response[0] = sendApp(); + if (response[0] < 0x02) { + // ACMD41 - start initialization + response[0] = sendOpCond(); + } + + _delay_ms(10); + attempts++; + } while (response[0] != SD_SUCCESS); + + // CMD58 - send operation conditions register + sendOCR(response); + if (response[0] > 0x01) { + printString("SD card error 58\r\n"); + return false; + } else if (!(response[1] & 0x80)) { + printString("SD card not ready\r\n"); + return false; + } + + printString("SD card ready\r\n"); + + return true; +} + +bool readSingleBlock(uint32_t address, uint8_t *block) { + select(); + + command(CMD17, address, CMD17_CRC); + uint8_t response = readR1(); + uint8_t token = 0xff; + bool success = false; + + if (response == SD_SUCCESS) { + // read command was successful, wait for start block token + for (uint16_t attempt = 0; attempt < SD_MAX_READ && token == 0xff; attempt++) { + token = transmit(0xff); + } + + if (token == SD_START_BLOCK) { + // start block token received, 512 data bytes follow + for (uint16_t i = 0; i < SD_BLOCK_SIZE; i++) { + block[i] = transmit(0xff); + } + + // 16-bit CRC (ignore for now) + transmit(0xff); + transmit(0xff); + + success = true; + } + } + + deselect(); + + return success; +} + +bool readMultiBlock(uint32_t address, Consumer consume) { + select(); + + command(CMD18, address, CMD18_CRC); + uint8_t response = readR1(); + bool success = false; + bool cont = false; + uint8_t block[SD_BLOCK_SIZE]; + + if (response == SD_SUCCESS) { + // read command was successful + do { + // wait for start block token + uint8_t token = 0xff; + for (uint16_t attempt = 0; attempt < SD_MAX_READ && token == 0xff; attempt++) { + token = transmit(0xff); + } + + if (token == SD_START_BLOCK) { + // start block token received, 512 data bytes follow + for (uint16_t i = 0; i < SD_BLOCK_SIZE; i++) { + block[i] = transmit(0xff); + } + + // 16-bit CRC (ignore for now) + transmit(0xff); + transmit(0xff); + + cont = consume(block); + } else { + break; + } + } while (cont); + + deselect(); + select(); + + command(CMD12, CMD12_ARG, CMD12_CRC); + // stuff byte on CMD12? + transmit(0xff); + response = readR1(); + + if (response == SD_SUCCESS) { + // wait for not busy + uint8_t busy = 0x00; + for (uint16_t attempt = 0; attempt < SD_MAX_READ && busy == 0x00; attempt++) { + busy = transmit(0xff); + } + success = busy != 0x00; + } + } + + deselect(); + + return success; +} + +bool writeSingleBlock(uint32_t address, uint8_t *block) { + select(); + + command(CMD24, address, CMD24_CRC); + uint8_t response = readR1(); + uint8_t token = 0xff; + bool success = false; + + if (response == SD_SUCCESS) { + // write command was successful, send start block token + transmit(SD_START_BLOCK); + + // write 512 data bytes + for (uint16_t i = 0; i < SD_BLOCK_SIZE; i++) { + transmit(block[i]); + } + + // wait for data response + for (uint16_t attempt = 0; attempt < SD_MAX_WRITE && token == 0xff; attempt++) { + token = transmit(0xff); + } + + if ((token & 0x0f) == 0x05) { + // data was accepted, wait while busy programming data + for (uint16_t attempt = 0; attempt < SD_MAX_WRITE && token == 0x00; attempt++) { + token = transmit(0xff); + } + + success = true; + } + } + + deselect(); + + return success; +} diff --git a/sdcard.h b/sdcard.h new file mode 100644 index 0000000..0c83f6d --- /dev/null +++ b/sdcard.h @@ -0,0 +1,101 @@ +/* + * File: sdcard.h + * Author: torsten.roemer@luniks.net + * + * Created on 24. Februar 2024, 00:13 + */ + +#ifndef SDCARD_H +#define SDCARD_H + +#include +#include +#include "pins.h" +#include "spi.h" +#include "usart.h" +#include "types.h" + +#define CMD0 0 +#define CMD0_ARG 0x00000000 +#define CMD0_CRC 0x94 + +#define CMD8 8 +#define CMD8_ARG 0x0000001aa +#define CMD8_CRC 0x86 + +// TODO CRC +#define CMD12 12 +#define CMD12_ARG 0x00000000 +#define CMD12_CRC 0x00 + +#define CMD17 17 +#define CMD17_CRC 0x3b + +#define CMD18 18 +#define CMD18_CRC 0x57 + +#define CMD24 24 +#define CMD24_CRC 0x6f + +#define CMD55 55 +#define CMD55_ARG 0x00000000 +#define CMD55_CRC 0x65 + +#define CMD58 58 +#define CMD58_ARG 0x00000000 +#define CMD58_CRC 0xfd + +#define ACMD41 41 +#define ACMD41_ARG 0x40000000 +#define ACMD41_CRC 0x77 + +// TODO depends on SPI SCK frequency +#define SD_MAX_READ 50000 // SPI clock ticks in 100 ms +#define SD_MAX_WRITE 125000 // SPI clock ticks in 250 ms + +#define SD_BLOCK_SIZE 512 + +#define SD_CMD_ILLEGAL 2 + +#define SD_SUCCESS 0x00 +#define SD_START_BLOCK 0xfe + +/** + * Initializes the SD Card and returns true on success, false otherwise. + * + * @return true on success, false otherwise + */ +bool initSDCard(void); + +/** + * Reads a single block of 512 bytes at the given address into + * the given buffer and returns true on success, false otherwise. + * + * @param address address in 512 byte units + * @param block 512 byte buffer + * @return success + */ +bool readSingleBlock(uint32_t address, uint8_t *block); + +/** + * Reads multiple blocks of 512 bytes starting at the given address + * and, for each block, passes the buffer to the given function. + * By returning false, the function can request to stop reading blocks. + * + * @param address + * @param consume + * @return success + */ +bool readMultiBlock(uint32_t address, Consumer consume); + +/** + * Writes a single block of 512 bytes starting at the given address + * from the given buffer and returns true on success, false otherwise. + * + * @param address address in 512 byte units + * @param block 512 byte buffer + * @return success + */ +bool writeSingleBlock(uint32_t address, uint8_t *block); + +#endif /* SDCARD_H */ diff --git a/spi.c b/spi.c index 65d43ac..92f51af 100644 --- a/spi.c +++ b/spi.c @@ -10,7 +10,7 @@ void spiSlow(void) { SPCR &= ~(1 << SPR0); SPCR |= (1 << SPR1); - SPSR &= ~(1 << SPI2X); + SPSR |= (1 << SPI2X); } void spiFast(void) {