Showing posts with label logic analyser. Show all posts
Showing posts with label logic analyser. Show all posts

Monday, October 22, 2012

SD card clock edge problems?

There's something still not right with our SD card initialisation routines.
We've suspected for a while that there's something going on with the clock edge for reading/writing responses to/from the SD card.

Our logic probe appears to be displaying what we expect to see, but in code, we're never trapping the correct responses back from the card. For example, we're looking for 0x01 from the first init routine but now we're getting none 0xFF responses, but they're not 0x01 from the card.

The probe says the card is returning 0x01 but the PIC is getting some other value, so we hacked in a bit of debugging code that we can watch on the "output" window:


// send the cs line low to send commands
spi_assert();

// send the initialise command
r=sendCommand(CMD_GO_IDLE_STATE, 0);
if (r != IN_IDLE_STATE ) {
      state=r;
      sendClocks(2, 0xFE);
      sendClocks(2, state);
      spi_deassert();
      return SD_RETCODE_NOT_IDLE;
}else{

      // do rest of init code here

}



The resulting output looked like this....


The logic probe says we're being sent 0x01 from the SD card but when we echo the incoming value back onto the SPI line, we get 0x7F.

So we're expecting (and the probe says we're getting) 0b00000001
And the PIC says it's getting 0b01111111

