SDFileSystem modification in trouble...

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
liak
Registered User
Registered User
Posts: 195
Joined: Fri Oct 05, 2007 12:26 am

SDFileSystem modification in trouble...

Post by liak » Wed Sep 10, 2008 3:34 pm

Dear all, especially Steven,
I ran into problem with the modification I did for the SD module (originated from Steven).
I tried reading 4 sectors from the SD card and load them into an array of buffers. These buffer are declared as public array as FShared(5). I thought by modifying the ReadSector subroutine as follow will do. It may not be the correct way.
Initially it worked well. But later I realized that the modification will not work if I read more than 1 sector at a time. Can anyone suggest a correct way to do it? And point out where I went wrong? I am thinking of doing it in the shortest time so, I put everything into one call to the subroutine.

Code: Select all

Public Dim FShared(5) As TShared
...
...
Public Sub FReadSector(ByRef pSector As LongWord, pBuffer As Boolean = True)
Dim TimeOut, Error As Byte
Dim Index As Word

Clear(FShared(1))
Clear(FShared(2))
Clear(FShared(3))
Clear(FShared(4))

'have to do like this, since SF does not allow us to use array in memory indexing
#if SectorToRead >= 1

   If (Disk.RWError = True And pBuffer) Then     // If RW error has occurred, prevent further reading from 
                                                 // disk until reinitialised, unless not buffering
                                                 // (pBuffer = False, used for DiskMounted)
      Exit
   EndIf
   TimeOut = $00
   Error = 1
   If (Disk.CardType And $04) = 0 Then           // MMC or SD - byte addressing (SDHC use block addressing)
      pSector = pSector << 9
   EndIf 
   Repeat
      CS = 0                                     
      If SendCmd(CMD17, pSector) = $00 Then      // Send Cmd 17
         If SeekResponse($FE) = $FE Then         // Read start token
            Index = 0
            If pBuffer Then
               FSR0 = @FShared(1).CurrentSectorBuffer
               Repeat                            // Read data block
#if SD_SPI = SW                                  // Software SPI version - use SendByte
                  POSTINC0 = ReceiveByte()
#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed
                  SSPIF = 0 
                  SSPBuffer = $FF 
                  Repeat 
                     ClrWDT 
                  Until SSPIF = 1 
                  POSTINC0 = SSPBuffer
#endif
                  Inc(Index)
               Until Index = $200
            Else
               Repeat                            // Don't read data block
                  ReceiveByte()
                  Inc(Index)
               Until Index = $200
            EndIf
            ReceiveByte()                        // Read dummy CRC to conclude data block
            ReceiveByte()
            Error = 0                            // Read completed successfully
         EndIf   
      EndIf      
      CS = 1
      SendByte($FF)                              // Clock SD/MMC to complete read
      Inc(TimeOut)
   Until Error = 0 Or TimeOut > $01
   If Error = 1 Then
      Disk.RWError = True                        // Set RWError flag - can only be cleared using Init()
   EndIf

#endif

#if SectorToRead >= 2
'2nd Buffer
   Inc(pSector)                                  // Move to next Sector and next buffer
    
   If (Disk.RWError = True And pBuffer) Then     // If RW error has occurred, prevent further reading from 
                                                 // disk until reinitialised, unless not buffering
                                                 // (pBuffer = False, used for DiskMounted)
      Exit
   EndIf
   TimeOut = $00
   Error = 1
   If (Disk.CardType And $04) = 0 Then           // MMC or SD - byte addressing (SDHC use block addressing)
      pSector = (pSector << 9) 
   EndIf 

   Repeat
      CS = 0                                     
      If SendCmd(CMD17, pSector) = $00 Then      // Send Cmd 17
         If SeekResponse($FE) = $FE Then         // Read start token
            Index = 0
            If pBuffer Then
               FSR0 = @FShared(2).CurrentSectorBuffer
               Repeat                            // Read data block
#if SD_SPI = SW                                  // Software SPI version - use SendByte
                  POSTINC0 = ReceiveByte()
