Shop OBEX P1 Docs P2 Docs Learn Events
SD Card start up — Parallax Forums

SD Card start up

Hello,

I am trying to get the SD card to respond on the P2 Edge. I ran a program that comes with Flexprop, sd_dir.bas, to make sure my card does work. Originally i got no output but now i've been able to get F6 and added a timeout to the program. It seems to always time out though. I did read a few other pieces of code in the obex and saw the response for success should be 0x01. I'm confused on what kind of timing is needed for the card to return CMD0 correctly though.

_CLKFREQ        = 180_000_000
BAUD_RATE       = 2_000_000
SERIAL_CLOCK    = (_CLKFREQ / BAUD_RATE)
BIT_PERIOD      = ((SERIAL_CLOCK << 16) & $FFFFFC00) + (8 - 1)  ' P2 smartpin bit-period calc

RX_PIN          = 63
TX_PIN          = 62

SD_CS           = 60
SD_CLK          = 61
SD_MOSI         = 59
SD_MISO         = 58

dat
    org 0
    asmclk

main
    ' UART RX setup
    wrpin   ##(P_ASYNC_RX | P_HIGH_15K), #RX_PIN
    wxpin   ##BIT_PERIOD, #RX_PIN
    wypin   #1, #RX_PIN
    dirh    #RX_PIN

    ' UART TX setup
    wrpin   ##(P_ASYNC_TX | P_OE), #TX_PIN
    wxpin   ##BIT_PERIOD, #TX_PIN
    wypin   #1, #TX_PIN
    dirh    #TX_PIN

    ' SD pin directions
    drvh    #SD_CS               ' Deselect SD card
    dirh    #SD_CS
    dirh    #SD_CLK
    dirh    #SD_MOSI
    dirl    #SD_MISO            ' Let MISO float as input

    ' UART startup test
    mov     result, #"S"
    call    #uart_tx_char
    mov     result, #13         ' CR
    call    #uart_tx_char
    mov     result, #10         ' LF
    call    #uart_tx_char

    ' Set bit_delay for SPI clock (200 kHz)
    mov     bit_delay, ##(_CLKFREQ / 200_000)

    ' Send 80 dummy clocks with CS high (SD reset)
    mov     temp, #80
.send_dummy
    drvh    #SD_MOSI
    drvl    #SD_CLK
    waitx   bit_delay
    drvh    #SD_CLK
    waitx   bit_delay
    djnz    temp, #.send_dummy

    ' Send CMD0 (GO_IDLE_STATE)
    drvl    #SD_CS               ' Select card

    mov     transfer_data, #$40  ' CMD0
    call    #spi_tx8
    mov     transfer_data, #0    ' Argument = 0
    call    #spi_tx8
    call    #spi_tx8
    call    #spi_tx8
    call    #spi_tx8
    mov     transfer_data, #$95  ' Valid CRC for CMD0
    call    #spi_tx8

    ' Send a few extra clocks (important!)
    mov     temp, #10
.send_post
    mov     transfer_data, #$FF
    call    #spi_tx8
    djnz    temp, #.send_post

    ' Wait for response 0x01 (Idle state)
    mov     temp2, #100
.wait_response
    mov     transfer_data, #$FF
    call    #spi_tx8
    mov     result, transfer_data
    call    #print_hex_byte
    mov     result, #" "
    call    #uart_tx_char

    cmp     transfer_data, #$01  wz
    if_z    jmp #.got_response
    djnz    temp2, #.wait_response

    ' Timed out
    mov     result, #"T"
    call    #uart_tx_char
    jmp     #$

.got_response
    drvh    #SD_CS               ' Deselect card again
    mov     result, #"!"
    call    #uart_tx_char
    mov     result, #13
    call    #uart_tx_char
    mov     result, #10
    call    #uart_tx_char
    jmp     #$

' Transmit & receive 8-bit over SPI (MSB first)
spi_tx8
    mov     result, #0
    mov     bitcnt, #8
    mov     temp3, transfer_data
