stm32读写SD卡(SPI模式)

这篇具有很好参考价值的文章主要介绍了stm32读写SD卡(SPI模式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、SD卡简介

二、源码下载

三、移植条件

1、芯片参数

2、硬件连接

四、驱动代码

1、依赖宏如下

2、驱动代码实现

3、测试代码

4、运行截图


一、SD卡简介

SD卡有SD驱动模式和SPI驱动模式,本例中使用SPI模式驱动SD卡。

二、源码下载

https://download.csdn.net/download/qq_30095023/88014550

三、移植条件

1、芯片参数

芯片类型:STM32F103VET6。

flash大小为512KB,RAM大小 64KB。

2、硬件连接

本例使用TFT屏幕上的SD卡插口,测试所用SD卡容量大小为4GB。

spi sd卡,透明的stm32,stm32,嵌入式硬件,单片机

 SD_CS、SPI_MOSI、SPI_MISO、SPI_CLK 与单片机连接。note:请确保屏幕电源连接正常。

spi sd卡,透明的stm32,stm32,嵌入式硬件,单片机

如上图,红色框框所标记区域为TFT屏幕SD卡的SPI接口,将此接口与STM32单片机的SPI所对应的IO连接。

四、驱动代码

note: u8 、u16、u32等为自定义数据类型,编译报错请按需修改。LOG_XX为日志打印,请按需替换。

1、依赖宏如下

定义一些操作SD卡的命令和SD卡的类型。

//CMD定义
#define CMD0        0//卡复位
#define CMD1        1
#define CMD8        8//命令8 ,SEND_IF_COND
#define CMD9        9//命令9 ,读CSD数据
#define CMD10       10//命令10,读CID数据
#define CMD12       12//命令12,停止数据传输
#define CMD16       16//命令16,设置SectorSize 应返回0x00
#define CMD17       17//命令17,读sector
#define CMD18       18//命令18,读Multi sector
#define CMD23       23//命令23,设置多sector写入前预先擦除N个block
#define CMD24       24//命令24,写sector
#define CMD25       25//命令25,写Multi sector
#define CMD41       41//命令41,应返回0x00
#define CMD55       55//命令55,应返回0x01
#define CMD58       58//命令58,读OCR信息
#define CMD59       59//命令59,使能/禁止CRC,应返回0x00

//SD卡类型
#define ERR         0x00
#define MMC         0x01
#define V1          0x02
#define V2          0x04
#define V2HC        0x06

2、驱动代码实现

1)、ENTER_SD_BLOCK_SIZE定义SD卡的物理扇区大小为512Byte。

2)、函数SPI_Enable_T用来使能SPI接口,在SDinit的时候需要调用。

3)、函数inline u8 SPI_WriteByte_T用来单片机通过SPI读写SD卡,这里使用内联方式,减少函数调用过程中时间代价,有效提高SD卡的读写速率。关于inline关键字的详细用法请参考:

C语言基础知识--inline(内联)关键字_BIN-XYB的博客-CSDN博客

4)、 函数b_sd_read_write_byte用来读写数据。

5)、函数b_sd_send_cmd用来发送指令给SD卡。

6)、函数b_sd_read_data用来从SD卡读取指定长度的数据。

7)、函数b_sd_send_block_data用来向SD卡写入数据。

8)、函数b_sd_set_speed用来配置SPI通信速度。

9)、函数b_sd_init用来初始化SD卡,需要在使用SD卡前调用一次。

10)、函数b_sd_get_cid用来查询SD卡的CID。

11)、函数b_sd_get_csd用来查询SD卡的CSD。

12)、函数b_sd_read_sector用来读扇区数据,该接口一般用于FatFs文件系统。

13)、函数b_sd_write_sector用来写扇区数据,该接口一般用于FatFs文件系统。

FatFs文件系统移植请参考:FatFs移植到STM32(SD卡)_BIN-XYB的博客-CSDN博客

14)、函数b_sd_get_sector_number用来查询SD卡的扇区数量。

15)、函数b_sd_sync_data用来保存SD卡的数据。

完整源码如下:

#define ENTER_SD_BLOCK_SIZE      (512)
#define ENTER_SD_SECTOR_SIZE     (ENTER_SD_BLOCK_SIZE)


