OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3908|回复: 1

F030F4的SD卡初始化,读写代码.

[复制链接]

30

主题

104

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
456475
金钱
456475
注册时间
2014-8-23
在线时间
59 小时
发表于 2015-4-9 16:27:28 | 显示全部楼层 |阅读模式
假定工作在48MHz.工作正常.

[mw_shl_code=c,true]#include "stm32f0xx.h" #include "SD.h" uint8_t SD_Type = 0; //SD卡的类型 uint8_t SDSPI_ReadWriteByte(uint8_t Data) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_SendData8(SPI1, Data); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_ReceiveData8(SPI1); } void SD_DisSelect(void) { GPIO_SetBits(GPIOA, GPIO_Pin_4); SDSPI_ReadWriteByte(0xFF); } void SD_SPI_Init(void) { SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE); SPI_I2S_DeInit(SPI1); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; /* 48MHz / 8 = 6MHz */ SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF); SPI_Cmd(SPI1, ENABLE); } //SPI硬件层初始化 void SD_SPI_Pre_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; /* 48MHz / 8 = 6MHz */ SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF); SPI_Cmd(SPI1, ENABLE); GPIO_SetBits(GPIOA, GPIO_Pin_4); } //选择sd卡,并且等待卡准备OK //返回值:0,成功;1,失败; uint8_t SD_Select(void) { GPIO_ResetBits(GPIOA, GPIO_Pin_4); if(SD_WaitReady() == 0)return 0; //等待成功 SD_DisSelect(); return 1;//等待失败 } //等待卡准备好 //返回值:0,准备好了;其他,错误代码 uint8_t SD_WaitReady(void) { uint32_t t = 0; { if(SDSPI_ReadWriteByte(0xFF) == 0xFF)return 0; //OK t++; } while(t < 0xFFFFFF); //等待 return 1; } //等待SD卡回应 //Response:要得到的回应值 //返回值:0,成功得到了该回应值 // 其他,得到回应值失败 uint8_t SD_GetResponse(uint8_t Response) { uint16_t Count = 0xFFFF; //等待次数 while ((SDSPI_ReadWriteByte(0xFF) != Response) && Count)Count--; //等待得到准确的回应 if (Count == 0)return MSD_RESPONSE_FAILURE; //得到回应失败 else return MSD_RESPONSE_NO_ERROR;//正确回应 } //从sd卡读取一个数据包的内容 //buf:数据缓存区 //len:要读取的数据长度. //返回值:0,成功;其他,失败; uint8_t SD_RecvData(uint8_t *buf, uint16_t len) { if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE while(len--)//开始接收数据 { *buf = SDSPI_ReadWriteByte(0xFF); buf++; } //下面是2个伪CRC(dummy CRC) SDSPI_ReadWriteByte(0xFF); SDSPI_ReadWriteByte(0xFF); return 0;//读取成功 } //向sd卡写入一个数据包的内容 512字节 //buf:数据缓存区 //cmd:指令 //返回值:0,成功;其他,失败; uint8_t SD_SendBlock(uint8_t *buf, uint8_t cmd) { uint16_t t; if(SD_WaitReady())return 1;//等待准备失效 SDSPI_ReadWriteByte(cmd); if(cmd != 0xFD) //不是结束指令 { for(t = 0; t < 512; t++)SDSPI_ReadWriteByte(buf[t]); //提高速度,减少函数传参时间 SDSPI_ReadWriteByte(0xFF);//忽略crc SDSPI_ReadWriteByte(0xFF); t = SDSPI_ReadWriteByte(0xFF); //接收响应 if((t & 0x1F) != 0x05)return 2; //响应错误 } return 0;//写入成功 } //向SD卡发送一个命令 //输入: uint8_t cmd 命令 // uint32_t arg 命令参数 // uint8_t crc crc校验值 //返回值:SD卡返回的响应 uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc) { uint8_t r1; uint8_t Retry = 0; SD_DisSelect();//取消上次片选 if(SD_Select())return 0xFF;//片选失效 //发送 SDSPI_ReadWriteByte(cmd | 0x40);//分别写入命令 SDSPI_ReadWriteByte(arg >> 24); SDSPI_ReadWriteByte(arg >> 16); SDSPI_ReadWriteByte(arg >> 8); SDSPI_ReadWriteByte(arg); SDSPI_ReadWriteByte(crc); if(cmd == CMD12)SDSPI_ReadWriteByte(0xff); //Skip a stuff byte when stop reading //等待响应,或超时退出 Retry = 0x1F; do { r1 = SDSPI_ReadWriteByte(0xFF); } while((r1 & 0x80) && Retry--); //返回状态值 return r1; } //获取SD卡的CID信息,包括制造商信息 //输入: uint8_t *cid_data(存放CID的内存,至少16Byte) //返回值:0:NO_ERR // 1:错误 uint8_t SD_GetCID(uint8_t *cid_data) { uint8_t r1; //发CMD10命令,读CID r1 = SD_SendCmd(CMD10, 0, 0x01); if(r1 == 0x00) { r1 = SD_RecvData(cid_data, 16); //接收16个字节的数据 } SD_DisSelect();//取消片选 if(r1)return 1; else return 0; } //获取SD卡的CSD信息,包括容量和速度信息 //输入:uint8_t *cid_data(存放CID的内存,至少16Byte) //返回值:0:NO_ERR // 1:错误 uint8_t SD_GetCSD(uint8_t *csd_data) { uint8_t 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,则初始化不能通过. uint32_t SD_GetSectorCount(void) { uint8_t csd[16]; uint32_t Capacity; uint8_t n; uint16_t csize; //取CSD信息,如果期间出错,返回0 if(SD_GetCSD(csd) != 0) return 0; //如果为SDHC卡,按照下面方式计算 if((csd[0] & 0xC0) == 0x40) //V2.00的卡 { csize = csd[9] + ((uint16_t)csd[8] << 8) + 1; Capacity = (uint32_t)csize << 10;//得到扇区数 } else //V1.XX的卡 { n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1; Capacity = (uint32_t)csize << (n - 9); //得到扇区数 } return Capacity; } //初始化SD卡 uint8_t SD_Initialize(void) { uint8_t r1; // 存放SD卡的返回值 uint16_t retry; // 用来进行超时计数 uint8_t buf[4]; uint16_t i; SD_SPI_Pre_Init(); //初始化IO for(i = 0; i < 10; i++)SDSPI_ReadWriteByte(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 = SDSPI_ReadWriteByte(0xFF); //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 = SDSPI_ReadWriteByte(0xFF); //得到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();//取消片选 SD_SPI_Init(); if(SD_Type)return 0; else if(r1)return r1; return 0xAA;//其他错误 } //读SD卡 //buf:数据缓存区 //sector:扇区 //cnt:扇区数 //返回值:0,ok;其他,失败. uint8_t SD_ReadDisk(uint8_t *buf, uint32_t sector, uint8_t cnt) { uint8_t r1; if(SD_Type != SD_TYPE_V2HC)sector <<= 9; //转换为字节地址 if(cnt == 1) { r1 = SD_SendCmd(CMD17, sector, 0x01); //读命令 if(r1 == 0) //指令发送成功 { r1 = SD_RecvData(buf, 512); //接收512个字节 } } else { r1 = SD_SendCmd(CMD18, sector, 0x01); //连续读命令 do { r1 = SD_RecvData(buf, 512); //接收512个字节 buf += 512; } while(--cnt && r1 == 0); SD_SendCmd(CMD12, 0, 0x01); //发送停止命令 } SD_DisSelect();//取消片选 return r1;// } //写SD卡 //buf:数据缓存区 //sector:起始扇区 //cnt:扇区数 //返回值:0,ok;其他,失败. uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt) { uint8_t r1; if(SD_Type != SD_TYPE_V2HC)sector *= 512; //转换为字节地址 if(cnt == 1) { r1 = SD_SendCmd(CMD24, sector, 0x01); //读命令 if(r1 == 0) //指令发送成功 { r1 = SD_SendBlock(buf, 0xFE); //写512个字节 } } else { if(SD_Type != SD_TYPE_MMC) { SD_SendCmd(CMD55, 0, 0x01); SD_SendCmd(CMD23, cnt, 0x01); //发送指令 } r1 = SD_SendCmd(CMD25, sector, 0x01); //连续读命令 if(r1 == 0) { do { r1 = SD_SendBlock(buf, 0xFC); //接收512个字节 buf += 512; } while(--cnt && r1 == 0); r1 = SD_SendBlock(0, 0xFD); //接收512个字节 } } SD_DisSelect();//取消片选 return r1;// } [/mw_shl_code]

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2015-4-9 23:02:39 | 显示全部楼层
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-24 03:18

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表