IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> nRF52832学习记录(十二、SPI接口的应用 Micro SD卡读写测试) -> 正文阅读

[嵌入式]nRF52832学习记录(十二、SPI接口的应用 Micro SD卡读写测试)

1、nRF52xx SPI介绍

SPI接口基础介绍

SPI,支持全双工通信,我们这里主要介绍一下nRF52系列处理器上的SPI资源。
在nRF52832处理器中,有3个SPI 模块,SPI0 、SPI1、SPI2。
如果 SPI 作为主机使用 带有 EasyDMA 则称为 SPIM。
如果 SPI 作为从机使用 带有 EasyDMA 则称为 SPIS。

SPI的4根线:

  • SDO/MOSI – 主设备数据输出,从设备数据输入;
  • SDI/MISO – 主设备数据输入,从设备数据输出;
  • SCLK – 时钟信号,由主设备产生;
  • CS/SS – 从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高(一般来说是低电平选中)。

SPi的四种模式:

CPOL=0,表示当SCLK=0时处于空闲态,空闲低电平,所以有效状态就是SCLK处于高电平时
CPOL=1,表示当SCLK=1时处于空闲态,空闲高电平,所以有效状态就是SCLK处于低电平时
CPHA=0,表示数据采样是在第1个边沿
CPHA=1,表示数据采样是在第2个边沿

主设备能够控制时钟,因为SPI通信并不像UART或者IIC通信那样有专门的通信周期,起始信号,结束信号;所以SPI协议能够通过控制时钟信号线,当没有数据交流的时候我们的时钟线要么是保持高电平要么是保持低电平。

不同的从设备可能在出厂时由工厂配置为某种通信模式,无法改变,所以我们得根据从设备的通讯模式来设置主设备的模式。

我们还是从基本的SPI设备使用介绍开始学习,以后应用需要使用到EasyDMA再做相应的记录。

nRF52832的SPI模式
在这里插入图片描述

SPI 寄存器

注意 SPI0、SPI1 和 TWI0、TWI1 共有基础地址,两者不能同时使用
在这里插入图片描述

SPI 库函数介绍

先熟悉一下需要用到的库函数,SPI的库函数结构 和 I2C 基本上一致

SPI初始化函数

nrf_drv_spi_init有4个参数:
第一个参数选择使用哪个 SPI 模块(3个) 如果使用 EasyDMA 就是SPIM;
第二个参数是 SPI 的配置;
第三个参数是 自定义的事件处理函数,如果设置为NULL, 则使能 SPI 的阻塞模式;
第四个参数 写 NULL暂时,不太清楚,也是处理函数。

/*
第一个参数就是定义使用哪一个 SPI 或者是 SPIM(如果使用EasyDMA的话):
typedef struct
{
    uint8_t inst_idx;
    union
    {
#ifdef SPIM_PRESENT
        nrfx_spim_t spim;
#endif
#ifdef SPI_PRESENT
        nrfx_spi_t  spi;
#endif
    } u;
    bool    use_easy_dma;
} nrf_drv_spi_t;

nrf_drv_spi_t 结构体中的 nrfx_spi_t 结构体:
typedef struct
{
    NRF_SPI_Type * p_reg;        ///< Pointer to a structure with SPI registers.
    uint8_t        drv_inst_idx; ///< Driver instance index.
} nrfx_spi_t;

nrfx_spi_t 结构体中的  NRF_SPI_Type 就是对应的 SPI 的每个寄存器

第二个参数定义 SPI 的配置:

typedef struct
{
    uint8_t sck_pin;      ///< SCK pin number.
    uint8_t mosi_pin;     ///< MOSI pin number (optional).
                          /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
                           *   if this signal is not needed. /
    uint8_t miso_pin;     ///< MISO pin number (optional).
                          /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
                           *   if this signal is not needed. /
    uint8_t ss_pin;       ///< Slave Select pin number (optional).
                          /**< Set to @ref NRF_DRV_SPI_PIN_NOT_USED
                           *   if this signal is not needed. The driver
                           *   supports only active low for this signal.
                           *   If the signal should be active high,
                           *   it must be controlled externally. /
    uint8_t irq_priority; ///< Interrupt priority.
    uint8_t orc;          ///< Over-run character.
                          /**< This character is used when all bytes from the TX buffer are sent,
                               but the transfer continues due to RX. /
    nrf_drv_spi_frequency_t frequency; ///< SPI frequency.
    nrf_drv_spi_mode_t      mode;      ///< SPI mode.
    nrf_drv_spi_bit_order_t bit_order; ///< SPI bit order.
} nrf_drv_spi_config_t;

其中nrf_drv_spi_bit_order_t bit_order; ///< SPI bit order.
表示每个数据是高位先发还是地位先发

第三个参数是 用户提供的事件处理程序,如果为空,使用阻塞模式。

第四个参数是 传递给事件处理的上下文。

整体结构和I2C一样

*/
ret_code_t nrf_drv_spi_init(nrf_drv_spi_t const * const p_instance,
                            nrf_drv_spi_config_t const * p_config,
                            nrf_drv_spi_evt_handler_t    handler,
                            void *                       p_context)

