Cannot write blocks to SDHC card

I’m trying to raw write blocks to an SDHC card in SPI mode. I’m not actually using an Arduino but a breadboard Atmega328p @ 8 MHz with a 3.3V supply (that way I don’t need to level convert for the SD)

I wrote the SD code based on scattered specs and code examples on the internet. None of which are very complete. I am able to initialize the card and can read blocks to my hearts content. But when I try to write a sector from data stored in EEPROM, I get the expected 0x05 response but the data isn’t actually written. I read back the sector and it is unchanged.

Relevant sections of code:

#define SET_CS()   PORTB |= _BV(DDB2)
#define CLEAR_CS() PORTB &= ~_BV(DDB2)

uint8_t SD_response(uint8_t resp){
  scheduler_stop();
  uint16_t count = 0xFFF;
  uint8_t result;

  while(count) {
    result = spi_rx_byte();
    if (result == resp)break;
    count--;
  }
 // scheduler_start();
  if(!count){return 1;}
  return 0; 
}

#define SD_READ 0x11
#define SD_WRITE 0x18
uint8_t SD_block_command(uint8_t cmd, uint32_t block){
uint8_t cnt = 0;
 again:
 cnt++;
  CLEAR_CS();
  //0x51
  SD_cmd[0] = 0x40 | cmd;
  SD_cmd[1] = (block & 0xFF000000) >> 24;
  SD_cmd[2] = (block & 0x00FF0000) >> 16;
  SD_cmd[3] = (block & 0x0000FF00) >> 8;
  SD_cmd[4] = block & 0xFF;
  SD_cmd[5] = 0xFF;
  spi_send(SD_cmd, 6);
  if (SD_response(0x00) == 1) {
      printk(PSTR("SD block timeout\n"));
	  spi_init();
	  SD_init();
	  if(cnt < 16)goto again;
	  return 1;
  }
	if(cmd == SD_READ){
		if (SD_response(0xFE) == 1){
			printk(PSTR("SD data error\n"));
			return 1;
		}
	} else {
		spi_send_byte(0xFE);
	}
  return 0;
}

void SD_write_block(uint32_t block){
	scheduler_stop();
	if(SD_block_command(SD_WRITE, block)){
		scheduler_start();
		return;
	}
	spi_tx_eeprom(512);
	spi_send_byte(0xFF);
	spi_send_byte(0xFF);
	printk(PSTR("resp=%d\n"), spi_rx_byte());
	while(!spi_rx_byte());
	SET_CS();
	spi_rx_byte();
	CLEAR_CS();
	while(!spi_rx_byte());
	SET_CS();
	scheduler_start();
}
void spi_tx_eeprom(int length){
	int i = 0;
	for(i=0;i < length;i++){
		spi_send_byte(eeprom_read_byte(i));
	}
}

void spi_send_byte(uint8_t b){
	SPDR = b;
    while (!(SPSR & _BV(SPIF)));
}

uint8_t spi_rx_byte(){
  SPDR = 0xFF;
  while (!( SPSR & _BV(SPIF)));
  return SPDR;
}

Hopefully I didn’t make a mistake copying that. My code is spread between multiple files and I pasted it together (hence the weird place for the #define s)

Thanks!

Welp I feel really dumb now. It turns out the main problem was that I forgot to add the write_block() function to by header file and I was using it implicitly.

Turns out it defaulted to giving it an int argument instead of a uint32_t so the block number it was trying to write to was total garbage. I didn't spot the error until

I dumped every byte sent and received over SPI and saw the value after command 24. I could've fixed it in a moment if I had just thought to print out the block value.

Oh well you live and you learn. Maybe I should've compiled with -Werror :stuck_out_tongue: