赞
踩
目录
SD卡有SD驱动模式和SPI驱动模式,本例中使用SPI模式驱动SD卡。
https://download.csdn.net/download/qq_30095023/88014550
芯片类型:STM32F103VET6。
flash大小为512KB,RAM大小 64KB。
本例使用TFT屏幕上的SD卡插口,测试所用SD卡容量大小为4GB。
SD_CS、SPI_MOSI、SPI_MISO、SPI_CLK 与单片机连接。note:请确保屏幕电源连接正常。
如上图,红色框框所标记区域为TFT屏幕SD卡的SPI接口,将此接口与STM32单片机的SPI所对应的IO连接。
note: u8 、u16、u32等为自定义数据类型,编译报错请按需修改。LOG_XX为日志打印,请按需替换。
定义一些操作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
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;
- }
- 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容量。
可以看到,成功查到SD数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。