#define GPIO_TYPE      GPIOD   //GPIO组类型
#define SD_CS          SD_CS_Pin//片选引脚PD8
#define SPI            hspi2   //spi设备

#define SD_CS_DISABLE()         GPIO_TYPE->BSRR=SD_CS//GPIO置位(拉高)
#define SD_CS_ENABLE()          GPIO_TYPE->BRR=SD_CS//GPIO复位(拉低)

static u8 SPI_Enable_T(SPI_HandleTypeDef *hspi)
{
    if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
    {
      __HAL_SPI_ENABLE(hspi);
    }
    return 0;
}

static inline u8 SPI_WriteByte_T(SPI_HandleTypeDef* hspi,u8 Byte)
{
    while((hspi->Instance->SR&SPI_FLAG_TXE)==RESET);//等待发送区空
    hspi->Instance->DR=Byte;//发送一个byte 
    while((hspi->Instance->SR&SPI_FLAG_RXNE)==RESET);//等待接收完一个byte
    return hspi->Instance->DR;//返回收到的数据
}

#define SPI_WriteByte(spi_x, data) SPI_WriteByte_T(&spi_x, data)
#define SPI_Enable(spi_x) SPI_Enable_T(&spi_x)

u8 b_sd_read_write_byte(u8 tx_data)
{
    u8 rx_data = SPI_WriteByte(SPI, tx_data);
    return rx_data;
}

static int b_sd_send_cmd(u8 cmd,u32 arg, u8 crc)
{
    u8 r1;
    u8 retry;

    SD_CS_DISABLE();
    b_sd_read_write_byte(0XFF);
    SD_CS_ENABLE();
    do
    {
        retry = b_sd_read_write_byte(0XFF);
    }while(retry!=0xFF);

    b_sd_read_write_byte(cmd | 0x40);
    b_sd_read_write_byte(arg >> 24);
    b_sd_read_write_byte(arg >> 16);
    b_sd_read_write_byte(arg >> 8);
    b_sd_read_write_byte(arg);
    b_sd_read_write_byte(crc);

  if(cmd == CMD12)
    {
        b_sd_read_write_byte(0XFF);
    }
    do
    {
        r1 = b_sd_read_write_byte(0xFF);
    }while(r1 & 0X80);

    return r1;
}

static u8 sd_type = 0;
static u8 b_sd_read_data(u8 *data, u16 len)
{
    u8 r1 = 0;
    SD_CS_ENABLE();
    do
    {
        r1 = b_sd_read_write_byte(0xFF);
    }while(r1 != 0xFE);
    
    while(len--)
    {
        *data = b_sd_read_write_byte(0xFF);
        data++;
    }
    b_sd_read_write_byte(0xFF);
    b_sd_read_write_byte(0xFF);
    return 0;
}

static u8 b_sd_send_block_data(u8* data, u8 cmd)
{
    u16 t;
    u8 r1;
    do{
        r1 = b_sd_read_write_byte(0xFF);
    }while(r1 != 0xFF);

    b_sd_read_write_byte(cmd);
    if(cmd != 0XFD)//不是结束指令
    {
        for(t = 0; t < ENTER_SD_BLOCK_SIZE; t++)
        {
            b_sd_read_write_byte(data[t]);//提高速度,减少函数传参时间
        }
        b_sd_read_write_byte(0xFF);//忽略crc
        b_sd_read_write_byte(0xFF);
        t = b_sd_read_write_byte(0xFF);//接收响应
        if((t & 0x1F) != 0x05)
        {
            return 2;//响应错误
        }
    }
    return 0;//写入成功
}

inline void b_sd_set_speed(u32 speed)
{
    hspi2.Init.BaudRatePrescaler = speed;
}