nrf_drv_spi_config_t SPI默认配置如下:

#define NRF_DRV_SPI_DEFAULT_CONFIG                           \
{                                                            \
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \
    .orc          = 0xFF,                                    \
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \
    .mode         = NRF_DRV_SPI_MODE_0,                      \
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \
}

SPI数据传输函数

nrf_drv_spi_transfer有5个参数:
第一个参数,和上面介绍的一样,哪个SPI 模块;
第二个参数 ,指向传输缓冲区的指针;
第三个参数,为 传输缓冲区的长度;
第四个参数,指向接收缓冲区的指针;
第五个参数,为 接收缓冲区的长度;

/*
第一个参数, 和初始化一样
使用哪一个 SPI 或者是 SPIM(如果使用EasyDMA的话):

第二个参数指向传输缓冲区的指针。没有则可以为空。

第三个数据 为 传输缓冲区的长度

第四个数据 接收缓冲区

5长度

*/
__STATIC_INLINE
ret_code_t nrf_drv_spi_transfer(nrf_drv_spi_t const * const p_instance,
                                uint8_t const * p_tx_buffer,
                                uint8_t         tx_buffer_length,
                                uint8_t       * p_rx_buffer,
                                uint8_t         rx_buffer_length)

2、nRF52xx SPI 使用示例

w25qxx SPI Flash读写

这里只放Flash的驱动部分.c文件部分,至于里面的CMD,是在.h文件里面定义的,不同的SPI设备不同,这里是为了给出使用示例,所以不贴全部代码,代码是 清风蓝牙教程的demo:

w25qxx.c 驱动

#include <string.h>
#include "nrf_drv_common.h"
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "w25q16.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "nrf_delay.h"

#define SPI_INSTANCE  0 /**< SPI instance index. */


static volatile bool spi_xfer_done;  //SPI数据传输完成标志
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */

static uint8_t    spi_tx_buf[256];   /**< TX buffer. */
static uint8_t    spi_rx_buf[256];   /**< RX buffer. */


/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
  spi_xfer_done = true;
}


void hal_spi_init(void)
{
//	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
//  spi_config.ss_pin = SPI_CS_PIN;
//  APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));	
		
	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}

/*****************************************************************************
** 描  述:读出一个字节
** 入  参:无
** 返回值:读出的数据
******************************************************************************/
uint8_t SpiFlash_ReadOneByte(void) 
{
    uint8_t len = 1;

	spi_tx_buf[0] = 0xFF;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
	while(!spi_xfer_done);
    return (spi_rx_buf[0]);
}
/*****************************************************************************
** 描  述:写入一个字节
** 入  参:Dat:待写入的数据
** 返回值:无
******************************************************************************/
void SpiFlash_WriteOneByte(uint8_t Dat)
{
    uint8_t len = 1;
	
    spi_tx_buf[0] = Dat;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
    while(!spi_xfer_done);
}