.txbit
    rcl     temp3, #1        wc
    drvc    #SD_MOSI
    drvl    #SD_CLK
    waitx   bit_delay
    drvh    #SD_CLK
    waitx   bit_delay
    testp   #SD_MISO        wc
    rcl     result, #1
    djnz    bitcnt, #.txbit
    drvh    #SD_MOSI             ' Idle MOSI high
    mov     transfer_data, result
    ret

' Print byte in RESULT as 2 hex chars
print_hex_byte
    mov     temp, result
    shr     temp, #4             ' High nibble
    mov     result, temp
    call    #print_nibble
    and     result, #$0F         ' Low nibble
    call    #print_nibble
    ret

print_nibble
    cmp     result, #10 wc
    if_b    add result, #"0"
    if_nc   add result, #"A"-10
    call    #uart_tx_char
    ret

' UART TX single character from RESULT
uart_tx_char
    wypin   result, #TX_PIN
.wait_tx
    rdpin   pr0, #TX_PIN wc
    if_c    jmp #.wait_tx
    ret

' Variables
bitcnt         res 1
temp           res 1
temp2          res 1
temp3          res 1
cnt            res 1
result         res 1
bit_delay      res 1
transfer_data  res 1

Comments

  • evanhevanh Posts: 16,379
    edited 2025-04-13 02:51

    Are you sure you're wanting to talk directly to the SD card and not rather use existing ready to go libraries instead? Flexspin, for example, comes with the well tested C libs that can also be used by Spin programs.

  • Hi @evanh!!

    Yes for now, it’s more about learning the assembly behind it than any kind of major project. If I need to I can just program anything I needed to use in production using C.

    I think the timing for the Sd card or spi might be a little more strict than anything I’ve seen before.

  • evanhevanh Posts: 16,379
    edited 2025-04-13 03:06

    Well, you might want to read the spin2 SD card driver, sdspi_bashed3.spin2, for FSRW or Kye's FAT32, I don't remember which API is which ... I fixed it up not long ago. The original had some missing checks in it that had made SD card support flaky prior to that.
    https://forums.parallax.com/discussion/comment/1563810/#Comment_1563810

  • evanhevanh Posts: 16,379
    edited 2025-04-13 04:15
        if op <> 12    ' not CMD12
            if not pinr(cs)    ' if already selected
                deselect()
            pinl(cs)    ' SELECT
            if not ready()
                deselect()
                return $ff    ' failed, timeout
    

    This snippet, at the head of the command routine, I think I took it from Flexspin's driver. It ensures a deselect-select cycle for every command except CMD12. And it also waits for the card to return from any busy activity. The old bashed driver didn't check anything I don't think.

    Also, be aware that SD cards can get confused and not respond to anything. Sometimes you just have to resocket it. Roger added a controlled power switch to his 4-bit wired add-on board that allows the driver to power cycle the SD card at its convenience. https://forums.parallax.com/discussion/174988/new-sd-mode-p2-accessory-board/p1
    And Rayman followed suit with his nice Propeller 2 Platform board https://forums.parallax.com/discussion/comment/1566287/#Comment_1566287

  • @evanh, Thanks for the suggestions! You're driver i must have missed some kind of way.

    @evanh said:
    Also, be aware that SD cards can get confused and not respond to anything. Sometimes you just have to resocket it.

    I'm re-writing a few things based on this and eliminating many assumptions in the code. I'm doing a few manual test before i send the $40 now. Any particular reason it gets confused? Does that mean that it might sometimes randomly drive something high or low? Or is is literally i need to take the card out and put it back in sometimes?

    Also i corrected the code in the print_hex. I was printing high high instead of high low i noticed after going block by block. So far, I have a check to

    Before:

    print_hex_byte
        mov     temp, result
        shr     temp, #4             ' High nibble
        mov     result, temp
        call    #print_nibble
        and     result, #$0F         ' Low nibble
        call    #print_nibble
        ret
    
    print_nibble
        cmp     result, #10 wc
        if_b    add result, #"0"
        if_nc   add result, #"A"-10
        call    #uart_tx_char
        ret 
    

    After:

    print_hex_byte
        mov     temp, result         ' Save the full byte
    
        mov     result, temp
        shr     result, #4
        and     result, #$0F         ' Isolate high nibble
        call    #print_nibble
    
        mov     result, temp
        and     result, #$0F         ' Isolate low nibble
        call    #print_nibble
    
        ret
    
        print_nibble
        cmp     result, #10 wc
        if_b    add result, #"0"
        if_nc   add result, #"A"-10
        call    #uart_tx_char
        ret
    
  • evanhevanh Posts: 16,379
    edited 2025-04-14 00:59

    There will always be a reason. But I was pretty green when modifying the SPI mode stuff. And since it was already a done thing I mostly just optimised the low level bit-shifting routines at the time. Flexspin's original bit-bashing was very slow.

    That said, SPI mode, unlike SD mode, is supposed to always respond to a command. Also, one can enter SPI mode from SD mode but not the reverse. That's one sure way to not get a response - Issue a SD mode CMD0 when already in SPI mode.

  • evanhevanh Posts: 16,379
    edited 2025-04-14 01:45

    I just tested your code. It works when the "Flash" EEPROM is disabled.

    Methods of sharing the pins is something that hasn't been documented by Parallax. The various developers have bumbled their ways through it. Easiest to just leave the EEPROM disabled for the moment.

  • evanhevanh Posts: 16,379
    edited 2025-04-14 05:13

    I got the scope out to figure out why the EEPROM was such a problem when you're using SPI cmode 3, it shouldn't conflict ... found bugs!!!

    First, you'd missed pre-shifting the tx byte, temp3, to the left end of the 32-bit register. You were just transmitting all zeros. I don't know why any combination appeared to work now.

    Next, transfer_data was being overwritten and therefore not holding its prior value, yet you were sending the command as if it did hold its value.

    Lastly, the "extra clocks" loop was completely skipping over the SD card's response so you'd not be seeing it even if the card had responded.

    The initial pin setup wasn't ensuring the pins got set high either. Although I don't think that actually caused any problems.

    Here's my hacked around version:

    con
    _CLKFREQ        = 180_000_000
    BAUD_RATE       = 2_000_000
    SERIAL_CLOCK    = (_CLKFREQ / BAUD_RATE)
    BIT_PERIOD      = ((SERIAL_CLOCK << 16) & $FFFFFC00) + (8 - 1)  ' P2 smartpin bit-period calc
    
    RX_PIN          = 63
    TX_PIN          = 62
    
    SD_CS           = 60
    SD_CLK          = 61
    SD_MOSI         = 59
    SD_MISO         = 58
    
    dat
        org 0
        asmclk
    
    main
        ' UART RX setup
        wrpin   ##(P_ASYNC_RX | P_HIGH_15K), #RX_PIN
        wxpin   ##BIT_PERIOD, #RX_PIN
        wypin   #1, #RX_PIN
        dirh    #RX_PIN
    
        ' UART TX setup
        wrpin   ##(P_ASYNC_TX | P_OE), #TX_PIN
        wxpin   ##BIT_PERIOD, #TX_PIN
        wypin   #1, #TX_PIN
        dirh    #TX_PIN
    
        ' SD pin directions
        wrpin   #0, #SD_CS
        wrpin   #0, #SD_CLK
        wrpin   #0, #SD_MOSI
        wrpin   #0, #SD_MISO
        drvh    #SD_CS               ' Deselect SD card
        drvh    #SD_CLK
        drvh    #SD_MOSI
        fltl    #SD_MISO            ' Let MISO float as input
    
        ' UART startup test
        mov     result, #"S"
        call    #uart_tx_char
        mov     result, #13         ' CR
        call    #uart_tx_char
        mov     result, #10         ' LF
        call    #uart_tx_char
    
        ' Set halfbit_delay for SPI clock (400 kHz)
        mov     halfbit_delay, ##(_CLKFREQ / 400_000 / 2 - 6)
    
        ' Send 80 dummy EEPROM clocks with CS high
        mov     temp, #80
    .eprom_dummy
        outl    #SD_CS
        waitx   halfbit_delay
        outh    #SD_CS
        waitx   halfbit_delay
        djnz    temp, #.eprom_dummy
    
        ' Send 80 dummy SD clocks with CS high
        mov     temp, #80
    .sd_dummy
        outl    #SD_CLK
        waitx   halfbit_delay
        outh    #SD_CLK
        waitx   halfbit_delay
        djnz    temp, #.sd_dummy
    
        ' Send CMD0 (GO_IDLE_STATE)
        drvl    #SD_CS               ' Select card
    
        mov     transfer_data, #$40  ' CMD0
        call    #spi_tx8
        mov     transfer_data, #0    ' Argument = 0
        call    #spi_tx8
        call    #spi_tx8
        call    #spi_tx8
        call    #spi_tx8
        mov     transfer_data, #$95  ' Valid CRC for CMD0
        call    #spi_tx8
    
            ' Wait for response 0x01 (Idle state)
            mov     temp2, #500
    .wait_response
            mov     transfer_data, #$FF
            call    #spi_tx8
            cmp     result, #$01   wz
            call    #print_hex_byte
            mov     result, #" "
            call    #uart_tx_char
    
    if_nz   djnz    temp2, #.wait_response
    
            drvh    #SD_CS               ' Deselect card again
    
            ' Timed out
    if_nz   mov     result, #"T"
    if_nz   call    #uart_tx_char
    if_nz   jmp     #$
    
    .got_response
            mov     result, #"!"
            call    #uart_tx_char
            mov     result, #13
            call    #uart_tx_char
            mov     result, #10
            call    #uart_tx_char
            jmp     #$
    
    
    ' Transmit & receive 8-bit over SPI (MSB first)
    spi_tx8
        mov     result, #0
        mov     bitcnt, #8
        setbyte temp3, transfer_data, #3
    .txbit
        rcl     temp3, #1   wc
        drvl    #SD_CLK
        drvc    #SD_MOSI
        waitx   halfbit_delay
        drvh    #SD_CLK
        waitx   halfbit_delay
        testp   #SD_MISO   wc
        rcl     result, #1
        djnz    bitcnt, #.txbit
        drvh    #SD_MOSI             ' Idle MOSI high
        ret   wcz    ' restore flags
    
    
    ' Print byte in RESULT as 2 hex chars
    print_hex_byte
        mov     temp, result         ' Save the full byte
    
        mov     result, temp
        shr     result, #4
        and     result, #$0F         ' Isolate high nibble
        call    #print_nibble
    
        mov     result, temp
        and     result, #$0F         ' Isolate low nibble
        call    #print_nibble
    
        ret   wcz    ' restore flags
    
    print_nibble
        cmp     result, #10 wc
        if_b    add result, #"0"
        if_nc   add result, #"A"-10
        call    #uart_tx_char
        ret   wcz    ' restore flags
    
    
    ' UART TX single character from RESULT
    uart_tx_char
        wypin   result, #TX_PIN
    .wait_tx
        rdpin   pr0, #TX_PIN wc
        if_c    jmp #.wait_tx
        ret   wcz    ' restore flags
    
    ' Variables
    bitcnt         res 1
    temp           res 1
    temp2          res 1
    temp3          res 1
    cnt            res 1
    result         res 1
    halfbit_delay  res 1
    transfer_data  res 1
    
  • @evanh > @evanh said:

    I just tested your code. It works when the "Flash" EEPROM is disabled.

    Methods of sharing the pins is something that hasn't been documented by Parallax. The various developers have bumbled their ways through it. Easiest to just leave the EEPROM disabled for the moment.

    That checks out. I've been able to find a lot of tutorials and after the post to help. There is one by a fellow named potato-head and a ray desilva. But I think the documentation is kind of spread out. At least I know I am not the only one that has stumbled in the dark a bit. :D

    @evanh said:
    I got the scope out to figure out why the EEPROM was such a problem when you're using SPI cmode 3, it shouldn't conflict ... found bugs!!!

    I may need to purchase one then and learn to use it. Most of the soldering an projects I have done have been from basic electronics books as a hobby. Any recommendations on said scope?

    Next, transfer_data was being overwritten and therefore not holding its prior value, yet you were sending the command as if it did hold its value.

    I did see this one along with some other things that were not needed. I am still cleaning that up but I am going to merge what you have presented with what I have and clean things up a bit.

    I think at this point I honestly want to shift directions on making things per and just do an unofficial document on learning the assembly stuff. At least someone coming behind me may not have to stumble as much. Thank you very much!

  • @proppy said:
    I may need to purchase one then and learn to use it. Most of the soldering an projects I have done have been from basic electronics books as a hobby. Any recommendations on said scope?

    IMO: Get one of those sub-10€/$ 8ch USB logic analyzers. They're not good per se, but infinite times better than poking around in the dark, amazing value.

  • evanhevanh Posts: 16,379

    @proppy said:
    I think at this point I honestly want to shift directions on making things per and just do an unofficial document on learning the assembly stuff. At least someone coming behind me may not have to stumble as much. Thank you very much!

    Your assembly ain't too bad really, just you've picked a messy problem to work with. I assume you've done a decent amount of assembly on other architectures already.

    That said, yes, do tidy it up. More structure will help. Assembly lacking scoping of variables makes it too easy to reuse them when they probably shouldn't be. All too guilty of that myself.

  • @Wuerfel_21 said:

    @proppy said:
    I may need to purchase one then and learn to use it. Most of the soldering an projects I have done have been from basic electronics books as a hobby. Any recommendations on said scope?

    IMO: Get one of those sub-10€/$ 8ch USB logic analyzers. They're not good per se, but infinite times better than poking around in the dark, amazing value.

    Hello @Wuerfel_21!

    Thank you! I'm going to go check that out this week then.

    @evanh said:

    @proppy said:
    I think at this point I honestly want to shift directions on making things per and just do an unofficial document on learning the assembly stuff. At least someone coming behind me may not have to stumble as much. Thank you very much!

    Your assembly ain't too bad really, just you've picked a messy problem to work with. I assume you've done a decent amount of assembly on other architectures already.

    Thank you. Yes, I am coming from the Z80/6520 assembly world, strictly hobby stuff. I was able to pick up a piece of equipment called the Hydra Game Development Kit. It is great really, though it uses the P1 chip, and got me interested in learning more about Parallax stuff.

    That said, yes, do tidy it up. More structure will help. Assembly lacking scoping of variables makes it too easy to reuse them when they probably shouldn't be. All too guilty of that myself.

    Will definitely do! Others have said I need to do the same so I think I really will now. Thank you again!

  • evanhevanh Posts: 16,379

    @proppy said:
    Thank you. Yes, I am coming from the Z80/6520 assembly world, strictly hobby stuff. I was able to pick up a piece of equipment called the Hydra Game Development Kit. It is great really, though it uses the P1 chip, and got me interested in learning more about Parallax stuff.

    The Propellers caught my attention because of the open multicore access to all the hardware, and using assembly is well supported and in an uncomplicated way. Hitting the metal in a multicore environment is encouraged! :)

  • evanhevanh Posts: 16,379

    @proppy said:

    @Wuerfel_21 said:

    @proppy said:
    I may need to purchase one then and learn to use it. Most of the soldering an projects I have done have been from basic electronics books as a hobby. Any recommendations on said scope?

    IMO: Get one of those sub-10€/$ 8ch USB logic analyzers. They're not good per se, but infinite times better than poking around in the dark, amazing value.

    Hello @Wuerfel_21!

    Thank you! I'm going to go check that out this week then.

    Get one that has a couple of analogue channels too. It's always good to be able to verify voltages and slopes to be sure the logic is powered properly.

Sign In or Register to comment.