Shop OBEX P1 Docs P2 Docs Learn Events
Need help with fast SD card SPI rountines. — Parallax Forums

Need help with fast SD card SPI rountines.

KyeKye Posts: 2,200
edited 2010-05-31 15:15 in Propeller 1
I need some help trying to get some SPI rountines for any SD card to work.

Basically, I made a general purpose SPI function that can run at any speed from 1 to about 3 Mhz. The code for that is below.

... CTRA is set to NCO mode (%00100) and its output is the clock pin.

... The SPITiming is either:

· fastTiming := ((constant(2_968_750 << 9) / clkfreq) << 23)
· slowTiming := ((constant(234_375 << 13) / clkfreq) << 19)

' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
'                       Read SPI
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                 
readSPI                 mov     SPICounter,           #8                           ' Setup counter to read in 1 - 32 bits.
                        mov     SPIDataIn,            #0 wc                        '

                        mov     phsa,                 #0                           ' Start clock low.
                        mov     frqa,                 SPITiming                    '
 
readSPILoop             waitpne clockPin,             clockPin                     ' Get bit.
                        rcl     SPIDataIn,            #1                           '
                        waitpeq clockPin,             clockPin                     '
                        test    dataOutPin,           ina wc                       '
                        
                        djnz    SPICounter,           #readSPILoop                 ' Loop.
 
                        mov     frqa,                 #0                           ' Stop clock high.
                        rcl     SPIDataIn,            #1                           ' 
                                                     
readSPI_ret             ret                                                        ' Return.
 
' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
'                       Write SPI
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
writeSPI                mov     SPICounter,           #8                           ' Setup counter to write out 1 - 32 bits.
                        ror     SPIDataOut,           SPICounter                   '
                        
                        mov     phsa,                 #0                           ' Start clock low.
                        mov     frqa,                 SPITiming                    '
 
writeSPILoop            shl     SPIDataOut,           #1 wc                        ' Set bit.
                        waitpne clockPin,             clockPin                     ' 
                        muxc    outa,                 dataInPin                    '
                        waitpeq clockPin,             clockPin                     '
 
                        djnz    SPICounter,           #writeSPILoop                ' Loop. 
    
                        mov     frqa,                 #0                           ' Stop clock high.
                        or      outa,                 dataInPin                    ' 
                        
writeSPI_ret            ret                                                        ' Return.


The nice thing about the above piece of code is that I can run the clock at many different speeds and still read the data correctly. However, I need to increase the preformance of my driver and thus I need the above code to go faster.

So after looking through the new FSRW block driver I came up with this below:

... CTRA is set to·Duty mode (%00110) and its output is the clock pin.

... The SPITiming is either:

· fastTiming := ((constant(2_968_750 << 9) / clkfreq) << 23)
· slowTiming := ((constant(234_375 << 13) / clkfreq) << 19)

' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
'                       Read SPI
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                 
readSPI                 mov     SPICounter,           #8                           ' Setup counter to read in 1 - 32 bits.
                        mov     SPIDataIn,            #0                           '
 
                        mov     frqa,                 SPITiming
 
readSPILoop             waitpeq clockPin,             clockPin
                        test    dataOutPin,           ina wc
                        rcl     SPIDataIn,            #1
                        djnz    SPICounter,           #readSPILoop
                    
                        mov     frqa,                 #0

                                     
readSPI_ret             ret                                                        ' Return.
 
' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
'                       Write SPI
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
writeSPI                mov     SPICounter,           #8                           ' Setup counter to write out 1 - 32 bits.
                        ror     SPIDataOut,           SPICounter                   '
                        
                        mov     frqa,                 SPITiming
 
writeSPILoop            shl     SPIDataOut,           #1 wc
                        muxc    outa,                 dataInPin
                        waitpeq clockPin,             clockPin
                        djnz    SPICounter,           #writeSPILoop 
                     
                        mov     frqa,                 #0
                        or      outa,                 dataInPin 
                        
writeSPI_ret            ret                                                        ' Return.

And with this above code I should be able to go faster than 3Mhz but I still should be able to slow the clock down when I want to at will.

However, the above code does not work with the SD card and refuses to work at all. I do not know what I am doing wrong.

...

I've attached the source code and all the files need to run the demo I'm working on. The file with the SPI rountines is the SD2.0_FATEngine.spin

Thank you for any help,

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Nyamekye,

Comments

  • pullmollpullmoll Posts: 817
    edited 2010-05-30 16:41
    Kye said...
    I need some help trying to get some SPI rountines for any SD card to work.

    A quick look at the code tells me that perhaps you're running through the loop multiple times while the waitpeq is true?
    The original loop waits until the condition isn't met any longer.
    I think you can only try to read (or write) like you try when the clock is running parallel to the loop, i.e. exact same timing, and you use the waitpeq just to synchronize on the falling or rising edge!? In that case you could even put it in front of the loop and just clock the bits in or out.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Pullmoll's Propeller Projects
  • KyeKye Posts: 2,200
    edited 2010-05-31 01:43
    Mmm, that's not what's sopposed to happen. The duty cycle mode makes sure the clock is only high for one clock cycle and then it goes low for like 29 clock cycles after that.

    I'm very sure the code does not lock up, but it also garbles the information.

    I will probably need lonesock's or Kuneko help on this as I got the idea from their code.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • kuronekokuroneko Posts: 3,623
    edited 2010-05-31 09:04
    Kye said...
    I will probably need lonesock's or kuroneko help on this as I got the idea from their code.
    I think you slightly misunderstood the code in the SPI driver, sorry. DUTY mode is not really suitable for arbitrary clock frequencies.
    • have a look at this thread as to why 25% DUTY mode is used
    • forget about latching onto a single cycle pulse (from a DUTY counter) with a waitpxx in general, I can make it fail reliably (e.g. pin 0 and cog 4, 5MHz/PLL16)a
    • if you want higher speeds you have to rely on instruction timings at some point, just make sure you're sync'd to the clock, then transfer the data blind
    a most likely the rising edge is delayed slightly longer then the falling one which makes the pulse invisible to the waitpxx logicb, having said that there are cases when the pin/cog combination does work, sometimes too well (double count)
    b which is odd because it does work with 5MHz/PLL8 so I assume clock jitter may play a role as well

    Post Edited (kuroneko) : 5/31/2010 11:01:35 AM GMT
  • AribaAriba Posts: 2,690
    edited 2010-05-31 11:54
    Kye

    I don't think that such an asymetrical clock matches the specs of an SD card. There will be a minimal high time for the clock greater than 12.5 ns.
    Mostly a clock at the higher limit needs to have a duty of near to 50%.

    If you not use a counter for the clock, but set and clear the clockpin with assembly instructions you are faster (5 instructions = 4 MHz clock @ 80MHz). The counter is only needed if you want 2 or 1 instruction loops (=10Mhz or 20MHz clock) but then you need also a second counter as multiplexer and shiftregister.

    Why do you want to slow down the clock? With 4 or 5 Mhz every SD card should work.

    Andy
  • KyeKye Posts: 2,200
    edited 2010-05-31 15:15
    Its nice to be able to slow down the clock becuase then you can reuse the same code for the slow SPI initialization commands.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
Sign In or Register to comment.