/*****************************************************************************
** 描  述:写入命令
** 入  参:*CMD:指向待写入的命令
** 返回值:RET_SUCCESS
******************************************************************************/
uint8_t SpiFlash_Write_CMD(uint8_t *CMD)
{
	uint8_t len = 3;

	spi_tx_buf[0] = *CMD;
	spi_tx_buf[1] = *(CMD+1);
	spi_tx_buf[2] = *(CMD+2);
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
	while(!spi_xfer_done);
    return RET_SUCCESS;
}
/*****************************************************************************
** 描  述:写使能
** 入  参:无
** 返回值:无
******************************************************************************/
void SpiFlash_Write_Enable(void)
{
	spi_xfer_done = false;
	SpiFlash_WriteOneByte(SPIFlash_WriteEnable_CMD);
	while(!spi_xfer_done);
}
/*****************************************************************************
** 描  述:擦除扇区,W25Q128FVSIG最小的擦除单位是扇区
** 入  参:Block_Num:块号
**         Sector_Number:扇区号
** 返回值:
******************************************************************************/
void SPIFlash_Erase_Sector(uint8_t Block_Num,uint8_t Sector_Number)
{
	SpiFlash_Write_Enable();

	spi_tx_buf[0] = SPIFlash_SecErase_CMD;
	spi_tx_buf[1] = Block_Num;
	spi_tx_buf[2] = Sector_Number<<4;
	spi_tx_buf[3] = 0x00;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, 4, spi_rx_buf, 4));
	while(!spi_xfer_done);	
	nrf_delay_ms(10);    //每次擦除数据都要延时等待写入结束
    return ;
}
/*****************************************************************************
** 描  述:向指定的地址写入数据
**         *pBuffer:指向待写入的数据
**         WriteAddr:写入的起始地址
**         WriteBytesNum:读出的字节数
** 返回值:RET_SUCCESS
******************************************************************************/
uint8_t SpiFlash_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t WriteBytesNum)
{
	uint8_t len;

	SpiFlash_Write_Enable();

	spi_tx_buf[0] = SPIFlash_PageProgram_CMD;
	spi_tx_buf[1] = (uint8_t)((WriteAddr&0x00ff0000)>>16);
	spi_tx_buf[2] = (uint8_t)((WriteAddr&0x0000ff00)>>8);
	spi_tx_buf[3] = (uint8_t)WriteAddr;

	memcpy(&spi_tx_buf[4],pBuffer,WriteBytesNum);

	len = WriteBytesNum + 4;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, 0));
	while(!spi_xfer_done);	

	return RET_SUCCESS;
}
/*****************************************************************************
** 描  述:从指定的地址读出指定长度的数据
** 入  参:pBuffer:指向存放读出数据的首地址       
**         ReadAddr:待读出数据的起始地址
**         ReadBytesNum:读出的字节数
** 返回值:
******************************************************************************/
uint8_t SpiFlash_Read(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
{
	uint8_t len;

	spi_tx_buf[0] = SPIFlash_ReadData_CMD;
	spi_tx_buf[1] = (uint8_t)((ReadAddr&0x00ff0000)>>16);
	spi_tx_buf[2] = (uint8_t)((ReadAddr&0x0000ff00)>>8);
	spi_tx_buf[3] = (uint8_t)ReadAddr;

	len = ReadBytesNum + 4;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
	while(!spi_xfer_done);	
	memcpy(pBuffer,&spi_rx_buf[4],ReadBytesNum);

	return RET_SUCCESS;
}
/********************************************END FILE*******************************************/

MicroSD卡(TF卡)SPI测试

SD卡往往有多种通讯方式(上电后需要主机告诉SD卡采用什么方式,所以需要SD卡初始化程序):

  • 标准的SPI
  • SD模式 —— 一根线的SDIO接口
  • SD模式 —— 四根线的SDIO接口,一次可以4个数据出,4个数据回

Micro SD卡SPI模式基础知识

Micro SD(TF)卡的引脚说明:
在这里插入图片描述
Micro SD卡只有8个引脚是比SD卡少了一个Vss。可以买个SD卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,可以完全当做SD卡来操作。
在《SD卡接口规范》文档中,有SD卡工作在SPI模式的引脚定义:
在这里插入图片描述SD卡和Micro SD卡的SPI操作方式是一样的。

SD卡的 SPI 时钟空闲时为高电平,在时钟的第二个边沿,也就是时钟线的电平由低变高时 采集数据,所以配置 SPI 的极性和相位: CPOL = 1, CPHA = 1。由上面第一章的内容可知 读取SD卡 nRF52832工作在模式 3

下面的图片参考博文:MicroSD卡(TF卡)SPI模式实现方法
在这里插入图片描述在这里插入图片描述在这里插入图片描述

Micro SD卡SPI 程序移植测试

研究了这么多,最好还是回到当初STM32正点原子的教程里面有SD的读写,驱动,想着直接移植过来试试,最后应该是成功了,没有做过多的测试读写,只是读了一下扇区大小。

  • 程序中主要注意 读取SD卡 nRF52832工作在模式 3;
  • 速度设置需要额外更改;

.h部分

//省略

typedef unsigned char      u8;  
typedef unsigned long int  u32;
typedef unsigned int       u16;
								    	 
// SD卡类型定义  
#define SD_TYPE_ERR     0X00
#define SD_TYPE_MMC     0X01
#define SD_TYPE_V1      0X02
#define SD_TYPE_V2      0X04
#define SD_TYPE_V2HC    0X06	   
// SD卡指令表  	   
#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
//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF

void hal_spi_init(void);
uint8_t SpiFlash_ReadOneByte(void);
void SpiFlash_WriteOneByte(uint8_t Dat);
void spi_set_highspeed(void);
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc);
u8 SD_Initialize(void);
u8 SD_GetResponse(u8 Response);
u8 SD_RecvData(u8*buf,u16 len);
u8 SD_GetCSD(u8 *csd_data);
u32 SD_GetSectorCount(void);

