Правильный ли этот код для инициализации SD-карты в режиме SPI? - PullRequest
1 голос
/ 30 мая 2019

Я хочу инициализировать SD-карту вручную с Arduino Mega 2560 и прочитать ее содержимое.

Я прочитал много руководств, объясняющих, как это правильно делать, а также код библиотеки SD для Arduino, но я не могу заставить его работать.

uint8_t spi_byte(uint8_t byte) {
  SPDR = byte;
  asm volatile("nop");
  while(!(SPSR & B10000000));
  uint8_t response = SPDR;
  return response;
}

uint8_t sd_cmd(uint8_t cmd, uint32_t args) {
  if(cmd != 0x40) while(spi_byte(0xFF) != 0xFF);
  spi_byte(cmd);
  int8_t c;
  for(c = 3; c >= 0; --c) spi_byte(args >> (c << 3));
  uint8_t crc;
  if(cmd == 0x40) crc = 0x95;
  else if(cmd == 0x48) crc = 0x87;
  else crc = 0xFF;
  spi_byte(crc);
  uint8_t response;
  for(c = 16; ((response = spi_byte(0xFF)) & 0x80) && c; --c);
  /*if((cmd < 0x51) || (cmd > 0x59))*/ spi_byte(0xFF); //S
  return response;
}

void sd_init() {
  DDRB &= B11110000;
  PORTB |= B0001; //set CS pull-up resistor (just-in-case)
  DDRB |= B0001; //set CS to output
  PORTB |= B1000; //set MISO pull-up resistor (just-in-case)
  SPCR = B01010010; //no interrupt, MSB first, master, mode 0, fosc/64 (250 kHz)
  SPSR &= ~1; //clear SPI double speed
  DDRB |= B0110; //set SCK and MOSI to output
  delayMicroseconds(200000);
  uint8_t c;
  cli();
  for(c = 0; c < 10; ++c) spi_byte(0xFF); //synchronize clock
  clb(PORTB, 0); //set CS low
  uint8_t response;
  uint16_t timeout = 1024;
  while((response=sd_cmd(0x40, 0)) != 0x1) { //CMD0: reset card
    if((!response) || (response == 0xFF)) error(3); //we don't even have a card
    if(!--timeout) error(7); //timed out
  }
  if(sd_cmd(0x48, 0x1AA) != 1) error(1); //CMD8: make sure we are using SD v2
  for(c = 0; c < 4; ++c) spi_byte(0xFF);
  do {
    sd_cmd(0x77, 0); //CMD55: introduce application-specific command
    response = sd_cmd(0x69, 0x40000000); //ACMD41: initialize card
    if(!--timeout) error(8); //timed out
  } while(response != 0);

  sd_cmd(0x7A, 0); //CMD58: read OCR
  response = spi_byte(0xFF);
  if((response & B11000000) != B11000000) error(2); //make sure we are using SDHC
  for(c = 0; c < 3; ++c) spi_byte(0xFF);

  stb(PORTB, 0); //set CS high
  SPCR = B01010000; //fosc/4 (4 MHz)
  SPSR |= 1; //set SPI double speed (8 MHz)
  for(c = 0; c < 10; ++c) spi_byte(0xFF); //synchronize clock
  sei();
  return;
}

void sd_read(uint8_t* buffer, uint32_t block, uint8_t start2, uint8_t size2) {
  /*start2 and size2 are in 2 byte units, size2 = 0 means whole block (512 B)*/
  cli();
  block <<= 9; //S//D
  clb(PORTB, 0); //set CS low
  uint8_t response;
  if((response=sd_cmd(0x51, block))) { //CMD17: read single block
    Serial.begin(9600); //D
    Serial.println(response, HEX); //D
    error(10);
  }
  while(spi_byte(0xFF) != 0xFE) ; //TODO: timeout error 9
  uint8_t b = 0; uint8_t end = 0;
  do {
    if((b >= start2) && (!end)) {
      *(buffer++) = spi_byte(0xFF);
      *(buffer++) = spi_byte(0xFF);
      --size2;
      if(!size2) end = 1;
    }
    else {
      spi_byte(0xFF);
      spi_byte(0xFF);
    }
    ++b;
  } while(b); //repeat 256 times
  spi_byte(0xFF); spi_byte(0xFF); //two more bytes to close
  stb(PORTB, 0); //set CS high
  spi_byte(0xFF);
  sei();
  return;
}

В некоторые дни он работает отлично, иногда просто блокируется во время инициализации, и, наконец, прямо сейчас я пытаюсь прочитать содержимое SD-карты, и он просто отображает 0x55AA навсегда. Я подозреваю, что это аппаратная проблема, но я хотел бы исключить возможность того, что мой код может быть неправильным. Кроме того, я не знаю, почему мне нужно умножить адрес чтения на 512. Будучи картой SDHC, я ожидал бы, что адреса будут в блоках, верно?

...