#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed
                  SSPIF = 0 
                  SSPBuffer = $FF 
                  Repeat 
                     ClrWDT 
                  Until SSPIF = 1 
                  POSTINC0 = SSPBuffer
#endif
                  Inc(Index)
               Until Index = $200
            Else
               Repeat                            // Don't read data block
                  ReceiveByte()
                  Inc(Index)
               Until Index = $200
            EndIf
            ReceiveByte()                        // Read dummy CRC to conclude data block
            ReceiveByte()
            Error = 0                            // Read completed successfully
         EndIf   
      EndIf      
      CS = 1
      SendByte($FF)                              // Clock SD/MMC to complete read
      Inc(TimeOut)
   Until Error = 0 Or TimeOut > $01
   If Error = 1 Then
      Disk.RWError = True                        // Set RWError flag - can only be cleared using Init()
   EndIf

#endif

#if SectorToRead >= 3
'3rd buffer
   Inc(pSector)
   If (Disk.RWError = True And pBuffer) Then     // If RW error has occurred, prevent further reading from 
                                                 // disk until reinitialised, unless not buffering
                                                 // (pBuffer = False, used for DiskMounted)
      Exit
   EndIf
   TimeOut = $00
   Error = 1
   If (Disk.CardType And $04) = 0 Then           // MMC or SD - byte addressing (SDHC use block addressing)
      pSector = pSector << 9
   EndIf 
   Repeat
      CS = 0                                     
      If SendCmd(CMD17, pSector) = $00 Then      // Send Cmd 17
         If SeekResponse($FE) = $FE Then         // Read start token
            Index = 0
            If pBuffer Then
               FSR0 = @FShared(3).CurrentSectorBuffer
               Repeat                            // Read data block
#if SD_SPI = SW                                  // Software SPI version - use SendByte
                  POSTINC0 = ReceiveByte()
#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed
                  SSPIF = 0 
                  SSPBuffer = $FF 
                  Repeat 
                     ClrWDT 
                  Until SSPIF = 1 
                  POSTINC0 = SSPBuffer
#endif
                  Inc(Index)
               Until Index = $200
            Else
               Repeat                            // Don't read data block
                  ReceiveByte()
                  Inc(Index)
               Until Index = $200
            EndIf
            ReceiveByte()                        // Read dummy CRC to conclude data block
            ReceiveByte()
            Error = 0                            // Read completed successfully
         EndIf   
      EndIf      
      CS = 1
      SendByte($FF)                              // Clock SD/MMC to complete read
      Inc(TimeOut)
   Until Error = 0 Or TimeOut > $01
   If Error = 1 Then
      Disk.RWError = True                        // Set RWError flag - can only be cleared using Init()
   EndIf

#endif


#if SectorToRead = 4
'4th sector
   Inc(pSector)
   If (Disk.RWError = True And pBuffer) Then     // If RW error has occurred, prevent further reading from 
                                                 // disk until reinitialised, unless not buffering
                                                 // (pBuffer = False, used for DiskMounted)
      Exit
   EndIf
   TimeOut = $00
   Error = 1
   If (Disk.CardType And $04) = 0 Then           // MMC or SD - byte addressing (SDHC use block addressing)
      pSector = pSector << 9
   EndIf 
   Repeat
      CS = 0                                     
      If SendCmd(CMD17, pSector) = $00 Then      // Send Cmd 17
         If SeekResponse($FE) = $FE Then         // Read start token
            Index = 0
            If pBuffer Then
               FSR0 = @FShared(4).CurrentSectorBuffer
               Repeat                            // Read data block
#if SD_SPI = SW                                  // Software SPI version - use SendByte
                  POSTINC0 = ReceiveByte()
#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed
                  SSPIF = 0 
                  SSPBuffer = $FF 
                  Repeat 
                     ClrWDT 
                  Until SSPIF = 1 
                  POSTINC0 = SSPBuffer
