...
 
Commits (4)
//
//
// 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_hardware(uint8_t chr) {
return SPI.transfer(chr);
}
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 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(&sectors_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;
}
......@@ -319,8 +319,8 @@ void sd_loop() {
//
// put card in SPI mode
//
set(MOSI_port,MOSI_pin);
set(CS_port,CS_pin);
set(MOSI_port, MOSI_pin);
set(CS_port, CS_pin);
for (count = 0; count < 80; ++count) {
set(SCK_port,SCK_pin);
SPI_delay();
......