Writing blocks to SD card: data is never written

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm trying to raw write blocks to an SDHC card in SPI mode. I'm using 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!

 

This topic has a solution.
Last Edited: Sat. Jul 29, 2017 - 08:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

smeezekitty wrote:
SD code based on scattered specs and code examples on the internet. None of which are very complete

Why? I haven't worked with SD cards yet but I've seen several SD card libraries floating around.

Some are read only though. Maybe you missed the writing part?

 

 

https://github.com/search?p=3&q=...

 

smeezekitty wrote:
Hopefully I didn't make a mistake copying that. My code is spread
Why don't you start with tidying up your own code?

When writing code I always keep track of the global structure, and I regulary rearrange parts just to make it easyer to read.

If you never do that the code gets uglier and uglier and becomes progressively harder to maintain and no fun to work with.

 

It is easy to write code which has no obvious errors, but it is hard to write code which  obviously has no errors.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

 

SD code based on scattered specs and code examples on the internet. None of which are very complete

 

Why? I haven't worked with SD cards yet but I've seen several SD card libraries floating around.

Some are read only though. Maybe you missed the writing part?

 

 

Yes there are some with writing support and that is what it is based on. I just don't see anything I am doing fundamentally different from other libraries.

 

Quote:

Why don't you start with tidying up your own code?

 

When writing code I always keep track of the global structure, and I regulary rearrange parts just to make it easyer to read.

If you never do that the code gets uglier and uglier and becomes progressively harder to maintain and no fun to work with.

 

Except it really isn't difficult to maintain. There is just a lot of code (about 2.5K lines) and for example SPI and SD code are in separate files. So I pasted them together for the purpose of this post.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I send one byte of padding (0xFF) before the 'Start Block' token (0xFE).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just tried it but unfortunately no change. I even tried different sectors and taking the SD card and searching for the data with HXD to see if it is getting written in the wrong place and nada.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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 :P