//省略

.c部分

//省略

#define SPI_INSTANCE  0 /**< SPI instance index. */
#define SDCARD  1
u8  SD_Type=0;//SD卡的类型 

static volatile bool spi_xfer_done;  //SPI数据传输完成标志
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */

static uint8_t    spi_tx_buf[256];   /**< TX buffer. */
static uint8_t    spi_rx_buf[256];   /**< RX buffer. */

static nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
  spi_xfer_done = true;
}

void hal_spi_init(void)
{
    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
	spi_config.frequency = NRF_DRV_SPI_FREQ_250K;
	#ifdef   SDCARD
	spi_config.mode = NRF_DRV_SPI_MODE_3;
	#endif
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}

void spi_set_highspeed(void)
{
	nrf_drv_spi_uninit(&spi);	//改之前必须uninit
	spi_config.frequency = NRF_DRV_SPI_FREQ_4M;
   	APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
}

/*****************************************************************************
** 描  述:读出一个字节
** 入  参:无
** 返回值:读出的数据
******************************************************************************/
uint8_t SpiFlash_ReadOneByte(void) 
{
    uint8_t len = 1;

	spi_tx_buf[0] = 0xFF;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
	while(!spi_xfer_done);
    return (spi_rx_buf[0]);
}

/*****************************************************************************
** 描  述:写入一个字节
** 入  参:Dat:待写入的数据
** 返回值:无
******************************************************************************/
void SpiFlash_WriteOneByte(uint8_t Dat)
{
    uint8_t len = 1;
	
    spi_tx_buf[0] = Dat;
	spi_xfer_done = false;
	APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
    while(!spi_xfer_done);
}

//向SD卡发送一个命令
//输入: u8 cmd   命令 
//      u32 arg  命令参数
//      u8 crc   crc校验值	   
//返回值:SD卡返回的响应															  
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
    u8 r1;	
	u8 Retry=0; 
	// SD_DisSelect();//取消上次片选
	// if(SD_Select())return 0XFF;//片选失效 
	//发送
    SpiFlash_WriteOneByte(cmd | 0x40);//分别写入命令
    SpiFlash_WriteOneByte(arg >> 24);
    SpiFlash_WriteOneByte(arg >> 16);
    SpiFlash_WriteOneByte(arg >> 8);
    SpiFlash_WriteOneByte(arg);	  
    SpiFlash_WriteOneByte(crc); 
	if(cmd==CMD12)SpiFlash_WriteOneByte(0xff);//Skip a stuff byte when stop reading
    //等待响应,或超时退出
	Retry=0X1F;
	do
	{
		r1=SpiFlash_ReadOneByte();
	}while((r1&0X80) && Retry--);	 
	//返回状态值
    return r1;
}		

