/* Optimized SD Library for Teensy 3.X * Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com * * Development of this SD library was funded by PJRC.COM, LLC by sales of * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop * open source software by purchasing genuine Teensy or other PJRC products. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice, development funding notice, and this permission * notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #if defined(__arm__) #include "SD_t3.h" #ifdef USE_TEENSY3_OPTIMIZED_CODE volatile uint8_t * SDClass::csreg; uint8_t SDClass::csmask; uint8_t SDClass::card_type; #define CMD0_GO_IDLE_STATE 0x4095 // arg=0 #define CMD1_SEND_OP_COND 0x41FF #define CMD8_SEND_IF_COND 0x4887 // arg=0x1AA #define CMD55_APP_CMD 0x77FF #define ACMD41_SD_SEND_OP_COND 0x69FF #define CMD58_READ_OCR 0x7AFF #define CMD17_READ_SINGLE_BLOCK 0x51FF #define CMD24_WRITE_BLOCK 0x58FF uint8_t SDClass::sd_cmd0() { send_cmd(CMD0_GO_IDLE_STATE, 0); uint8_t r1 = recv_r1(); end_cmd(); return r1; } uint32_t SDClass::sd_cmd8() { send_cmd(CMD8_SEND_IF_COND, 0x1AA); uint8_t r1 = recv_r1(); uint32_t cond = 0x80000000; if (r1 == 1) { cond = recv_r3_or_r7(); //Serial.print(cond, HEX); } end_cmd(); return cond; } uint8_t SDClass::sd_acmd41(uint32_t hcs) { //Serial.print("acmd41:"); send_cmd(CMD55_APP_CMD, 0); uint8_t r1 = recv_r1(); end_cmd(); send_cmd(ACMD41_SD_SEND_OP_COND, hcs); r1 = recv_r1(); end_cmd(); return r1; } uint32_t SDClass::sd_cmd58(void) { send_cmd(CMD58_READ_OCR, 0); uint8_t r1 = recv_r1(); uint32_t ocr = 0; if (r1 == 0) ocr = recv_r3_or_r7(); end_cmd(); return ocr; } bool SDClass::sd_read(uint32_t addr, void * data) { //Serial.printf("sd_read %ld\n", addr); if (card_type < 2) addr = addr << 9; send_cmd(CMD17_READ_SINGLE_BLOCK, addr); uint8_t r1 = recv_r1(); if (r1 != 0) { end_cmd(); //Serial.println(" sd_read fail r1"); return false; } while (1) { uint8_t token = SPI.transfer(0xFF); //Serial.printf("t=%02X.", token); if (token == 0xFE) break; if (token != 0xFF) { end_cmd(); //Serial.println(" sd_read fail token"); return false; } // TODO: timeout } uint8_t *p = (uint8_t *)data; uint8_t *end = p + 510; SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); while (p < end) { while (!(SPI0_SR & 0xF0)) ; SPI0_PUSHR = 0xFFFF | SPI_PUSHR_CTAS(1); uint32_t in = SPI0_POPR; *p++ = in >> 8; *p++ = in; } while (!(SPI0_SR & 0xF0)) ; uint32_t in = SPI0_POPR; *p++ = in >> 8; *p++ = in; while (!(SPI0_SR & 0xF0)) ; SPI0_POPR; // ignore crc DIRECT_WRITE_HIGH(csreg, csmask); SPI.transfer(0xFF); return true; // token = 0xFE // data, 512 bytes // crc, 2 bytes } void SDClass::send_cmd(uint16_t cmd, uint32_t arg) { DIRECT_WRITE_LOW(csreg, csmask); SPI.transfer(cmd >> 8); SPI.transfer16(arg >> 16); SPI.transfer16(arg); SPI.transfer(cmd); } uint8_t SDClass::recv_r1(void) { uint8_t ret, count=0; do { ret = SPI.transfer(0xFF); //Serial.print(ret); //Serial.print("."); if ((ret & 0x80) == 0) break; } while (++count < 9); return ret; } uint32_t SDClass::recv_r3_or_r7(void) { uint32_t r; r = SPI.transfer16(0xFFFF) << 16; r |= SPI.transfer16(0xFFFF); return r; } void SDClass::end_cmd(void) { DIRECT_WRITE_HIGH(csreg, csmask); SPI.transfer(0xFF); } #endif #endif