u8 b_sd_init(void)
{
    u8 r1;
    u8 buff[6] = {0};
    u16 retry; 
    u8 i;

    SPI_Enable(SPI);

    b_sd_set_speed(SPI_BAUDRATEPRESCALER_256);
    SD_CS_DISABLE();
    for(retry = 0; retry < 10; retry++)
    {
        b_sd_read_write_byte(0XFF);
    }
    
    do//SD卡进入IDLE状态
    {
        r1 = b_sd_send_cmd(CMD0 ,0, 0x95);
    }while(r1 != 0x01);
    
    //查看SD卡的类型
    sd_type = 0;
    r1 = b_sd_send_cmd(CMD8, 0x1AA, 0x87);
    if(r1 == 0x01)
    {
        for(i = 0; i < 4; i++)
        {
            buff[i] = b_sd_read_write_byte(0XFF);//Get trailing return value of R7 resp
        }
        if(buff[2] == 0X01 && buff[3] == 0XAA)//卡是否支持2.7~3.6V
        {
            retry = 0XFFFE;
            do
            {
                b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
                r1 = b_sd_send_cmd(CMD41, 0x40000000, 0X01);//发送CMD41
            }while(r1 && retry--);
            if(retry && b_sd_send_cmd(CMD58, 0, 0X01) == 0)//鉴别SD2.0卡版本开始
            {
                for(i = 0; i < 4; i++)
                {
                    buff[i] = b_sd_read_write_byte(0XFF);//得到OCR值
                }
                if(buff[0] & 0x40)
                {
                    sd_type = V2HC;
                }else {
                    sd_type = V2;
                }
            }
        }
        else
        {
            b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
            r1 = b_sd_send_cmd(CMD41, 0, 0X01);//发送CMD41
            if(r1 <= 1)
            {
                sd_type = V1;
                retry = 0XFFFE;
                do //等待退出IDLE模式
                {
                    b_sd_send_cmd(CMD55, 0, 0X01);//发送CMD55
                    r1 = b_sd_send_cmd(CMD41,0,0X01);//发送CMD41
                }while(r1 && retry--);
            }else//MMC卡不支持CMD55+CMD41识别
            {
                sd_type = MMC;//MMC V3
                retry = 0XFFFE;
                do //等待退出IDLE模式
                {
                    r1 = b_sd_send_cmd(CMD1, 0, 0X01);//发送CMD1
                }while(r1 && retry--);
            }
            if(retry == 0 || b_sd_send_cmd(CMD16, 512, 0X01) != 0)
            {
                sd_type = 0;//错误的卡
            }
        }
    }
    SD_CS_DISABLE();
    b_sd_set_speed(SPI_BAUDRATEPRESCALER_2);
    if(sd_type)
    {
        return HAL_OK;
    }
    else
    {
        return HAL_ERROR;
    }
}

u8 b_sd_get_cid(u8* cid_data)
{
    u8 r1 = b_sd_send_cmd(CMD10, 0, 0x01);//读取CID寄存器
    if(r1 == 0x00)
    {
        r1 = b_sd_read_data(cid_data,16);
    }
    SD_CS_DISABLE();
    if(r1 != 0) 
    {
        return HAL_ERROR;
    }
    else
    {
        return HAL_OK;
    }
}

