1.硬件引脚介绍:
本测试采用SPI模式读写SD卡,相关引脚配置如下:
片选:SD_CS->PB13,对应SD卡的1脚,低电平有效
时钟:SPI1_SCK->PA5,对应SD卡的5脚
主入从出:MISO->PA6,对应SD卡的7脚
主出从入:MOSI->PA7,对应SD卡的2脚
2.初始化步骤:
while(SD_Initialize())
{
//提示检查SD卡
}
//初始化SD卡
u8 SD_Initialize(void)
{
u8 r1; // 存放SD卡的返回值
u16 retry; // 用来进行超时计数
u8 buf[4];
u16 i;
SD_SPI_Init(); //初始化IO
SD_SPI_SpeedLow(); //设置到低速模式
for(i=0;i<10;i++)SD_SPI_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[i]=SD_SPI_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[i]=SD_SPI_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//MMC卡不支持CMD55+CMD41识别
{
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_SpeedHigh();//高速
if(SD_Type)return 0;
else if(r1)return r1;
return 0xaa;//其他错误
}
如果返回值为正常类型,则跳出初始化循环,接下来介绍该函数中的SD_SPI_Init()函数及其内部调用的SPI_Init()函数
SPI_Init()函数初始化单片机的SPI1外设,具体配置为PA5.6.7三个引脚的设置
//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card
SPI_InitTypeDef SPI_InitStructure;
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //选择了串行时钟的稳态:时钟悬空高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI外设
SPI1_ReadWriteByte(0xff);//启动传输
}
3.主函数部分:
int main(void)
{
u32 total,free;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
delay_init(); //延时函数初始
LED_Init();
uart_init(19200); //串口初始化为19200
exfuns_init(); //为fatfs相关变量申请内存
mem_init(); //初始化内存池
printf ("程序初始化结束:->\r\n");
if(SD_Initialize()) //检测SD卡
{
printf ("未检测到SD卡:->\r\n");
while(SD_Initialize())
{
printf("别看着了,滚去找原因,根本没插卡:\r\n");
LED1=!LED1;
delay_ms(500);
}
LED1=1;
}
printf("SD卡正常加载:\r\n");
printf("申请内存结果:%d\r\n",(unsigned int)exfuns_init());//为fatfs相关变量申请内存,返回0成功
f_mount(fs[0],"0:",1); //挂载SD卡
exf_getfree((u8*)"0",&total,&free);//得到SD卡的总容量和剩余容量
printf("SD卡的容量是:%d\r\n",free);
printf("开始扫描串口数据**************************\r\n");
while(1) //测试使用串口输入函数名是成功的
{
if(USART_RX_STA&0x8000)//不断检测串口接收完成?这里可以去串口中断服务函数中做一些if判断来限制串口的数据格式,数据大小
{
WriteData(); //串口接收完成开始写入SD卡,该函数是自己封装的,详细请看下面介绍的SD卡写入步骤
USART_RX_STA=0; //清零标志位
}
LED0=!LED0;//程序运行指示灯
delay_ms(500);
}
}
由于里面的函数都是SD驱动函数封装起来的,所以要了解底层写入过程,还要参考SD卡的通讯时序等等,不在此赘述,以后会另做记录
void WriteData(void) //一旦在串口中断标志置位,则调用该函数,开始启动创建文件的步骤:
{
//*********写入SD卡****************************
mf_mount(0,1); //注册工作区域
mf_open(fileName,4); //创建文件
mf_close();
mf_open(fileName,2); //以可写方式打开文件
mf_lseek(mf_size()); //将指针移动到文件末尾,以便追加数据
mf_write(USART_RX_BUF,142); //追加写入串口发来的数据到SD卡
mf_close(); //关闭文件
printf("WriteData()执行完毕,数据写入成功\r\n");
}