#endif
                  Inc(Index)
               Until Index = $200
            Else
               Repeat                            // Don't read data block
                  ReceiveByte()
                  Inc(Index)
               Until Index = $200
            EndIf
            ReceiveByte()                        // Read dummy CRC to conclude data block
            ReceiveByte()
            Error = 0                            // Read completed successfully
         EndIf   
      EndIf      
      CS = 1
      SendByte($FF)                              // Clock SD/MMC to complete read
      Inc(TimeOut)
   Until Error = 0 Or TimeOut > $01
   If Error = 1 Then
      Disk.RWError = True                        // Set RWError flag - can only be cleared using Init()
   EndIf

#endif
'push the lead pSector value to next 
Inc(pSector)            'pass by reference
End Sub         
As you see, basically I just repeated the original ReadSector 4 times. :oops: Probably a lazyman's method. Since I am not good in very low level structure of PIC, I did this. Steven, if you are reading this, please tell me how to go more properly.


Thanks.

Regards,
Liak.

liak
Registered User
Registered User
Posts: 195
Joined: Fri Oct 05, 2007 12:26 am

Proper modification required...

Post by liak » Thu Sep 11, 2008 4:25 am

Dear Steven and all,
After a day of debugging and trying, I have finalized that:
1. The memory buffers are not filled up with data from the corresponding sectors read.
2. Only the first buffer FShared(1) contains the correct data.
Most likely this is due to my poor understanding of what all those low level registers mean. I have temporarily bypassed the procedure by reading each sector separately using the original ReadSector subroutine and then filled up each buffer.
But of course, this is expected to be much much slower than if I can fill it up straight using the low level registers. So I hope someone and I think Steven will have to save me from this. Or at least some can translate the following segment of the code for me:

Code: Select all

#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed
                  SSPIF = 0 
                  SSPBuffer = $FF 
                  Repeat 
                     ClrWDT 
                  Until SSPIF = 1 
                  POSTINC0 = SSPBuffer
#endif
Assembly :oops: . ..

Thanks.

Regards,
Liak

Doj
Posts: 362
Joined: Wed Apr 11, 2007 10:18 pm
Location: East Sussex

Post by Doj » Thu Sep 11, 2008 3:41 pm

liak this is very well explained in the data sheet, but here is an explanation which is not exhausive but should give you an idea how the SPI works, it might seem a little confusing initially but is very simple and for this reason can operate at high speeds.

Code: Select all

#elseif SD_SPI = MSSP                            // MSSP version - use inline routine to optimise speed 
I expect you know this just means the hardware SPI is in use.

Code: Select all

SSPIF = 0 
To test for a byte being received you have to look at the "SSPIF" (PIR1.3 on my chip, not "SSPBUF" as recommended by microchip due to a silcon ***k up).
This flag must be cleared in software prior to the start of reception(or transmission).

Code: Select all

SSPBuffer = $FF
In order to either send or receive data on the SPI port you must generate the clock. This is done by putting a byte in "SSPBuffer".

Every time you send a byte the receive buffer(also "SSPBuffer") is filled up with the data from the slave unit, the data goes in a circle, you push one bit out and a bit is pused back from the slave.

This happens even when you send data, usually the data coming back when sending is useless so you just ignore it.

The opposite is true for receiving, you put any value you choose(one that the slave will ignore!) into "SSPBuffer" and the clock is sent which moves the byte in the slave to the emptied "SSPBuffer".

Code: Select all

 Repeat 
       ClrWDT 
 Until SSPIF = 1 
This just sits in a loop until the "SSPIF" flag is set indicating data has been received/transmitted, if the watchdog timer is on it is cleared so no reset will occur(not required if watchdog is on, but a safe precaution).


Code: Select all

 POSTINC0 = SSPBuffer 
This is a very simple transfer of the byte in "SSPBuffer" to the system variable "POSTINC0" which is then used by the compiler to pass the data to its routines.
You could if you wished use your own variable here if you were writing your own routines.

liak
Registered User
Registered User
Posts: 195
Joined: Fri Oct 05, 2007 12:26 am

Post by liak » Mon Sep 15, 2008 5:56 am

Dear Doj,

Thanks for your explanation and reply. That sure clears my doubt and improves my understanding. Will try to do my own coding based on this knowledge.

Regards,
Liak :lol:

Post Reply