我正在尝试通过 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。从左到右,设置的标志是:
我无法重现您所经历的事情。我没有 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。