u8 b_sd_get_csd(u8 *csd_data)
{
    u8 r1 = b_sd_send_cmd(CMD9, 0, 0x01);//发CMD9命令,读CSD寄存器
    if(r1 == 0)
    {
        r1 = b_sd_read_data(csd_data, 16);//接收16个字节的数据
    }
    SD_CS_DISABLE();//取消片选
    if(r1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

s32 b_sd_read_sector(u32 sector, u8 number, u8*data, u32 timeout)
{
    u8 r1;
    if(sd_type != V2HC)
    {
        sector <<= 9;//转换为字节地址
    }
    if(number == 1)
    {
        r1 = b_sd_send_cmd(CMD17, sector, 0X01);//读命令
        if(r1 == 0)//指令发送成功
        {
            r1 = b_sd_read_data(data, ENTER_SD_SECTOR_SIZE);//接收512个字节
        }
    }
    else
    {
        r1 = b_sd_send_cmd(CMD18, sector, 0X01);//连续读命令
        do
        {
            r1 = b_sd_read_data(data, ENTER_SD_SECTOR_SIZE);//接收512个字节
            data += ENTER_SD_SECTOR_SIZE;
        }while(--number && r1 == 0);
        b_sd_send_cmd(CMD12, 0, 0X01);//发送停止命令
    }
    SD_CS_DISABLE();//取消片选
    return HAL_OK;
}


s32 b_sd_write_sector(u32 sector, u8 number, u8 *data, u32 timeout)
{
    u8 r1;
    if(sd_type != V2HC)
    {
        sector *= ENTER_SD_SECTOR_SIZE;//转换为字节地址
    }

    if(number == 1)
    {
        r1 = b_sd_send_cmd(CMD24, sector, 0X01);//读命令
        if(r1 == 0)//指令发送成功
        {
            r1 = b_sd_send_block_data(data, 0xFE);//写512个字节
        }
    }else
    {
        if(sd_type != MMC)
        {
            b_sd_send_cmd(CMD55, 0, 0X01);
            b_sd_send_cmd(CMD23, number, 0X01);//发送指令
        }
        r1 = b_sd_send_cmd(CMD25, sector, 0X01);//连续读命令
        if(r1 == 0)
        {
            do
            {
                r1 = b_sd_send_block_data(data, 0xFC);//接收512个字节
                data += ENTER_SD_SECTOR_SIZE;
            }while(--number && r1 == 0);
            r1 = b_sd_send_block_data(0, 0xFD);//接收512个字节
        }
    }   
    SD_CS_DISABLE();//取消片选
    return HAL_OK;
}

u32 b_sd_get_sector_number(void)
{
    u8 csd[16];
    u32 capacity;
    u16 csize;

    //取CSD信息,如果期间出错,返回0
    if(b_sd_get_csd(csd)!=0)
    {
        return 0;
    }
    //如果为SDHC卡,按照下面方式计算
    if((csd[0] & 0xC0) == 0x40)//V2.00的卡
    {
        csize = csd[9] + ((u16)csd[8] << 8) + 1;
        capacity = (u32)csize << 10;//得到扇区数
    }
    else//V1.XX的卡
    {
        u8 n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
        csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
        capacity= (u32)csize << (n - 9);//得到扇区数
    }
    return capacity;
}

u32 b_sd_get_sector_size(void)
{
    return ENTER_SD_SECTOR_SIZE;
}

u32 b_sd_get_block_size(void)
{
    return ENTER_SD_BLOCK_SIZE;
}

u32 b_sd_sync_data(void)
{
    SD_CS_ENABLE();//片选
    do{
        HAL_Delay(5);
    }while(b_sd_read_write_byte(0xFF)!=0xFF);
    SD_CS_DISABLE();//取消片选
    return 0;
}

3、测试代码

void b_sd_test_demo(void)
{
    static u8 test_buffer[ENTER_SD_SECTOR_SIZE] = {0};
    LOG_INFO("start b_sd_init\r\n");
    u8 ret = b_sd_init();
    u8 *p = test_buffer;
    LOG_INFO("b_sd_init:%d\r\n", sd_type);
    
    memset(test_buffer, 0, ENTER_SD_SECTOR_SIZE);
    ret = b_sd_get_cid(test_buffer);
    LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[0],p[1],p[2],p[3]);
    LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[4],p[5],p[6],p[7]);
    LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[8],p[9],p[10],p[11]);
    LOG_INFO("cid:%02X,%02X,%02X,%02X\r\n", p[12],p[13],p[14],p[15]);
    LOG_INFO("b_sd_get_cid:%d\r\n", ret);
    
    memset(test_buffer, 0, ENTER_SD_SECTOR_SIZE);
    ret = b_sd_get_csd(test_buffer);
    LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[0],p[1],p[2],p[3]);
    LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[4],p[5],p[6],p[7]);
    LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[8],p[9],p[10],p[11]);
    LOG_INFO("csd:%02X,%02X,%02X,%02X\r\n", p[12],p[13],p[14],p[15]);
    LOG_INFO("b_sd_get_csd:%d\r\n", ret);

    u32 num = b_sd_get_sector_number();
    LOG_INFO("b_sd_get_sector_number:%d\r\n", num);
}

 测试代码先sd初始化,然后查询cid,csd,再查询SD容量。

4、运行截图

spi sd卡,透明的stm32,stm32,嵌入式硬件,单片机

可以看到,成功查到SD数据。文章来源地址https://www.toymoban.com/news/detail-729211.html

