AVR128DB28通过SPI与SD卡通信失败

问题描述 投票:0回答:1

我正在尝试通过 SPI 实现 AVR128DB28 微控制器和 SD 卡之间的通信。我在 Microchip Studio 中对其进行编程,编程是通过 UPDI 上的 MPLAB SNAP 完成的。该程序是 C 语言。

我的程序如下所示:

/*
 * AVR128_sdcardTEST.c
 *
 * Created: 15.04.2024 14:52:31
 * Author : Komputer_3
 */ 

#include <avr/io.h>
#include <avr/delay.h>
#include <stdint-gcc.h>

#define SPI_PORT    PORTA
#define MOSI        PIN4_bm
#define MISO        PIN5_bm
#define SCK         PIN6_bm
#define CS          PIN7_bm

#define CS_ENABLE()     SPI_PORT.OUTCLR = CS
#define CS_DISABLE()    SPI_PORT.OUTSET = CS

#define CMD0        0
#define CMD0_ARG    0x00000000
#define CMD0_CRC    0x94

uint8_t SPI_transfer(uint8_t data){
    SPI0.DATA = data;
    while (!(SPI0.INTFLAGS & SPI_IF_bm));
    return SPI0.DATA;
}

void SDC_powerUp(void){
    CS_DISABLE();
    _delay_ms(1);
    for(uint8_t i=0; i<10; i++){
        SPI_transfer(0xff);
    }
    CS_DISABLE();
    SPI_transfer(0xff);
}

void SDC_command(uint8_t cmd, uint32_t arg, uint8_t crc){
    SPI_transfer((cmd & 127) | 64); //0b01xxxxxx
    
    SPI_transfer((uint8_t)(arg >> 24));
    SPI_transfer((uint8_t)(arg >> 16));
    SPI_transfer((uint8_t)(arg >> 8));
    SPI_transfer((uint8_t)(arg));
    
    SPI_transfer(crc | 1);  //0bxxxxxxx1
}

uint8_t SDC_readRes1(){
    uint8_t i = 0, res1;
    // keep polling until actual data received
    while((res1 = SPI_transfer(0xFF)) == 0xFF){ // ERROR: returns 0 instead of 1
        i++;

        // if no data received for 8 bytes, break
        if(i > 8) break;
    }

    return res1;
}