However, comparing these to the timing graph, the 0x01 actually comes in during the transfer of the byte 0xFE (our error code to say we've had a non-0xFF response from the card). So the PIC has already decided that it's time to raise an error, although the probe says we've had an 0xFF response.

It looks like the response from the card 0x00 followed by 0xFF is being merged by the PIC - it's taking the last bit of 0x00 and appending the following 7 bits of 0xFF - giving us 0b01111111 (or 0x7F in hex).
So somewhere in this little lot, the responses from the SD card look like they're being read on the "wrong" edge of the clock pin.......

SD card send/receive still not quite there

After breathing a massive sign of relief once the SD card starting responding to SPI commands, we thought it'd be relatively straight forward to get data into and out of the card. We've already worked with NAND flash memory (in our case AT45DB041D eeprom chips), and stream-reading/writing data, so it shouldn't be too difficult....

We're still having problems getting a reasonable response from the SD card.
Everything points to the clocking edge being either incorrect or incorrectly configured, but it's just not making sense! We got an ok response from the SD card after initialising it. So now we send command 18 and wait for an ok before streaming the data back from the card:


void testReadData(){
      // send command 18, param x10 (start multi-block read from address 0x10)
      // and wait for an ok response
      sendCommand(CMD_READ_MULTIPLE_BLOCKS, 0x10);
     
      // what follows should be data?
      r=SPISendAndReceive(0x00);
      r=SPISendAndReceive(0x00);
      r=SPISendAndReceive(0x00);
      r=SPISendAndReceive(0x00);
      r=SPISendAndReceive(0x00);
     
}


But here's the weird thing - after we've successfully initialised the card, we get some sort of noisy signal back from the card after sending the second byte from the (redundant) CRC check.

So that we can see where our init routine ends, we send a single byte 0xC0 to the SD card once we think the init routine has successfully completed. This can be seen clearly in the screenshot below:


What is unusual is the noisy signal coming back from the card. The logic probe decodes it as 0xFF but there are definitely peaks and troughs occuring in the signal in between the clock cycles.


Here, we're sending a command to the SD card, to initiate a multi-block read so we send a command byte 18, followed by the sector to begin reading from (we chose 0x10) and a CRC value of 0x95 (the CRC is not actually used when in SPI mode, but must be included as part of the data "packet"). In the screen grab above, we seem to be getting a noisy signal back from the SD card while sending the sector address to read from.

The send command function includes a check for an ok response before returning:


unsigned char sendCommand(unsigned char command, unsigned long param) {
      r=SPISendAndReceive(0xFF); // dummy byte
      r=SPISendAndReceive(command | 0x40);
      r=SPISendAndReceive(param>>24);
      r=SPISendAndReceive(param>>16);
      r=SPISendAndReceive(param>>8);
      r=SPISendAndReceive(param);
      r=SPISendAndReceive(0x95); // correct CRC for first command in SPI
      // after that CRC is ignored, so no problem with
      // always sending 0x95
      r=SPISendAndReceive(0xFF); // ignore return byte
      return waitForNot(0xFF);
}


But we never seem to get a non 0xFF response back from the card.....


Friday, October 19, 2012

Initialising an SD card with a 16F1825 PIC microcontroller

After nearly two days of headache and heartache - and thanks in no small part to the brilliant Arduino/AVR code from Chris McClelland- we finally managed to successfully initialise an SD card with our PIC microcontroller.

Before we stuff it up and it stops working, here's some demo code:

#include <system.h>
// ##################################
// using a 32 Mhz internal oscillator
#pragma DATA _CONFIG1, 0x0804
#pragma DATA _CONFIG2, 0x1DFF
#pragma CLOCK_FREQ 32000000

unsigned char arrayMarker;
unsigned char arrayPointer;
unsigned long currSector;
unsigned char state;
unsigned char r;
unsigned char sample;
unsigned char playSample;

// ##################################

#define TOKEN_SUCCESS 0x00
#define TOKEN_READ_CAPACITY 0xFE
#define TOKEN_READ_SINGLE 0xFE
#define TOKEN_READ_MULTIPLE 0xFE
#define TOKEN_WRITE_SINGLE 0xFE
#define TOKEN_WRITE_MULTIPLE 0xFC
#define TOKEN_WRITE_FINISH 0xFD
#define IN_IDLE_STATE 0x01
#define ERASE_RESET (1<<1)
#define ILLEGAL_COMMAND (1<<2)
#define COMMAND_CRC_ERROR (1<<3)
#define ERASE_SEQUENCE_ERROR (1<<4)
#define ADDRESS_ERROR (1<<5)
#define PARAMETER_ERROR (1<<6)

#define CMD_GO_IDLE_STATE 0
#define CMD_SEND_OP_COND 1
#define CMD_SEND_CSD 9
#define CMD_STOP_TRANSMISSION 12
#define CMD_READ_SINGLE_BLOCK 17
#define CMD_READ_MULTIPLE_BLOCKS 18
#define CMD_WRITE_SINGLE_BLOCK 24
#define CMD_WRITE_MULTIPLE_BLOCKS 25
#define CMD_APP_SEND_OP_COND 41
#define CMD_APP_CMD 55

#define SD_RETCODE_SUCCESS 0x00
#define SD_RETCODE_NOT_IDLE 1
#define SD_RETCODE_ERROR_INIT 2
#define SD_RETCODE_ERROR_READ 3
#define SD_RETCODE_ERROR_NO_DATA 4
#define SD_RETCODE_ERROR_READ_CAPACITY 5
#define SD_RETCODE_ERROR_WRITE 6
#define SD_RETCODE_ERROR_NOT_READY 7
#define SD_RETCODE_TIMEOUT_ERROR      8


unsigned char SPISendAndReceive(unsigned char b){
sspbuf=b; //send data
while(!(ssp1stat & (1<<BF)));
r=sspbuf;            
      return(r);
}

void sdClockSpeed(bool fast){     
      ssp1con1 = (ssp1con1 & 0b11110000) | (fast ? 0b0000 : 0b1010);
}

void spi_deassert(){
      // pull the /cs pin high on the slave
      porta.1=1;
}

void spi_assert(){
      // drop the /cs pin low on the slave
      porta.1=0;
}

unsigned char waitForNot(unsigned char response){
      unsigned short i;
      unsigned char byte;
      i=0xFFFF;
      byte=response;
      while(byte==response && --i){
            byte=SPISendAndReceive(0xFF);
      }     
      return byte;
}

unsigned char waitFor(unsigned char response){
      unsigned short i;
      unsigned char byte;
      i=0xFFFF;
      byte=response+1;
      while(byte!=response && --i){
            byte=SPISendAndReceive(0xFF);
      }     
      return byte;
}

void sendClocks(unsigned char numClocks, unsigned char byte) {
      unsigned char i;
      for ( i = 0; i < numClocks; i++ ) {
            r=SPISendAndReceive(byte);
      }
}

unsigned char sendCommand(unsigned char command, unsigned long param) {
r=SPISendAndReceive(0xFF); // dummy byte
r=SPISendAndReceive(command | 0x40);
      r=SPISendAndReceive(param>>24);
      r=SPISendAndReceive(param>>16);
      r=SPISendAndReceive(param>>8);
      r=SPISendAndReceive(param);
r=SPISendAndReceive(0x95); // correct CRC for first command in SPI
// after that CRC is ignored, so no problem with
// always sending 0x95
r=SPISendAndReceive(0xFF); // ignore return byte
      return waitForNot(0xFF);
}

unsigned char initialiseSD(){
      unsigned char i;
     
      // initialise the SPI module on the PIC
      ssp1add            = 0x19;                        //slow clock will be 32,000,000/100 = 320khz
     
      //ssp1con1.ckp=1;                              //spi idle clock high (bitx)
      ssp1con1      = 0b00111010;                  //spi master, clk=timer2, spi_clck idle high
     
      //ssp1stat.cke=1;                              // cke=bit6
      //ssp1stat.smp=0;                              // smp=bit7
      ssp1stat      = 0b00000000;                  //spi mode 3?
     
      sdClockSpeed(false);      //      load the SD card in low speed <400khz mode
     
      // hold the cs line high when powering up to enter spi mode
      spi_deassert();
     
      // power up the sd card and allow time to stabilise
      porta.0=1;
     
      // with CS high, send 256 clocks (32*8=256, 1.024ms @250kHz)
      // with DI low while card power stabilizes
      sendClocks(32, 0x00);
     
      // with CS high send 80 clocks (10*8=80, 0.32ms @250kHz)
      // with DI high to get the card ready to accept commands
      sendClocks(10, 0xFF);
     
      // send the cs line low to send commands
      spi_assert();
     
      // send the initialise command
      if (sendCommand(CMD_GO_IDLE_STATE, 0) != IN_IDLE_STATE ) {
            sendClocks(2, 0xFE);
            spi_deassert();
            return SD_RETCODE_NOT_IDLE;
      }else{
     
            // Tell the card to initialize itself with ACMD41.
            sendCommand(CMD_APP_CMD, 0);
            r = sendCommand(CMD_APP_SEND_OP_COND, 0);
     
            // Send CMD1 repeatedly until the initialization is complete.
            i = 0xFFFF;
            while ( r != TOKEN_SUCCESS && --i ) {
                  r = sendCommand(CMD_SEND_OP_COND, 0);
            }
     
            sendClocks(2, 0xFF);
            spi_deassert();
     
            if ( i == 0x0000 ) {
                  sendClocks(2, 0xFD);
                  spi_deassert();
                  return SD_RETCODE_TIMEOUT_ERROR;                 
            }
     
           
            // for debugging, turn off the LED/card
            porta.0=0;
           
            // we should now be in true SPI mode
            sdClockSpeed(true);
      }
     
}

void preloadTimer1(){
      //------------------------------------
      // pre-load timer1 with 65172 (0xFE94)
      //------------------------------------
      // 1/22050 = 0.00004535147
      // at 32mhz, fosc=8,000,000
      // and 8m/22050 = 362.812
      // so we want our timer1 to only count up to 363
      // timer1 is a 16-bit timer (0-65535) so we need to
      // preload it with 65535-363 = 65172

      tmr1h=0xfe;
      tmr1l=0x94;
}     

void startMultiBlockRead(){
      // send the spi commands to start a multi-block read from the sector value
      // held in the variable currSectorValue
     
}

void initialise(){

      osccon=0b11110000;            // oscillator=32mhz
      delay_ms(50);                   // wait for internal voltages to settle
      state=0;
      playSample=0;
     
      intcon=0b11000000;            // global interrupts enabled and peripheral interrupts
      apfcon0=0b01000000;
      trisa=0b11001100;            // out on 0,1,4,5 input on 2, DNK on 3
      trisc=0b00000010;            // all portC lines are output except pin1 (RC1=spi in)     
      ansela=0x00;                  // turn off analogue pins on portA
      anselc=0x00;                  // turn off analogue pins on portC
     
      option_reg=0b10000000;      // no weak pull-ups

           
      //-------------------------------------
      // initialise the SD card
      //--------------------------------------
      initialiseSD();
     
}

void nothing(){

      //----------------------------------------------------
      // start a multi-block read from the appropriate place
      //----------------------------------------------------
      currSector=1;
      startMultiBlockRead();
     
      //------------------------------------
      // start a pwm carrier signal @ 125khz
      //------------------------------------
      // Disable the CCPx pin output driver by setting the associated TRIS bit.
      // (for CCP1, the output pin is RC.5)
      trisc.5=1;
     
      // Load the PRx register with the PWM period value:     
      pr2=0x00;      // this will change every 1/22050th second, start at zero
     
      // Configure the CCP module for the PWM mode by loading the CCPxCON register with the appropriate values:
      // Load the CCPRxL register and the DCxBx bits of the CCPxCON register, with the PWM duty cycle value.
      pr2=0x3F;                        // this is the value to create a 125khz carrier (from datasheet)
      ccp2con = 0b00001100;       // puts the CCP module into PWM mode     
      ccptmrs = 0x00;             // all comparitors are using PWM based on timer2
     
      // Configure and start Timer2/4/6:
      t2con = 0b00000100; // start timer2, no scaling

      // Enable PWM output pin (CCP2 out = RC.5)
      trisc.5=0;
     
      //-------------------------------------------
      // interrupt on timer1 22050 times per second
      // ------------------------------------------
      // set up timer1 as a 16-bit timer, count on instruction cycle (fosc/4)
      t1con=0b00001000;      // set bit zero to 1 to actually start the timer
      // set up the actual timer1 interrupt:
      pie1=0x01;            // timer1 rollover interrupt is bit zero
      intcon=0x192;      // global interrupts (bit7) + peripheral interrupts (bit6)     
      preloadTimer1();
     
}

void playNextSample(){
      // dump the next sample from the buffer to the PWM module
      //pwm=sample;
      ccpr2l=(sample>>2);
      ccp2con.1=sample.1;
      ccp2con.0=sample.0;
     
      // read the next byte from the SD card
      sample=SPISendAndReceive(0x00);
     
}

void main(){
      initialise();
      while(1){
            if(playSample==1){
                  // drop the current sample to the PWM module
                  playSample=0;
                  playNextSample();                 
            }
      }
}

void interrupt(void){
      // bit zero of pir1 is tmr1 overflow interrupt
      if(pir1.0==1){
            // clear the timer1 interrupt flag
            pir1.0=0;
            // preload timer1 to get the next interrupt to occur in a timely manner
            preloadTimer1();
            // set the flag to play next sample (background task)
            playSample=1;
      }
}

With a microSD card connected in an SD card holder, using RA0 to power the card through a transistor and using RA1 as the chip select line, with the SPI lines are per the hardware peripheral (SPI_Clock = RC0, SPI_MISO/data in =RC1, SPI_MOSI/data out=RC2/RA3) here's the output from the logic probe from boot-up:


There's also the start of our PWM/timer1 interrupt code in there, just so that we have a global interrupt handler. The PWM control is already coded up, but was mostly stripped out to allow us to focus on getting the SD card working.

And thanks to Chris McClelland, it looks like it is!
Thanks again Chris.
You're ace!

SD Card SPI timing graphs

This whole SD-card-access-via-SPI is proving to be a bit of a headache.
Just to make sure we're not having a timing issue (cards need to be interfaced at < 400khz until properly initialised) we've even resorted to bit-banging the SPI and introducing delays between pin wiggling on the clock line.

The frustrating thing is that despite numerous attempts, we're still getting nothing back from the SD card. We found a cut down FAT16 library for Hi-tech C compiler, and tried converting that back to SourceBoost with limited success.

Using this hacked-up code, we did manage to get something back from the SD card:


The response we're looking for is 0x01. This is visible on the probe output (above) and the probe software correctly identifies it as 0x01 but for some reason, our code doesn't interpret it as such. It may be because the clock cycles - complete with stretching where the crazy library-based code tries to re-sync the clock line (and, it looks like, it fails!)

At least we're getting something back from the SD card, so we can presume it is actually powered up!
However, following the SD guide, sending the command bit 0x40, then 4 bytes of 0x00 and a CRC checksum of 0x95 should, after a few clock cycles, see 0x01 being returned from the SD card.

Sadly, we never see this...



#include <system.h>

//config1 = 0x0044      //68 =4+64
//config2 = 0x1DFF      //7676 = 1+2+4+8+16+32+64+128+256+1024+2048+4096

#pragma DATA _CONFIG1, 0x0804
#pragma DATA _CONFIG2, 0x1DFF
#pragma CLOCK_FREQ 32000000

typedef unsigned char byte;
unsigned char r;
unsigned char goSlow;
unsigned char spiIn;
unsigned char spiOut;

// ################################################################################

void toggleClock(){
      portc.0=1;
      if (goSlow=1){delay_us(10);}
      portc.0=0;
      if (goSlow=1){delay_us(8);}
      portc.0=1;
}

unsigned char sendAndReceiveSPI(unsigned char in){
      unsigned char tmp;
      tmp=0;
      spiOut=in;
      spiIn=0x00;
     
      if(spiOut.7==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=128;}
     
      if(spiOut.6==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=64;}
     
      if(spiOut.5==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=32;}
     
      if(spiOut.4==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=16;}
     
      if(spiOut.3==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=8;}
     
      if(spiOut.2==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=4;}
     
      if(spiOut.1==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=2;}
     
      if(spiOut.0==1){ porta.4=1;}else{porta.4=0;}
      toggleClock();
      if(portc.1==1){spiIn+=1;}
     
      return(spiIn);
}

void assertCS(){
      porta.1=0;
}

void deassertCS(){
      porta.1=1;
}

void initSDCard(){
      unsigned char i;
     
      // pull the SD card power pin low
      porta.0=0;
      delay_ms(1);
           
      // pull all the other pins low - they should be low
      // during the SD card power-up cycle
      portc.0=0;
      portc.1=0;
      portc.2=0;
      porta.3=0;
      porta.1=1;      // hold CS high during power up
      portc.0=1;      // hold clock high during rest
     
      // power up the SD card
      // (this is also the trigger on the logic probe)
      porta.0=1;
     
      // clock a few pulses to clear buffers etc.
      deassertCS();
      for(i=0; i<10; i++){
            r=sendAndReceiveSPI(0x00);
      }

     
      // enable the CS on the SD card
      assertCS();
                       
      // send the reset command (0x40 | 0b10000000)
      r=sendAndReceiveSPI(0x40);
     
      // send four blank/null bytes
      for(i=0; i<4; i++){
            r=sendAndReceiveSPI(0x00);
      }
      // send the CRC/checksum
      r=sendAndReceiveSPI(0x95);
           
      while(r!=0x01){
            r=sendAndReceiveSPI(0xFF);
      }
     
      // turn off debugging
      porta.0=0;
}

void initialise(){
     
      osccon=0b11110000;            // oscillator=32mhz
      intcon=0b11000000;            // global interrupts enabled and peripheral interrupts
      apfcon0=0b00000000;            // alternative pin function - bit6: SDO1 function is on RA4=1, RC2=0
      trisa=0x00;                        // all outputs on portA
      trisc=0b00000010;            // all portC lines are output except pin1 (RC1=spi in)     
      ansela=0x00;                  // turn off analogue pins on portA
      anselc=0x00;                  // turn off analogue pins on portC     
      option_reg=0b10000000;      // no weak pull-ups
     
      goSlow=1;
}

void main(){
      initialise();
      delay_ms(10);     
      initSDCard();
}

void interrupt(void){
      // bit zero of pir1 is tmr1 overflow interrupt
      if(pir1.0==1){
            // clear the timer1 interrupt flag
            pir1.0=0;
            // preload timer1 to get the next interrupt to occur in a timely manner           
      }
}

Once we get the basic SD initialise working, we'll look at putting it back onto the SPI hardware (bit-banging is soooo last year) but for now we're just trying to get the SD card to even acknowledge it's presence!


PIC 16F1825 pinout for SPI/SD card interface:

SPI_CLOCK:  RC0
SPI_MISO (in): RC1
SPI_MOSI (out): RC2 / RA3 (can be changed in firmware)

We're using RA0 to turn the SD card on through a FET
SPI_CS (chip select): RA1


Sourceboost SD Card reader

We're having a bit of a time of it at the minute, just trying to get our PIC16F1825 to correctly initialise an SD card. It's not helped by the fact that we're using a new chip we've never used before, in a language/syntax we've not used for a long time, with a compiler we've never seen, using hardware peripherals we've only ever simulated in software. These last few days have been a very steep learning curve!

At last night's BuildBrighton meeting, Jason spent a good few hours going over all our registers and code to see if there was anything obviously wrong, but found nothing. But we couldn't even get the SPI hardware module on the PIC to operate, let alone send data using it!

Jason has a Scanalogic Logic Probe/Analyser which has proved invaluable in finding out what's going on at a pin-wiggling level. Using it, it took just a few minutes to realise that one of our fuse settings was incorrect and that instead of sending data over SPI, we were outputting the PIC clock signal onto the (multiplexed) SPI_OUT pin. D'oh.

With the Scanalogic Probe in place, we were able to actually see pin activity as data was exchanged between the devices



Using this helped us identify where things were initially going wrong and after three hours of getting nowhere, we finally manged to see SPI data being sent over the pins. Now our problem is, there's nothing coming back from the SD card!


In the image above, the blue line is the SPI clock. The yellow is the SPI data going from the PIC to the SD Card. Red is the trigger pin (used to start the probe reading the data) and the green is *supposed* to be the data coming back from the SD card.....