diff --git a/.gitignore b/.gitignore index 7e22ad4c5d54c063bc463391a536809af72e30f8..5552569a772a2557b2e9423d295bbe50e6f896ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ -*.swp +*.hex +*.o +*.out *.swo +*.swp .DS_Store build diff --git a/sd_tests/neil/Makefile b/sd_tests/neil/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..850b0d2cd4f14fd762af0e6c7a2549837bbdc047 --- /dev/null +++ b/sd_tests/neil/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/neil/read_sd.c b/sd_tests/neil/read_sd.c new file mode 100644 index 0000000000000000000000000000000000000000..0f707a41a9823f57a0e952668a532b8a1a18be30 --- /dev/null +++ b/sd_tests/neil/read_sd.c @@ -0,0 +1,536 @@ +// +// +// 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 + +// +// 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); +} + +// +// main +// +int main(void) { + // + // main + // + 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; + // + // set clock divider to /1 + // + CLKPR = (1 << CLKPCE); + CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); + // + // 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); + // + // 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) + break; + } + // + // 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")); +}