uint8_t SD_goIdle(){
    // assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // send CMD0
    SDC_command(CMD0, CMD0_ARG, CMD0_CRC);

    // read response
    uint8_t res1 = SDC_readRes1();

    // deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

int main(void)
{
    _PROTECTED_WRITE(CLKCTRL.OSCHFCTRLA,  (CLKCTRL.OSCHFCTRLA & (~CLKCTRL_FRQSEL_gm)) | CLKCTRL_FRQSEL_24M_gc);
    
    SPI_PORT.DIRSET = MOSI | SCK | CS;
    SPI_PORT.DIRCLR = MISO;
    
    PORTD.DIRSET = PIN1_bm | PIN2_bm | PIN3_bm | PIN4_bm | PIN5_bm;
    
    SPI0.CTRLA =    (1 << SPI_MASTER_bp)    |       // Tryb Master
                    (0 << SPI_DORD_bp)      |       /* kierunek wysylania bitow: 0-MSB, 1-LSB */
                    (1 << SPI_CLK2X_bp)     |       /* podwojenie predkosci */
                    SPI_PRESC_DIV4_gc       |       /* Preskaler */
                    SPI_ENABLE_bm;                  /* wlacz SPI */
    SPI0.CTRLB = SPI_SSD_bm | SPI_MODE_0_gc;
    /* Select Slave Disable pin is set, according to he docs I linked, 
    when you are not using multi-master mode. Since I'm not, the CS/SS 
    pin is not under control of the SPI register and instead I control 
    it by manually changing the pins state: the CS_ENABLE and DISABLE 
    are #defined as PORTA.OUTSET = PIN7_bm and opposite */
    
    _delay_ms(500);
    
    SDC_powerUp();
    uint8_t res1 = SD_goIdle();
    if(res1 == 1){
        PORTD.OUTSET = PIN5_bm;
    }
    else{
        PORTD.OUTSET = res1 & 0b00001111;
        _delay_ms(1500);
        PORTD.OUTCLR = 0b00001111;
        PORTD.OUTSET = (res1 >> 4) & 0b00001111;
    }
    
    while (1) 
    {
    }
}


这是该网站代码的改编版本: http://www.rjhcoding.com/avrc-sd-interface-1.php
这是问题部分:

int8_t SDC_readRes1(){
    uint8_t i = 0, res1;
    // keep polling until actual data received
    while((res1 = SPI_transfer(0xFF)) == 0xFF){ // ERROR: returns 0 instead of 1
        i++;

        // if no data received for 8 bytes, break
        if(i > 8) break;
    }

    return res1;
}

简而言之,向 SD 卡发送 CMD0 命令,将卡的状态更改为空闲,应产生值为 0x01 或 0b00000000 的 R1 响应格式。最左边的位总是0,最右边的一位是“处于空闲状态”,其余的是错误标志。回报是0。

如果您需要,这里是微型数据表的链接: https://www.microchip.com/en-us/product/avr128db28#document-table(这是一个可下载 pdf 的网站的链接)

编辑:

上电复位(拔下电源然后再插入电源)后,卡会响应 0b00010111。从左到右,设置的标志是:

  1. 擦除序列错误 - 擦除命令序列发生错误。
  2. 非法命令 - 命令代码不正确。
  3. 擦除重置 - 擦除序列在执行前被清除,因为收到了擦除序列外命令。
  4. 空闲状态 - 卡重置并空闲。
microcontroller avr sd-card spi avr-gcc
1个回答
0
投票

我无法重现您所经历的事情。我没有 AVR128DB28,但我在 ATtiny3227 上运行您的代码,以 20MHz 和 3.3V 的时钟速度运行(因为 SD 卡是 3.3V 设备),SPI 的时钟频率为 5MHz。我能够持续获得

0x01
的 R1 回报。这是逻辑分析仪输出的屏幕截图。

我没有更改

SPI_transfer()
SDC_command()
SDC_readRes1()
上的任何代码。我减少了选择和取消选择 CS 引脚之前和之后不必要的延迟,因为这些延迟是完全没有必要的。我没有调用
SDC_powerUp()
,因为上电时已经有
_delay_ms(500)
,并且电压足以上升到发送 CMD0 的水平。总的来说,我的时间比你的代码更紧。

#include <avr/io.h>
#include <util/delay.h>

#define CMD0        0
#define CMD0_ARG    0x00000000UL
#define CMD0_CRC    0x94

static uint8_t SPI_master_init() {

    PORTA.OUTSET = PIN4_bm;                     // Set SS pin high to prevent accidential trigger
    PORTA.DIR |= (PIN1_bm | PIN3_bm | PIN4_bm); // Set MOSI, SCK, and SS as OUTPUT
    PORTA.DIRCLR = PIN2_bm;                     // Set MISO as INPUT
    SPI0.CTRLA = 
        SPI_MASTER_bm |           // Master mode
        SPI_PRESC_DIV4_gc |       // SPI_CLOCK = F_CPU / 4
        SPI_ENABLE_bm &           // Enable SPI
        (~SPI_DORD_bm);           // MSB first
    SPI0.CTRLB = SPI_MODE_0_gc;   // no buffer, SSD bit disabled, mode 0

}

uint8_t SPI_transfer(uint8_t data)
{
  // same as OP's code
}

void SDC_command(uint8_t cmd, uint32_t arg, uint8_t crc){
    // same as OP's code
}

uint8_t SDC_readRes1(){
    // same as OP's code 
}

int main() {

    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, !CLKCTRL_PEN_bm);    // disable prescaler

    SPI_master_init();
    
    _delay_ms(250);

    PORTA.OUTCLR = PIN4_bm;                 // enable chip select
    SDC_command(CMD0, CMD0_ARG, CMD0_CRC);  // send soft-reset cmd0
    uint8_t res1 = SDC_readRes1();          // get r1 reply
    PORTA.OUTSET = PIN4_bm;                 // disable chip select

    while(1) {

    }

    return 0;

}

我注意到http://www.rjhcoding.com/avrc-sd-interface-1.php上的SD卡连接图显示了带有8针的SD,如果不是完全错误的话,这是相当误导的,SD卡有 9 针,正确的连接应如 http://elm-chan.org/docs/mmc/mmc_e.html#pinout

我用于测试的 SD 卡是相当旧的 Kingston 4GB SDHC Class 4。

© www.soinside.com 2019 - 2024. All rights reserved.