u8 SD_Initialize(void)
{
    u8 r1;      // 存放SD卡的返回值
    u16 retry;  // 用来进行超时计数
    u8 buf[4];  
	u16 i;

	hal_spi_init();

 	for(i=0;i<10;i++)SpiFlash_WriteOneByte(0XFF);//发送最少74个脉冲
	retry=20;
	do
	{
		r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
	}while((r1!=0X01) && retry--);
 	SD_Type=0;//默认无卡
	if(r1==0X01)
	{
		if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0
		{
			for(i=0;i<4;i++)buf[i]=SpiFlash_ReadOneByte();	//Get trailing return value of R7 resp
			if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
			{
				retry=0XFFFE;
				do
				{
					SD_SendCmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
				}while(r1&&retry--);
				if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
				{
					for(i=0;i<4;i++)buf[i]=SpiFlash_ReadOneByte();//得到OCR值
					if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;    //检查CCS
					else SD_Type=SD_TYPE_V2;   
				}
			}
		}else//SD V1.x/ MMC	V3
		{
			SD_SendCmd(CMD55,0,0X01);		//发送CMD55
			r1=SD_SendCmd(CMD41,0,0X01);	//发送CMD41
			if(r1<=1)
			{		
				SD_Type=SD_TYPE_V1;
				retry=0XFFFE;
				do //等待退出IDLE模式
				{
					SD_SendCmd(CMD55,0,0X01);	//发送CMD55
					r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
				}while(r1&&retry--);
			}else
			{
				SD_Type=SD_TYPE_MMC;//MMC V3
				retry=0XFFFE;
				do //等待退出IDLE模式
				{											    
					r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
				}while(r1&&retry--);  
			}
			if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;//错误的卡
		}
	}
	// SD_DisSelect();//取消片选
	spi_set_highspeed();//高速
	if(SD_Type)return 0;
	else if(r1)return r1; 	   
	return 0xaa;//其他错误
}

//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{
	u16 Count=0xFFFF;//等待次数	   						  
	while ((SpiFlash_ReadOneByte()!=Response)&&Count)Count--;//等待得到准确的回应  	  
	if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败   
	else return MSD_RESPONSE_NO_ERROR;//正确回应
}

//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;	
u8 SD_RecvData(u8*buf,u16 len)
{			  	  
	if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
        *buf=SpiFlash_ReadOneByte();
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SpiFlash_WriteOneByte(0xFF);
    SpiFlash_WriteOneByte(0xFF);									  					    
    return 0;//读取成功
}

//获取SD卡的CSD信息,包括容量和速度信息
//输入:u8 *cid_data(存放CID的内存,至少16Byte)	    
//返回值:0:NO_ERR
//		 1:错误														   
u8 SD_GetCSD(u8 *csd_data)
{
    u8 r1;	 
    r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
    if(r1==0)
	{
    	r1=SD_RecvData(csd_data, 16);//接收16个字节的数据 
    }
	// SD_DisSelect();//取消片选
	if(r1)return 1;
	else return 0;
}  

//获取SD卡的总扇区数(扇区数)   
//返回值:0: 取容量出错 
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.														  
u32 SD_GetSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u8 n;
	u16 csize;  					    
	//取CSD信息,如果期间出错,返回0
    if(SD_GetCSD(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的卡
    {	
		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;
}
/********************************************END FILE*******************************************/

最后测试是在主函数调用SD_GetSectorCount函数计算扇区大小:
在这里插入图片描述在这里插入图片描述
换了一个512MB的卡:
在这里插入图片描述

最后的疑问

在这里插入图片描述
最后还是有一个问题的,我开始读取的是 8G的卡,扇区读出来正常,但是变成 MB 就是3290MB 了,看到网上有个视频教学也是使用的这个驱动,读取8G的卡居然和我这个数据一样 3290 MB。4G和512MB的卡读出来计算出来都正常,感觉是数据类型 低级问题导致的,但是一下子还真不知道,还希望有人能够指点……

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章           查看所有文章
加:2021-10-13 11:36:15  更:2021-10-13 11:37:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:47:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码