到了这里,关于stm32读写SD卡(SPI模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SPI方式读写SD卡速度有多快?

    很久没有写公众号了,一方面忙,另一方面也不知道写些什么内容,大家如果有想了解的(前提是我也懂),可以后台发送给我。 今天主要来测试一下SPI读写SD卡的速度。SD卡是一个嵌入式中非常常用的外设,可以用于存储一些大容量的数据。但用单片机读写SD卡速度一般都有

    2024年02月15日
    浏览(39)
  • STM32嵌入式系统:将数据保存到SD卡的操作

    STM32嵌入式系统:将数据保存到SD卡的操作 嵌入式系统在现代科技中扮演着重要角色,而STM32单片机是一种常用的嵌入式系统解决方案。本文将介绍如何使用STM32单片机将采集到的数据以TXT文件的格式保存到SD卡中,并且能够方便地读取这些本地数据。 硬件准备 为了实现数据保

    2024年02月01日
    浏览(45)
  • stm32读写SD卡(SDIO模式)

    目录 一、SD卡简介 二、源码下载 三、移植条件 1、芯片参数 2、硬件连接 四、驱动代码 1、依赖宏如下 2、驱动代码实现 3、测试代码 4、运行截图 五、总结 SD卡有SDIO驱动模式和SPI驱动模式,本例中使用SDIO模式驱动SD卡。 https://download.csdn.net/download/qq_30095023/88702705 https://downl

    2024年01月24日
    浏览(49)
  • STM32F429 Discovery开发板应用:实现SPI-SD Card文件写入(搭载FatFS文件系统)

    MCU:STM32F429ZIT6 开发环境:STM32CubeMX+MDK5   外购了一个SPI接口的SD Card模块,想要实现SD卡存储数据的功能。 首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。   此时,双击完后会关闭此界面,然后打开一个新界面。

    2024年02月08日
    浏览(59)
  • STM32CubeMX系列09——SDIO(SD卡读写、SD卡移植FATFS文件系统)

    ==== 文章汇总(有代码汇总) ==== 准备看看这方面的知识,一时间还没不清有什么区别,先补补课,不需要的跳过。 参考文章(内容来源):http://www.360doc.com/content/21/1125/22/59057945_1005908465.shtml 主要写这两个:SD卡、TF卡 共同点:SD、TF、MMC都是在MMC基础上演化发展不同的规范,

    2024年02月09日
    浏览(49)
  • 物联网开发笔记(96)- Micropython ESP32开发之SPI接口控制Micro SD卡TF卡模块挂载内存卡

    一、目的         这一节我们学习如何使用乐鑫的ESP32开发板连接SD卡模块,进行目录、文件的相关操作。         在早前我们也介绍过TFT SD卡的操作,这里我们重新复习一下。 物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块_microsd卡

    2024年01月25日
    浏览(48)
  • STM32CubeMX教程27 SDIO - 读写SD卡

    正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 逻辑分析仪nanoDLA 野火DAP仿真器 XCOM V2.6串口助手 使用STM32CubeMX软件配置STM32F407开发板 SDIO读写4线SD卡,实现轮询方式读写SD卡、以中断方式读取SD卡和以DMA方式读取SD卡 安全数码

    2024年02月19日
    浏览(67)
  • 【STM32学习】——SPI通信协议&SPI时序&W25Q64存储芯片&软件SPI读写

    目录 前言 一、SPI通信协议 1.概述​ 2.硬件电路  3.移位示意图 二、SPI时序 1.时序基本单元 2.完整时序波形 三、W25Q64存储芯片 1.芯片简介  2.硬件电路引脚定义  3.芯片框图 4.Flash操作注意事项 四、软件SPI读写W25Q64 五、SPI通信外设 总结 声明:学习笔记来自江科大自化协B站教

    2024年02月09日
    浏览(62)
  • STM32—SPI详解入门(使用SPI通讯读写W25Q128模块)

    目录 一、SPI是什么 二、SPI物理架构 三、SPI工作原理 四、SPI工作模式 五、SPI相关寄存器介绍 六、SPI用到的结构体与函数 1.结构体 2.函数 七、W25Q128芯片 1.W25Q128介绍 2.W25Q128存储架构 3.W25Q128常用指令 4.W25Q128状态寄存器 5.W25Q128常见操作流程 八、实验(使用SPI通讯读写W25Q128模块

    2024年02月14日
    浏览(53)
  • STM32CubeMX教程28 SDIO - 使用FatFs文件系统读写SD卡

    正点原子stm32f407探索者开发板V2.4 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 野火DAP仿真器 XCOM V2.6串口助手 使用STM32CubeMX软件配置STM32F407开发板 SDIO使用FatFs中间件读写4线SD卡,并实现以轮询方式读写SD卡或以DMA方式读取SD卡 FatFs文件系统相关知识请读者

    2024年02月19日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包