diff --git a/sd_tests/hardware_spi/hardware_spi.ino b/sd_tests/hardware_spi/hardware_spi.ino new file mode 100644 index 0000000000000000000000000000000000000000..5d0bde4c84b55cfa1a5e53866288433e59f10b45 --- /dev/null +++ b/sd_tests/hardware_spi/hardware_spi.ino @@ -0,0 +1,598 @@ +// +// +// hello.uSD.44.read.c +// +// 19200 baud microSD hello-world +// assumes FAT32 SDHC/XC +// +// Neil Gershenfeld 11/26/18 +// +// (c) Massachusetts Institute of Technology 2018 +// This work may be reproduced, modified, distributed, +// performed, and displayed for any purpose. Copyright is +// retained and must be preserved. The work is provided +// as is; no warranty is provided, and users accept all +// liability. +// + +#include <stdint.h> +#include <string.h> +#include <avr/io.h> +#include <avr/pgmspace.h> +#include <util/delay.h> + +//#include "tinySPI.h" // https://github.com/JChristensen/tinySPI + +#define output(directions,pin) (directions |= pin) // set port direction for output +#define input(directions,pin) (directions &= (~pin)) // set port direction for input +#define set(port,pin) (port |= pin) // set port pin +#define clear(port,pin) (port &= (~pin)) // clear port pin +#define pin_test(pins,pin) (pins & pin) // test for port pin +#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set +#define bit_delay_time 49.5 // bit delay for 19200 with overhead +//#define bit_delay_time 8.5 // bit delay for 115200 with overhead +#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay +#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay +#define char_delay() _delay_ms(10) // char delay +#define SPI_delay() _delay_us(2.5) // SPI delay + +#define serial_port PORTA +#define serial_direction DDRA +#define serial_pins PINA +#define serial_pin_in (1 << PA0) +#define serial_pin_out (1 << PA1) + +#define CS_port PORTA +#define CS_direction DDRA +#define CS_pin (1 << PA3) +#define SCK_port PORTA +#define SCK_direction DDRA +#define SCK_pin (1 << PA4) +#define MOSI_port PORTA +#define MOSI_direction DDRA +#define MOSI_pin (1 << PA6) +#define MISO_port PORTA +#define MISO_direction DDRA +#define MISO_pin (1 << PA5) +#define MISO_pins PINA + +#define DETECT_port PORTA +#define DETECT_direction DDRA +#define DETECT_pin (1 << PA7) + +#define led_port PORTB +#define led_direction DDRB +#define led_pin (1 << PB2) + +// +// put_char +// send character in txchar +// assumes line driver (inverts bits) +// assume use of serial_port, serial_pin_out +// +void put_char(char txchar) { + // + // start bit + // + clear(serial_port,serial_pin_out); + bit_delay(); + // + // unrolled loop to write data bits + // + if bit_test(txchar,0) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,1) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,2) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,3) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,4) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,5) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,6) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,7) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + // + // stop bit + // + set(serial_port,serial_pin_out); + bit_delay(); + // + // char delay + // + bit_delay(); + } + +// +// put_string +// print a null-terminated string +// assumes use of serial_port, serial_pin_out +// +void put_string(char *str) { + static int index; + index = 0; + do { + put_char(str[index]); + ++index; + } while (str[index] != 0); + } + +// +// put_flash_string +// print a null-terminated string from flash +// assumes use of serial_port, serial_pin_out +// +void put_flash_string(const char *string) { + static int index; + index = 0; + do { + put_char(pgm_read_byte(string+index)); + ++index; + } while (pgm_read_byte(string+index) != 0); + } + +// +// put_char_string +// print a string +// assumes use of serial_port, serial_pin_out +// +void put_char_string(uint8_t *string, uint16_t length) { + static int index; + for (index = 0; index < length; ++index) { + put_char(string[index]); + } + } + +// +// put_hex_char +// put a char in hex +void put_hex_char(uint8_t chr) { + static char hex[] = "0123456789ABCDEF"; + put_char(hex[(chr >> 4) & 0x0F]); + put_char(hex[chr & 0x0F]); + } + +// +// put_hex_string +// print a string in hex +// assumes use of serial_port, serial_pin_out +// +void put_hex_string(uint8_t *string, uint16_t length) { + static int index; + for (index = 0; index < length; ++index) { + put_hex_char(string[index]); + } + } + +// +// SPI_write +// write an SPI character and return the response +// +unsigned char SPI_write(uint8_t chr) { + USIDR = chr; + USISR = _BV(USIOIF); + do { + USICR = _BV(USIWM0) | _BV(USICS1) | + _BV(USICLK) | _BV(USITC); + } while ((USISR & _BV(USIOIF)) == 0); + return USIDR; +} + +unsigned char SPI_write_software(uint8_t chr) { + static unsigned char bit,ret; + ret = 0; + // + // bit loop + // + for (bit = 0; bit < 8; ++bit) { + clear(SCK_port,SCK_pin); + if (chr & (1 << (7-bit))) + set(MOSI_port,MOSI_pin); + else + clear(MOSI_port,MOSI_pin); + SPI_delay(); + set(SCK_port,SCK_pin); + SPI_delay(); + if pin_test(MISO_pins,MISO_pin) + ret |= (1 << (7-bit)); + } + // + // finish + // + return ret; + } + +// +// SD_command +// write an SD command and return the response +// +void SD_command(uint8_t command, uint32_t argument, uint8_t CRC, uint8_t *result) { + clear(CS_port,CS_pin); + SPI_write(command); + SPI_write((argument >> 24) & 0xFF); + SPI_write((argument >> 16) & 0xFF); + SPI_write((argument >> 8) & 0xFF); + SPI_write(argument & 0xFF); + SPI_write(CRC); + result[0] = SPI_write(0xFF); + result[1] = SPI_write(0xFF); + result[2] = SPI_write(0xFF); + result[3] = SPI_write(0xFF); + result[4] = SPI_write(0xFF); + result[5] = SPI_write(0xFF); + result[6] = SPI_write(0xFF); + result[7] = SPI_write(0xFF); + set(CS_port,CS_pin); + } + +// +// SD_read +// read size bytes at offset in sector into buffer +// +void SD_read(uint32_t sector,uint16_t offset,uint8_t *buffer,uint16_t size) { + static uint8_t chr; + static uint16_t index,count; + // + // start read + // + clear(CS_port,CS_pin); + // + // send CMD17 + // + SPI_write(0x51); + SPI_write((sector >> 24) & 0xFF); + SPI_write((sector >> 16) & 0xFF); + SPI_write((sector >> 8) & 0xFF); + SPI_write(sector & 0xFF); + SPI_write(0); + // + // loop until 0xFE data token + // + while (1) { + chr = SPI_write(0xFF); + if (chr == 0xFE) + break; + } + // + // read up to offset + // + for (index = 0; index < offset; ++index) { + chr = SPI_write(0xFF); + } + // + // read from offset + // + count = 0; + for (index = offset; index < (offset+size); ++index) { + buffer[count] = SPI_write(0xFF); + count += 1; + } + // + // read up to sector + // + for (index = (offset+size); index < 512; ++index) { + chr = SPI_write(0xFF); + } + // + // read checksum + // + chr = SPI_write(0xFF); + chr = SPI_write(0xFF); + // + // finish read + // + chr = SPI_write(0xFF); + set(CS_port,CS_pin); +} + +void setup() { + // + // main + // + + // + // set clock divider to /1 + // + CLKPR = (1 << CLKPCE); + CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); + + // Configure DETECT_pin as an input and led_pin as an output + DETECT_direction &= ~DETECT_pin; + led_direction |= led_pin; + + // + // initialize output pins + // + set(serial_port,serial_pin_out); + output(serial_direction,serial_pin_out); + set(CS_port,CS_pin); + output(CS_direction,CS_pin); + clear(MOSI_port,MOSI_pin); + output(MOSI_direction,MOSI_pin); + set(SCK_port,SCK_pin); + output(SCK_direction,SCK_pin); + set(MISO_port,MISO_pin); //turn on pull-up + input(MISO_direction,MISO_pin); +} + +void loop() { + static uint8_t count, sectors_per_cluster, FATs, attribute, result[8], buffer[50]; + static uint16_t bytes_per_sector, reserved_sectors, offset, file_cluster_low, file_cluster_hi, + buffer_length, sector_count; + static uint32_t partition, FAT_sectors, fat_sector, root_cluster, root_sector, file_length, + file_cluster, file_sector, chars_read; + + // Wait for card to be inserted + while (PINA & DETECT_pin) { + led_port |= led_pin; + _delay_ms(200); + led_port &= ~led_pin; + _delay_ms(200); + } + led_port |= led_pin; + + // + // put card in SPI mode + // + set(MOSI_port, MOSI_pin); + set(CS_port, CS_pin); + for (count = 0; count < 80; ++count) { + set(SCK_port, SCK_pin); + SPI_delay(); + clear(SCK_port, SCK_pin); + SPI_delay(); + } + set(SCK_port, SCK_pin); + + //SPI.begin(); + //SPI.setDataMode(SPI_MODE1); + + // + // CMD0: reset and enter idle state + // should return 0x01 + // + put_flash_string(PSTR("\r\nreset: 0x")); + SD_command(0x40, 0, 0x95, result); + put_hex_string(result,8); + + // + // CMD8: send interface condition, set SDHC + // should return 0x01000001AA + // + put_flash_string(PSTR("\r\nset interface: 0x")); + SD_command(0x48, 0x000001AA, 0x87, result); + put_hex_string(result,8); + + // + // initialization loop + // + put_flash_string(PSTR("\r\ninitialization:")); + while (1) { + // + // CMD55: application command follows + // should return 0x01 + // + put_flash_string(PSTR("\r\n application command: ")); + SD_command(0x77, 0, 0, result); + put_hex_string(result, 8); + + // + // ACMD41: initialize the card + // should return 0x00 when ready + // + put_flash_string(PSTR("\r\n initialize card: 0x")); + SD_command(0x69, 0x40000000, 0, result); + put_hex_string(result,8); + + // + // check if done + // + if (result[1] == 0) { + put_string("\ncard initialized"); + break; + } + + if (PINA & DETECT_pin) { + put_string("\ncard removed"); + return; + } + } + + // + // read the first partition table + // + put_flash_string(PSTR("\r\nread first partition table:")); + SD_read(0,0x1BE,buffer,32); + put_flash_string(PSTR("\r\n type: 0x")); // 0x0B or 0x0C for FAT32 + put_hex_string(buffer+4,1); + put_flash_string(PSTR("\r\n first sector: 0x")); + put_hex_string(buffer+8,4); + memcpy(&partition,buffer+8,4); + + // + // read the first partition block + // + put_flash_string(PSTR("\r\nread first partition block:")); + SD_read(partition,0,buffer,50); + // + memcpy(&bytes_per_sector,buffer+11,2); + put_flash_string(PSTR("\r\n bytes per sector: 0x")); + put_hex_char((bytes_per_sector >> 8) & 0xFF); + put_hex_char(bytes_per_sector); + // + memcpy(§ors_per_cluster,buffer+13,1); + put_flash_string(PSTR("\r\n sectors per cluster: 0x")); + put_hex_char(sectors_per_cluster); + // + memcpy(&reserved_sectors,buffer+14,2); + put_flash_string(PSTR("\r\n reserved sectors: 0x")); + put_hex_char((reserved_sectors >> 8) & 0xFF); + put_hex_char(reserved_sectors & 0xFF); + // + fat_sector = partition+reserved_sectors; + // + memcpy(&FATs,buffer+16,1); + put_flash_string(PSTR("\r\n FATs: 0x")); + put_hex_char(FATs & 0xFF); + // + memcpy(&FAT_sectors,buffer+36,4); + put_flash_string(PSTR("\r\n FAT sectors: 0x")); + put_hex_char((FAT_sectors >> 24) & 0xFF); + put_hex_char((FAT_sectors >> 16) & 0xFF); + put_hex_char((FAT_sectors >> 8) & 0xFF); + put_hex_char(FAT_sectors & 0xFF); + // + memcpy(&root_cluster,buffer+44,4); + put_flash_string(PSTR("\r\n root cluster: 0x")); + put_hex_char((root_cluster >> 24) & 0xFF); + put_hex_char((root_cluster >> 16) & 0xFF); + put_hex_char((root_cluster >> 8) & 0xFF); + put_hex_char(root_cluster & 0xFF); + + // + // read the root directory + // + root_sector = partition+reserved_sectors+(FATs*FAT_sectors); + put_flash_string(PSTR("\r\nread root directory:")); + offset = 0; + while (1) { + SD_read(root_sector,offset,buffer,32); + memcpy(&attribute,buffer+11,1); + put_flash_string(PSTR("\r\n attribute: 0x")); + put_hex_char(attribute); + if (attribute == 0xF) { + // + // long file name + // + put_flash_string(PSTR("\r\n long file name: 0x")); + put_hex_string(buffer+1,10); + put_hex_string(buffer+14,12); + put_hex_string(buffer+28,4); + put_flash_string(PSTR("\r\n ")); + put_char_string(buffer+1,10); + put_char_string(buffer+14,12); + put_char_string(buffer+28,4); + } + else { + // + // short file name + // + put_flash_string(PSTR("\r\n short file name: 0x")); + put_hex_string(buffer,11); + put_flash_string(PSTR("\r\n ")); + put_char_string(buffer,11); + // + memcpy(&file_cluster_hi,buffer+20,2); + memcpy(&file_cluster_low,buffer+26,2); + file_cluster = (((uint32_t) file_cluster_hi) << 16)+file_cluster_low; + put_flash_string(PSTR("\r\n file cluster: 0x")); + put_hex_char((file_cluster >> 24) & 0xFF); + put_hex_char((file_cluster >> 16) & 0xFF); + put_hex_char((file_cluster >> 8) & 0xFF); + put_hex_char(file_cluster & 0xFF); + // + memcpy(&file_length,buffer+28,4); + put_flash_string(PSTR("\r\n file length: 0x")); + put_hex_char((file_length >> 24) & 0xFF); + put_hex_char((file_length >> 16) & 0xFF); + put_hex_char((file_length >> 8) & 0xFF); + put_hex_char(file_length & 0xFF); + // + // break if not unused + // + if (buffer[0] != 0xE5) + break; + } + offset += 32; + } + + // + // read the file + // + put_flash_string(PSTR("\r\nread first file:\r\n\r\n")); + file_sector = root_sector+(file_cluster-2)*sectors_per_cluster; + buffer_length = 32; + offset = 0; + sector_count = 0; + chars_read = 0; + while (1) { + // + // check file length + // + if ((chars_read+buffer_length) > file_length) { + SD_read(file_sector,offset,buffer,file_length-chars_read); + put_char_string(buffer,file_length-chars_read); + break; + } + SD_read(file_sector,offset,buffer,buffer_length); + put_char_string(buffer,buffer_length); + chars_read += buffer_length; + offset += buffer_length; + + // + // check sector length + // + if (offset == bytes_per_sector) { + // + // sector length reached, check cluster length + // + sector_count += 1; + if (sector_count == sectors_per_cluster) { + // + // cluster length reached, get new cluster from FAT + // + SD_read(fat_sector,4*file_cluster,buffer,4); + memcpy(&file_cluster,buffer,4); + // + // continue + // + file_sector = root_sector+(file_cluster-2)*sectors_per_cluster; + offset = 0; + sector_count = 0; + } + else { + // + // cluster length not reached, continue + // + file_sector += 1; + offset = 0; + } + } + + // + // sector length not reached, continue + // + } + put_flash_string(PSTR("\r\n\r\nend of file\r\n")); + //SPI.end(); + led_port &= ~led_pin; + + while(1) {} +} diff --git a/sd_tests/hardware_spi/notes.txt b/sd_tests/hardware_spi/notes.txt new file mode 100644 index 0000000000000000000000000000000000000000..6eb79ba20b61418cb231d319427a4b45a181ce76 --- /dev/null +++ b/sd_tests/hardware_spi/notes.txt @@ -0,0 +1,25 @@ + +SPI_write just does 8 bits, msb first + +SD command args are command, argument, crc, and output buffer +SD command steps: +- set CS low +- write command byte +- write argument + - most significant byte first +- write crc +- write 8 255s, read 8 bytes from MISO +- set CS high + + +Reset +- command 0x40 +- arg 0x0 +- crc 0x95 +So should see +0x40 +0x00 +0x00 +0x00 +0x00 +0x95 diff --git a/sd_tests/hardware_spi/tinySPI.cpp b/sd_tests/hardware_spi/tinySPI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9dd4c05374f1139390d3e071500e7e7f987d9b1 --- /dev/null +++ b/sd_tests/hardware_spi/tinySPI.cpp @@ -0,0 +1,45 @@ +// Arduino tinySPI Library Copyright (C) 2018 by Jack Christensen and +// licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html +// +// Arduino hardware SPI master library for +// ATtiny24/44/84, ATtiny25/45/85, ATtiny261/461/861, ATtiny2313/4313. +// +// https://github.com/JChristensen/tinySPI +// Jack Christensen 24Oct2013 + +#include "tinySPI.h" + +void tinySPI::begin() +{ + USICR &= ~(_BV(USISIE) | _BV(USIOIE) | _BV(USIWM1)); + USICR |= _BV(USIWM0) | _BV(USICS1) | _BV(USICLK); + SPI_DDR_PORT |= _BV(USCK_DD_PIN); // set the USCK pin as output + SPI_DDR_PORT |= _BV(DO_DD_PIN); // set the DO pin as output + SPI_DDR_PORT &= ~_BV(DI_DD_PIN); // set the DI pin as input +} + +void tinySPI::setDataMode(uint8_t spiDataMode) +{ + if (spiDataMode == SPI_MODE1) + USICR |= _BV(USICS0); + else + USICR &= ~_BV(USICS0); +} + +uint8_t tinySPI::transfer(uint8_t spiData) +{ + USIDR = spiData; + USISR = _BV(USIOIF); // clear counter and counter overflow interrupt flag + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) // ensure a consistent clock period + { + while ( !(USISR & _BV(USIOIF)) ) USICR |= _BV(USITC); + } + return USIDR; +} + +void tinySPI::end() +{ + USICR &= ~(_BV(USIWM1) | _BV(USIWM0)); +} + +tinySPI SPI; diff --git a/sd_tests/hardware_spi/tinySPI.h b/sd_tests/hardware_spi/tinySPI.h new file mode 100644 index 0000000000000000000000000000000000000000..00aa171f2b171c6a68a601cb743a1ce89c20b7e4 --- /dev/null +++ b/sd_tests/hardware_spi/tinySPI.h @@ -0,0 +1,56 @@ +// Arduino tinySPI Library Copyright (C) 2018 by Jack Christensen and +// licensed under GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html +// +// Arduino hardware SPI master library for +// ATtiny24/44/84, ATtiny25/45/85, ATtiny261/461/861, ATtiny2313/4313. +// +// https://github.com/JChristensen/tinySPI +// Jack Christensen 24Oct2013 + +#ifndef TINYSPI_H_INCLUDED +#define TINYSPI_H_INCLUDED + +#include <util/atomic.h> + +// USI ports and pins +// (Thanks to nopnop2002 for adding the definitions for ATtiny461/861 and 2313/4313.) +#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + #define SPI_DDR_PORT DDRA + #define USCK_DD_PIN DDA4 + #define DO_DD_PIN DDA5 + #define DI_DD_PIN DDA6 +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + #define SPI_DDR_PORT DDRB + #define USCK_DD_PIN DDB2 + #define DO_DD_PIN DDB1 + #define DI_DD_PIN DDB0 +#elif defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__) + #define SPI_DDR_PORT DDRB + #define USCK_DD_PIN DDB2 + #define DO_DD_PIN DDB1 + #define DI_DD_PIN DDB0 +#elif defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) + #define SPI_DDR_PORT DDRB + #define USCK_DD_PIN DDB7 + #define DO_DD_PIN DDB6 + #define DI_DD_PIN DDB5 +#else + #error "tinySPI does not support this microcontroller." +#endif + +// SPI data modes +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 + +class tinySPI +{ + public: + static void begin(); + static void setDataMode(uint8_t spiDataMode); + static uint8_t transfer(uint8_t spiData); + static void end(); +}; + +extern tinySPI SPI; + +#endif diff --git a/sd_tests/software_spi/Makefile b/sd_tests/software_spi/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..850b0d2cd4f14fd762af0e6c7a2549837bbdc047 --- /dev/null +++ b/sd_tests/software_spi/Makefile @@ -0,0 +1,31 @@ +PROJECT=read_sd +SOURCES=$(PROJECT).c +MMCU=attiny84 +F_CPU = 20000000 + +CFLAGS=-mmcu=$(MMCU) -Wall -Os -DF_CPU=$(F_CPU) + +$(PROJECT).hex: $(PROJECT).out + avr-objcopy -O ihex $(PROJECT).out $(PROJECT).c.hex;\ + avr-size --mcu=$(MMCU) --format=avr $(PROJECT).out + +$(PROJECT).out: $(SOURCES) + avr-gcc $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES) + +program-bsd: $(PROJECT).hex + avrdude -p t44 -c bsd -U flash:w:$(PROJECT).c.hex + +program-dasa: $(PROJECT).hex + avrdude -p t44 -P /dev/ttyUSB0 -c dasa -U flash:w:$(PROJECT).c.hex + +program-avrisp2: $(PROJECT).hex + avrdude -p t44 -P usb -c avrisp2 -U flash:w:$(PROJECT).c.hex + +program-usbtiny: $(PROJECT).hex + avrdude -p t84 -P usb -c usbtiny -U flash:w:$(PROJECT).c.hex + +program-dragon: $(PROJECT).hex + avrdude -p t44 -P usb -c dragon_isp -U flash:w:$(PROJECT).c.hex + +program-ice: $(PROJECT).hex + avrdude -p t44 -P usb -c atmelice_isp -U flash:w:$(PROJECT).c.hex diff --git a/sd_tests/software_spi/read_sd.c b/sd_tests/software_spi/read_sd.c new file mode 100644 index 0000000000000000000000000000000000000000..4ec1dfba9af82be7169962496be743e5d53c6137 --- /dev/null +++ b/sd_tests/software_spi/read_sd.c @@ -0,0 +1,586 @@ +// +// +// hello.uSD.44.read.c +// +// 19200 baud microSD hello-world +// assumes FAT32 SDHC/XC +// +// Neil Gershenfeld 11/26/18 +// +// (c) Massachusetts Institute of Technology 2018 +// This work may be reproduced, modified, distributed, +// performed, and displayed for any purpose. Copyright is +// retained and must be preserved. The work is provided +// as is; no warranty is provided, and users accept all +// liability. +// + +#include <stdint.h> +#include <string.h> +#include <avr/io.h> +#include <avr/pgmspace.h> +#include <util/delay.h> + +#define output(directions,pin) (directions |= pin) // set port direction for output +#define input(directions,pin) (directions &= (~pin)) // set port direction for input +#define set(port,pin) (port |= pin) // set port pin +#define clear(port,pin) (port &= (~pin)) // clear port pin +#define pin_test(pins,pin) (pins & pin) // test for port pin +#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set +#define bit_delay_time 49.5 // bit delay for 19200 with overhead +//#define bit_delay_time 8.5 // bit delay for 115200 with overhead +#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay +#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay +#define char_delay() _delay_ms(10) // char delay +#define SPI_delay() _delay_us(2.5) // SPI delay + +#define serial_port PORTA +#define serial_direction DDRA +#define serial_pins PINA +#define serial_pin_in (1 << PA0) +#define serial_pin_out (1 << PA1) + +#define CS_port PORTA +#define CS_direction DDRA +#define CS_pin (1 << PA3) +#define SCK_port PORTA +#define SCK_direction DDRA +#define SCK_pin (1 << PA4) +#define MOSI_port PORTA +#define MOSI_direction DDRA +#define MOSI_pin (1 << PA6) +#define MISO_port PORTA +#define MISO_direction DDRA +#define MISO_pin (1 << PA5) +#define MISO_pins PINA + +#define DETECT_port PORTA +#define DETECT_direction DDRA +#define DETECT_pin (1 << PA7) + +#define led_port PORTB +#define led_direction DDRB +#define led_pin (1 << PB2) + +// +// put_char +// send character in txchar +// assumes line driver (inverts bits) +// assume use of serial_port, serial_pin_out +// +void put_char(char txchar) { + // + // start bit + // + clear(serial_port,serial_pin_out); + bit_delay(); + // + // unrolled loop to write data bits + // + if bit_test(txchar,0) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,1) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,2) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,3) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,4) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,5) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,6) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + if bit_test(txchar,7) + set(serial_port,serial_pin_out); + else + clear(serial_port,serial_pin_out); + bit_delay(); + // + // stop bit + // + set(serial_port,serial_pin_out); + bit_delay(); + // + // char delay + // + bit_delay(); + } + +// +// put_string +// print a null-terminated string +// assumes use of serial_port, serial_pin_out +// +void put_string(char *str) { + static int index; + index = 0; + do { + put_char(str[index]); + ++index; + } while (str[index] != 0); + } + +// +// put_flash_string +// print a null-terminated string from flash +// assumes use of serial_port, serial_pin_out +// +void put_flash_string(const char *string) { + static int index; + index = 0; + do { + put_char(pgm_read_byte(string+index)); + ++index; + } while (pgm_read_byte(string+index) != 0); + } + +// +// put_char_string +// print a string +// assumes use of serial_port, serial_pin_out +// +void put_char_string(uint8_t *string, uint16_t length) { + static int index; + for (index = 0; index < length; ++index) { + put_char(string[index]); + } + } + +// +// put_hex_char +// put a char in hex +void put_hex_char(uint8_t chr) { + static char hex[] = "0123456789ABCDEF"; + put_char(hex[(chr >> 4) & 0x0F]); + put_char(hex[chr & 0x0F]); + } + +// +// put_hex_string +// print a string in hex +// assumes use of serial_port, serial_pin_out +// +void put_hex_string(uint8_t *string, uint16_t length) { + static int index; + for (index = 0; index < length; ++index) { + put_hex_char(string[index]); + } + } + +// +// SPI_write +// write an SPI character and return the response +// +unsigned char SPI_write(uint8_t chr) { + static unsigned char bit,ret; + ret = 0; + // + // bit loop + // + for (bit = 0; bit < 8; ++bit) { + clear(SCK_port,SCK_pin); + if (chr & (1 << (7-bit))) + set(MOSI_port,MOSI_pin); + else + clear(MOSI_port,MOSI_pin); + SPI_delay(); + set(SCK_port,SCK_pin); + SPI_delay(); + if pin_test(MISO_pins,MISO_pin) + ret |= (1 << (7-bit)); + } + // + // finish + // + return ret; + } + +// +// SD_command +// write an SD command and return the response +// +void SD_command(uint8_t command, uint32_t argument, uint8_t CRC, uint8_t *result) { + clear(CS_port,CS_pin); + SPI_write(command); + SPI_write((argument >> 24) & 0xFF); + SPI_write((argument >> 16) & 0xFF); + SPI_write((argument >> 8) & 0xFF); + SPI_write(argument & 0xFF); + SPI_write(CRC); + result[0] = SPI_write(0xFF); + result[1] = SPI_write(0xFF); + result[2] = SPI_write(0xFF); + result[3] = SPI_write(0xFF); + result[4] = SPI_write(0xFF); + result[5] = SPI_write(0xFF); + result[6] = SPI_write(0xFF); + result[7] = SPI_write(0xFF); + set(CS_port,CS_pin); + } + +// +// SD_read +// read size bytes at offset in sector into buffer +// +void SD_read(uint32_t sector,uint16_t offset,uint8_t *buffer,uint16_t size) { + static uint8_t chr; + static uint16_t index,count; + // + // start read + // + clear(CS_port,CS_pin); + // + // send CMD17 + // + SPI_write(0x51); + SPI_write((sector >> 24) & 0xFF); + SPI_write((sector >> 16) & 0xFF); + SPI_write((sector >> 8) & 0xFF); + SPI_write(sector & 0xFF); + SPI_write(0); + // + // loop until 0xFE data token + // + while (1) { + chr = SPI_write(0xFF); + if (chr == 0xFE) + break; + } + // + // read up to offset + // + for (index = 0; index < offset; ++index) { + chr = SPI_write(0xFF); + } + // + // read from offset + // + count = 0; + for (index = offset; index < (offset+size); ++index) { + buffer[count] = SPI_write(0xFF); + count += 1; + } + // + // read up to sector + // + for (index = (offset+size); index < 512; ++index) { + chr = SPI_write(0xFF); + } + // + // read checksum + // + chr = SPI_write(0xFF); + chr = SPI_write(0xFF); + // + // finish read + // + chr = SPI_write(0xFF); + set(CS_port,CS_pin); +} + +void sd_loop() { + static uint8_t count, sectors_per_cluster, FATs, attribute, result[8], buffer[50]; + static uint16_t bytes_per_sector, reserved_sectors, offset, file_cluster_low, file_cluster_hi, + buffer_length, sector_count; + static uint32_t partition, FAT_sectors, fat_sector, root_cluster, root_sector, file_length, + file_cluster, file_sector, chars_read; + + // Wait for card to be inserted + while (PINA & DETECT_pin) { + led_port |= led_pin; + _delay_ms(200); + led_port &= ~led_pin; + _delay_ms(200); + } + led_port |= led_pin; + + // + // put card in SPI mode + // + set(MOSI_port, MOSI_pin); + set(CS_port, CS_pin); + for (count = 0; count < 80; ++count) { + set(SCK_port,SCK_pin); + SPI_delay(); + clear(SCK_port,SCK_pin); + SPI_delay(); + } + + // + // CMD0: reset and enter idle state + // should return 0x01 + // + put_flash_string(PSTR("\r\nreset: 0x")); + SD_command(0x40, 0, 0x95, result); + put_hex_string(result,8); + + // + // CMD8: send interface condition, set SDHC + // should return 0x01000001AA + // + put_flash_string(PSTR("\r\nset interface: 0x")); + SD_command(0x48, 0x000001AA, 0x87, result); + put_hex_string(result,8); + + // + // initialization loop + // + put_flash_string(PSTR("\r\ninitialization:")); + while (1) { + // + // CMD55: application command follows + // should return 0x01 + // + put_flash_string(PSTR("\r\n application command: ")); + SD_command(0x77, 0, 0, result); + put_hex_string(result, 8); + + // + // ACMD41: initialize the card + // should return 0x00 when ready + // + put_flash_string(PSTR("\r\n initialize card: 0x")); + SD_command(0x69, 0x40000000, 0, result); + put_hex_string(result,8); + + // + // check if done + // + if (result[1] == 0) { + put_string("\ncard initialized"); + break; + } + + if (PINA & DETECT_pin) { + put_string("\ncard removed"); + return; + } + } + + // + // read the first partition table + // + put_flash_string(PSTR("\r\nread first partition table:")); + SD_read(0,0x1BE,buffer,32); + put_flash_string(PSTR("\r\n type: 0x")); // 0x0B or 0x0C for FAT32 + put_hex_string(buffer+4,1); + put_flash_string(PSTR("\r\n first sector: 0x")); + put_hex_string(buffer+8,4); + memcpy(&partition,buffer+8,4); + + // + // read the first partition block + // + put_flash_string(PSTR("\r\nread first partition block:")); + SD_read(partition,0,buffer,50); + // + memcpy(&bytes_per_sector,buffer+11,2); + put_flash_string(PSTR("\r\n bytes per sector: 0x")); + put_hex_char((bytes_per_sector >> 8) & 0xFF); + put_hex_char(bytes_per_sector); + // + memcpy(§ors_per_cluster,buffer+13,1); + put_flash_string(PSTR("\r\n sectors per cluster: 0x")); + put_hex_char(sectors_per_cluster); + // + memcpy(&reserved_sectors,buffer+14,2); + put_flash_string(PSTR("\r\n reserved sectors: 0x")); + put_hex_char((reserved_sectors >> 8) & 0xFF); + put_hex_char(reserved_sectors & 0xFF); + // + fat_sector = partition+reserved_sectors; + // + memcpy(&FATs,buffer+16,1); + put_flash_string(PSTR("\r\n FATs: 0x")); + put_hex_char(FATs & 0xFF); + // + memcpy(&FAT_sectors,buffer+36,4); + put_flash_string(PSTR("\r\n FAT sectors: 0x")); + put_hex_char((FAT_sectors >> 24) & 0xFF); + put_hex_char((FAT_sectors >> 16) & 0xFF); + put_hex_char((FAT_sectors >> 8) & 0xFF); + put_hex_char(FAT_sectors & 0xFF); + // + memcpy(&root_cluster,buffer+44,4); + put_flash_string(PSTR("\r\n root cluster: 0x")); + put_hex_char((root_cluster >> 24) & 0xFF); + put_hex_char((root_cluster >> 16) & 0xFF); + put_hex_char((root_cluster >> 8) & 0xFF); + put_hex_char(root_cluster & 0xFF); + + // + // read the root directory + // + root_sector = partition+reserved_sectors+(FATs*FAT_sectors); + put_flash_string(PSTR("\r\nread root directory:")); + offset = 0; + while (1) { + SD_read(root_sector,offset,buffer,32); + memcpy(&attribute,buffer+11,1); + put_flash_string(PSTR("\r\n attribute: 0x")); + put_hex_char(attribute); + if (attribute == 0xF) { + // + // long file name + // + put_flash_string(PSTR("\r\n long file name: 0x")); + put_hex_string(buffer+1,10); + put_hex_string(buffer+14,12); + put_hex_string(buffer+28,4); + put_flash_string(PSTR("\r\n ")); + put_char_string(buffer+1,10); + put_char_string(buffer+14,12); + put_char_string(buffer+28,4); + } + else { + // + // short file name + // + put_flash_string(PSTR("\r\n short file name: 0x")); + put_hex_string(buffer,11); + put_flash_string(PSTR("\r\n ")); + put_char_string(buffer,11); + // + memcpy(&file_cluster_hi,buffer+20,2); + memcpy(&file_cluster_low,buffer+26,2); + file_cluster = (((uint32_t) file_cluster_hi) << 16)+file_cluster_low; + put_flash_string(PSTR("\r\n file cluster: 0x")); + put_hex_char((file_cluster >> 24) & 0xFF); + put_hex_char((file_cluster >> 16) & 0xFF); + put_hex_char((file_cluster >> 8) & 0xFF); + put_hex_char(file_cluster & 0xFF); + // + memcpy(&file_length,buffer+28,4); + put_flash_string(PSTR("\r\n file length: 0x")); + put_hex_char((file_length >> 24) & 0xFF); + put_hex_char((file_length >> 16) & 0xFF); + put_hex_char((file_length >> 8) & 0xFF); + put_hex_char(file_length & 0xFF); + // + // break if not unused + // + if (buffer[0] != 0xE5) + break; + } + offset += 32; + } + + // + // read the file + // + put_flash_string(PSTR("\r\nread first file:\r\n\r\n")); + file_sector = root_sector+(file_cluster-2)*sectors_per_cluster; + buffer_length = 32; + offset = 0; + sector_count = 0; + chars_read = 0; + while (1) { + // + // check file length + // + if ((chars_read+buffer_length) > file_length) { + SD_read(file_sector,offset,buffer,file_length-chars_read); + put_char_string(buffer,file_length-chars_read); + break; + } + SD_read(file_sector,offset,buffer,buffer_length); + put_char_string(buffer,buffer_length); + chars_read += buffer_length; + offset += buffer_length; + + // + // check sector length + // + if (offset == bytes_per_sector) { + // + // sector length reached, check cluster length + // + sector_count += 1; + if (sector_count == sectors_per_cluster) { + // + // cluster length reached, get new cluster from FAT + // + SD_read(fat_sector,4*file_cluster,buffer,4); + memcpy(&file_cluster,buffer,4); + // + // continue + // + file_sector = root_sector+(file_cluster-2)*sectors_per_cluster; + offset = 0; + sector_count = 0; + } + else { + // + // cluster length not reached, continue + // + file_sector += 1; + offset = 0; + } + } + + // + // sector length not reached, continue + // + } + put_flash_string(PSTR("\r\n\r\nend of file\r\n")); + led_port &= ~led_pin; +} + +// +// main +// +int main(void) { + // + // main + // + + // + // set clock divider to /1 + // + CLKPR = (1 << CLKPCE); + CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); + + // Configure DETECT_pin as an input and led_pin as an output + DETECT_direction &= ~DETECT_pin; + led_direction |= led_pin; + + // + // initialize output pins + // + set(serial_port,serial_pin_out); + output(serial_direction,serial_pin_out); + set(CS_port,CS_pin); + output(CS_direction,CS_pin); + clear(MOSI_port,MOSI_pin); + output(MOSI_direction,MOSI_pin); + set(SCK_port,SCK_pin); + output(SCK_direction,SCK_pin); + set(MISO_port,MISO_pin); //turn on pull-up + input(MISO_direction,MISO_pin); + + while (1) { + sd_loop(); + } +} diff --git a/sd_tests/software_spi/read_sd.c.hex b/sd_tests/software_spi/read_sd.c.hex new file mode 100644 index 0000000000000000000000000000000000000000..815da62be87ed0e366a951f7fc65b390f61abd1b --- /dev/null +++ b/sd_tests/software_spi/read_sd.c.hex @@ -0,0 +1,229 @@ +:1000000026C140C13FC13EC13DC13CC13BC13AC117 +:1000100039C138C137C136C135C134C133C132C12C +:1000200031C10D0A0D0A656E64206F662066696C29 +:10003000650D0A000D0A7265616420666972737449 +:100040002066696C653A0D0A0D0A000D0A20202011 +:1000500066696C65206C656E6774683A203078005C +:100060000D0A20202066696C6520636C75737465C9 +:10007000723A203078000D0A2020202020202020F5 +:10008000202020202020202020202020000D0A20B9 +:10009000202073686F72742066696C65206E616DD4 +:1000A000653A203078000D0A2020202020202020D2 +:1000B0002020202020202020202020000D0A202089 +:1000C000206C6F6E672066696C65206E616D653AA5 +:1000D000203078000D0A20202061747472696275E6 +:1000E00074653A203078000D0A7265616420726F81 +:1000F0006F74206469726563746F72793A000D0AD7 +:10010000202020726F6F7420636C75737465723A6F +:10011000203078000D0A202020464154207365636A +:10012000746F72733A203078000D0A202020464107 +:1001300054733A203078000D0A20202072657365D0 +:100140007276656420736563746F72733A203078D9 +:10015000000D0A202020736563746F727320706530 +:100160007220636C75737465723A203078000D0AE2 +:100170002020206279746573207065722073656336 +:10018000746F723A203078000D0A726561642066DF +:100190006972737420706172746974696F6E206221 +:1001A0006C6F636B3A000D0A2020206669727374CD +:1001B00020736563746F723A203078000D0A202036 +:1001C00020747970653A203078000D0A7265616498 +:1001D00020666972737420706172746974696F6EDD +:1001E000207461626C653A000D0A202020696E69F6 +:1001F0007469616C697A6520636172643A20307851 +:10020000000D0A2020206170706C69636174696F51 +:100210006E20636F6D6D616E643A20000D0A696E29 +:10022000697469616C697A6174696F6E3A000D0A6C +:1002300073657420696E746572666163653A203017 +:1002400078000D0A72657365743A203078001124C5 +:100250001FBECFE5D2E0DEBFCDBF10E0A0E6B0E02C +:10026000EAEFFDE002C005900D92A239B107D9F77F +:1002700021E0A2E9B0E001C01D92AC30B207E1F785 +:1002800090D5B9C5BDCED998E7EFF0E03197F1F739 +:10029000000080FF02C0D99A01C0D998E7EFF0E0D2 +:1002A0003197F1F7000081FF02C0D99A01C0D998B7 +:1002B000E7EFF0E03197F1F7000082FF02C0D99A32 +:1002C00001C0D998E7EFF0E03197F1F7000083FF24 +:1002D00002C0D99A01C0D998E7EFF0E03197F1F761 +:1002E000000084FF02C0D99A01C0D998E7EFF0E07E +:1002F0003197F1F7000085FF02C0D99A01C0D99863 +:10030000E7EFF0E03197F1F7000086FF02C0D99ADD +:1003100001C0D998E7EFF0E03197F1F7000087FFCF +:1003200002C0D99A01C0D99887EF90E00197F1F700 +:100330000000D99AE7EFF0E03197F1F7000087EF7E +:1003400090E00197F1F700000895CF93DF93EC015F +:1003500010920B0110920A01E0910A01F0910B0139 +:10036000EC0FFD1F80818FDF80910A0190910B01BE +:10037000019690930B0180930A01FE01E80FF91F8B +:1003800080818111E9CFDF91CF910895CF93DF93E1 +:10039000EC011092090110920801E0910801F0911E +:1003A0000901EC0FFD1F84916EDFE0910801F091CF +:1003B00009013196F0930901E0930801EC0FFD1F4C +:1003C000E491E111EACFDF91CF9108950F931F934C +:1003D000CF93DF938C01EB0110920701109206017D +:1003E00080910601909107018C179D0778F4F80120 +:1003F000E80FF91F808147DF809106019091070186 +:1004000001969093070180930601EACFDF91CF9187 +:100410001F910F910895CF93C82FE82FE295EF70A9 +:10042000F0E0E05AFF4F80812EDFCF70EC2FF0E03C +:10043000E05AFF4F8081CF9126CF0F931F93CF9328 +:10044000DF938C01EB011092050110920401809161 +:100450000401909105018C179D0778F4F801E80FCD +:10046000F91F8081D8DF80910401909105010196E8 +:100470009093050180930401EACFDF91CF911F9102 +:100480000F9108951092030110920201282F30E07D +:1004900067E070E0E1E0F0E080910201883088F5EB +:1004A000DC9880910201AB01481B5109D90102C0BF +:1004B000B595A7954A95E2F7A0FF02C0DE9A01C064 +:1004C000DE98B0E1BA95F1F700C0DC9A40E14A95B8 +:1004D000F1F700C090910201CD9B0FC0AB01491B09 +:1004E0005109DF0102C0AA0FBB1F4A95E2F7AD0117 +:1004F00080910301482B409303019F5F9093020179 +:10050000CBCF809103010895BF92CF92DF92EF92FB +:10051000FF920F931F93CF93DF93E42ED52EC62E19 +:10052000B72EF22EE801DB98ADDF8B2DABDF8C2DE3 +:10053000A9DF8D2DA7DF8E2DA5DF8F2DA3DF8FEFF8 +:10054000A1DF88838FEF9EDF89838FEF9BDF8A8314 +:100550008FEF98DF8B838FEF95DF8C838FEF92DFA8 +:100560008D838FEF8FDF8E838FEF8CDF8F83DB9A0E +:10057000DF91CF911F910F91FF90EF90DF90CF907F +:10058000BF900895AF92BF92CF92DF92EF92FF9209 +:100590000F931F93CF93DF93D62EC72EB82EA92E7D +:1005A0007A01E901DB9881E56DDF8A2D6BDF8B2D08 +:1005B00069DF8C2D67DF8D2D65DF80E063DF8FEFD6 +:1005C00061DF8E3FE1F710920101109200018091EE +:1005D0000001909101018E159F0560F48FEF52DFAD +:1005E0008091000190910101019690930101809307 +:1005F0000001EDCF1092FF001092FE00F092010179 +:10060000E09200010E0D1F1D8091000190910101EB +:1006100080179107E8F4E090FE00F090FF00EC0EE8 +:10062000FD1E8FEF2FDFF70180838091FE009091F8 +:10063000FF0001969093FF008093FE0080910001DF +:100640009091010101969093010180930001DCCF0C +:10065000109301010093000180910001909101012C +:100660008115924060F48FEF0DDF80910001909131 +:10067000010101969093010180930001EDCF8FEF6E +:1006800001DF8FEFFFDE8FEFFDDEDB9ADF91CF9191 +:100690001F910F91FF90EF90DF90CF90BF90AF90A0 +:1006A00008954F925F926F927F928F929F92AF9236 +:1006B000BF92CF92DF92EF92FF920F931F93CF9B47 +:1006C00015C0C29A2FEF84E39CE0215080409040F7 +:1006D000E1F700C00000C2982FEF84E39CE02150B6 +:1006E00080409040E1F700C00000E9CFC29ADE9A56 +:1006F000DB9A1092FD008091FD00803580F4DC9A39 +:1007000020E12A95F1F700C0DC9880E18A95F1F7A5 +:1007100000C08091FD008F5F8093FD00ECCF82E4EC +:1007200092E034DE05EF10E025E940E050E0BA0148 +:1007300080E4EADE68E070E0C8017FDE8EE292E0ED +:1007400025DE27E84AEA51E060E070E088E4DCDE7C +:1007500068E070E0C80171DE8CE192E017DE81E0B4 +:1007600092E014DE05EF10E020E040E050E0BA0136 +:1007700087E7CADE68E070E0C8015FDE88EE91E0DE +:1007800005DE20E040E050E060E070E489E6BCDE99 +:1007900068E070E0C80151DE8091F600811104C16B +:1007A00081E790E0D2DD8AEC91E0F0DD00E210E03C +:1007B00023EC30E04EEB51E060E070E0CB01E2DE94 +:1007C0008CEB91E0E3DD61E070E087EC90E035DEFA +:1007D00086EA91E0DBDD64E070E08BEC90E02DDEFA +:1007E0008091CB009091CC00A091CD00B091CE0033 +:1007F0008093BF009093C000A093C100B093C2004B +:1008000088E891E0C3DD6091BF007091C0008091E5 +:10081000C1009091C20002E310E023EC30E040E020 +:1008200050E0B0DE8091CE009091CF009093BE005A +:100830008093BD008EE691E0A9DD8091BE00EBDDE6 +:100840008091BD00E8DD8091D0008093BC0081E5FF +:1008500091E09CDD8091BC00DEDD8091D100909123 +:10086000D2009093BB008093BA0087E391E08EDDC5 +:100870008091BB00D0DD8091BA00CDDD2091BA001F +:100880003091BB008091BF009091C000A091C10049 +:10089000B091C200820F931FA11DB11D8093B600BD +:1008A0009093B700A093B800B093B9008091D300A3 +:1008B0008093B50089E291E069DD8091B500ABDD00 +:1008C0008091E7009091E800A091E900B091EA00E2 +:1008D0008093B1009093B200A093B300B093B400A2 +:1008E00084E191E053DD8091B40095DD8091B30007 +:1008F00092DD8091B2008FDD8091B1008CDD80911E +:10090000EF009091F000A091F100B091F20080937F +:10091000AD009093AE00A093AF00B093B0008EEF07 +:1009200090E034DD8091B00076DD8091AF0073DD22 +:100930008091AE0070DD8091AD006DDD2091BA0038 +:100940003091BB008091BF009091C000A091C10088 +:10095000B091C2006C017D01C20ED31EE11CF11CDE +:100960006091B50070E080E090E02091B10030919E +:10097000B2004091B3005091B40024D2DC01CB010D +:100980008C0D9D1DAE1DBF1D8093A9009093AA00E4 +:10099000A093AB00B093AC0087EE90E0F7DC109230 +:1009A000A8001092A70078C0CF9BD9CE83E890E032 +:1009B0001F910F91FF90EF90DF90CF90BF90AF907D +:1009C0009F908F907F906F905F904F90BECC8DE8FE +:1009D00090E0DCDC6BE070E083EC90E02EDD86E7FD +:1009E00090E0D4DC6BE070E083EC90E0EFDC809191 +:1009F000D7009091D800A0E0B0E0DC0199278827CB +:100A00002091DD003091DE00820F931FA11DB11DEA +:100A10008093A2009093A300A093A400B093A5009C +:100A200080E690E0B3DC8091A500F5DC8091A40025 +:100A3000F2DC8091A300EFDC8091A200ECDC8091DD +:100A4000DF009091E000A091E100B091E20080937E +:100A50009E0090939F00A093A000B093A1008BE410 +:100A600090E094DC8091A100D6DC8091A000D3DCE2 +:100A700080919F00D0DC80919E00CDDC8091C300EE +:100A8000853E09F04EC08091A7009091A800809605 +:100A90009093A8008093A7004091A7005091A800D0 +:100AA0006091A9007091AA008091AB009091AC0078 +:100AB00000E210E023EC30E065DD8091CE00809311 +:100AC000A60084ED90E062DC8091A600A4DC809119 +:100AD000A6008F3009F07BCF8CEB90E057DC6AE00A +:100AE00070E084EC90E0A9DC6CE070E081ED90E0D7 +:100AF000A4DC64E070E08FED90E09FDC86EA90E09B +:100B000045DC6AE070E084EC90E060DC6CE070E072 +:100B100081ED90E05BDC64E070E08FED90E056DC0E +:100B2000B2CF84E390E032DC8091A2009091A300E8 +:100B3000A091A400B091A500BC01CD016250710943 +:100B4000810991092091BC0030E040E050E03AD1A9 +:100B50000091A9001091AA002091AB003091AC0047 +:100B6000DC01CB01800F911FA21FB31F80939A005D +:100B700090939B00A0939C00B0939D0080E290E036 +:100B800090939900809398001092A8001092A7006B +:100B9000109297001092960010929200109293007B +:100BA0001092940010929500009198001091990075 +:100BB0008090920090909300A0909400B090950047 +:100BC000C0909E00D0909F00E090A000F090A10007 +:100BD00024013501400E511E611C711C4091A7007B +:100BE0005091A80060919A0070919B0080919C00A8 +:100BF00090919D00C414D504E604F70430F58601F5 +:100C00000819190923EC30E0BDDC60919E00709159 +:100C10009F008091920090919300681B790B83EC68 +:100C200090E0D4DB82E290E0B1DBC2981F910F919B +:100C3000FF90EF90DF90CF90BF90AF909F908F90FC +:100C40007F906F905F904F90089523EC30E09ADC96 +:100C5000609198007091990083EC90E0B7DB20914F +:100C600098003091990040919200509193006091CA +:100C7000940070919500420F531F611D711D4093A8 +:100C8000920050939300609394007093950080912C +:100C9000A7009091A800820F931F9093A8008093C3 +:100CA000A7002091BD003091BE008217930709F084 +:100CB0007BCF80919600909197000196909397003A +:100CC000809396002091BC0030E08217930709F0D2 +:100CD00050C04091A2005091A300440F551F440FF3 +:100CE000551F6091B6007091B7008091B800909147 +:100CF000B90004E010E023EC30E044DC8091C30054 +:100D00009091C400A091C500B091C6008093A2004C +:100D10009093A300A093A400B093A500BC01CD01C3 +:100D200062507109810991092091BC0030E040E0D6 +:100D300050E048D00091A9001091AA002091AB008A +:100D40003091AC00DC01CB01800F911FA21FB31FBB +:100D500080939A0090939B00A0939C00B0939D0079 +:100D60001092A8001092A70010929700109296007F +:100D70001BCF80919A0090919B00A0919C00B09114 +:100D80009D000196A11DB11D80939A0090939B0038 +:100D9000A0939C00B0939D001092A8001092A70011 +:100DA00003CF80E886BD16BCD798BA9AD99AD19A53 +:100DB000DB9AD39ADE98D69ADC9AD49ADD9AD598A3 +:100DC00070DCFECFEE27FF27AA27BB2708C0A20FA3 +:100DD000B31FE41FF51F220F331F441F551F9695A5 +:100DE00087957795679598F37040A9F7009799F7DD +:0A0DF000BD01CF010895F894FFCF74 +:100DFA003031323334353637383941424344454647 +:100E0A00000A6361726420696E697469616C697A47 +:100E1A006564000A636172642072656D6F76656449 +:020E2A000000C6 +:00000001FF diff --git a/sd_tests/software_spi/read_sd.out b/sd_tests/software_spi/read_sd.out new file mode 100755 index 0000000000000000000000000000000000000000..096853034f53a77fa154bf716d05f22e62598e80 Binary files /dev/null and b/sd_tests/software_spi/read_sd.out differ