查看: 344  |  回复: 0
基于STM32采用CS创世 SD NAND完成FATFS文件系统移植与测试(中篇)

主题

回复
发表于2023-02-23 11:54:11 | 只看该作者
1# 电梯直达

3.2 SPI硬件时序方式


上面的3.1小节是采用SPI模拟时序驱动SD NAND,STM32本身集成有SPI硬件模块,可以直接利用STM32硬件SPI接口读写。


下面贴出底层的适配代码。 上面贴出的驱动代码里,已经将驱动接口部分和协议逻辑部分区分开了,替换底层的SIP读写代码非常方便。

(1)主要替换的代码/*

函数功能:SPI初始化(模拟SPI)

硬件连接:

MISO--->PB14

MOSI--->PB15

SCLK--->PB13

*/

void SPI_Init(void)

{

/*开启时钟*/

RCC->APB1ENR|=1<<14; //开启SPI2时钟

RCC->APB2ENR|=1<<3; //PB

GPIOB->CRH&=0X000FFFFF; //清除寄存器

GPIOB->CRH|=0XB8B00000;

GPIOB->ODR|=0X7<<13; //PB13/14/15上拉--输出高电平

/*SPI2基本配置*/

SPI2->CR1=0X0; //清空寄存器

SPI2->CR1|=0<<15; //选择“双线双向”模式

SPI2->CR1|=0<<11; //使用8位数据帧格式进行发送/接收;

SPI2->CR1|=0<<10; //全双工(发送和接收);

SPI2->CR1|=1<<9; //启用软件从设备管理

SPI2->CR1|=1<<8; //NSS

SPI2->CR1|=0<<7; //帧格式,先发送高位

SPI2->CR1|=0x0<<3;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。

SPI2->CR1|=1<<2; //配置为主设备

SPI2->CR1|=1<<1; //空闲状态时, SCK保持高电平。

SPI2->CR1|=1<<0; //数据采样从第二个时钟边沿开始。

SPI2->CR1|=1<<6; //开启SPI设备。

}


/*

函数功能:SPI读写一个字节

*/

u8 SPI_ReadWriteOneByte(u8 data_tx)

{

u16 cnt=0;

while((SPI2->SR&1<<1)==0) //等待发送区空--等待发送缓冲为空

{

cnt++;

if(cnt>=65530)return 0; //超时退出 u16=2个字节

}

SPI2->DR=data_tx; //发送一个byte

cnt=0;

while((SPI2->SR&1<<0)==0) //等待接收完一个byte

{

cnt++;

if(cnt>=65530)return 0; //超时退出

}

return SPI2->DR; //返回收到的数据

}


函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节

函数参数:data是要写入的数据

返 回 值:读到的数据

*/

u8 SDCardReadWriteOneByte(u8 DataTx)

{

return SPI_ReadWriteOneByte(DataTx);

}

(2)运行效果3.3 SDIO方式

如果想提高SD NAND的读写速度,可以采用SDIO协议,STM32本身有SDIO的硬件支持,配置好SDIO的寄存器即可完成SD NAND的操作。 SDIO的数据线都比SPI多,读写速度自然没法比的。


下面贴出STM32F103ZE上面编写的SDIO协议读写SD NAND的驱动代码。


(1)整体工程代码(2)sdio.c#include "sdio_sdcard.h"

#include "string.h"

#include "sys.h"

#include "usart.h"


static u8 CardType=SDIO_STD_CAPACITY_SD_CARD_V1_1; //SD卡类型(默认为1.x卡)

static u32 CSD_Tab[4],CID_Tab[4],RCA=0; //SD卡CSD,CID以及相对地址(RCA)数据

static u8 DeviceMode=SD_DMA_MODE; //工作模式,注意,工作模式必须通过SDIO_SdCardSetDeviceMode,后才算数.这里只是定义一个默认的模式(SD_DMA_MODE)

static u8 StopCondition=0; //是否发送停止传输标志位,DMA多块读写的时候用到

volatile SDIO_SD_ERROR_INFO TransferError=SD_OK; //数据传输错误标志,DMA读写时使用

volatile u8 TransferEnd=0; //传输结束标志,DMA读写时使用

SD_CardInfo SDCardInfo; //SD卡信息


//SDIO_SdCardReadDiskSector/SDIO_SdCardWriteDiskSector函数专用buf,当这两个函数的数据缓存区地址不是4字节对齐的时候,

//需要用到该数组,确保数据缓存区地址是4字节对齐的.

__align(4) u8 SDIO_DATA_BUFFER[512];

/*

SD卡与开发板的SDIO方式接线关系如下:

DATA0---PC8

DATA1---PC9

DATA2---PC10

DATA3---PC11

CLK-----PC1

CMD-----PD2

*/


/*

函数功能:SDIO方式初始化SD卡

返回值 :错误代码;(0,无错误)

*/

SDIO_SD_ERROR_INFO SDIO_SdCardInit(void)

{

u8 clkdiv=0;

SDIO_SD_ERROR_INFO errorstatus=SD_OK;

//SDIO IO口初始化

RCC->APB2ENR|=1<<4; //使能PORTC时钟

RCC->APB2ENR|=1<<5; //使能PORTD时钟

RCC->AHBENR|=1<<10; //使能SDIO时钟

RCC->AHBENR|=1<<1; //使能DMA2时钟


GPIOC->CRH&=0XFFF00000;

GPIOC->CRH|=0X000BBBBB; //PC.8~12 复用输出


GPIOD->CRL&=0XFFFFF0FF;

GPIOD->CRL|=0X00000B00; //PD2复用输出,PD7 上拉输入


//SDIO外设寄存器设置为默认值

SDIO->POWER=0x00000000;

SDIO->CLKCR=0x00000000;

SDIO->ARG=0x00000000;

SDIO->CMD=0x00000000;

SDIO->DTIMER=0x00000000;

SDIO->DLEN=0x00000000;

SDIO->DCTRL=0x00000000;

SDIO->ICR=0x00C007FF;

SDIO->MASK=0x00000000;

STM32_NVIC_SetPriority(SDIO_IRQn,0,0); //SDIO中断配置

errorstatus=SDIO_SdPowerON(); //SD卡上电

SDIO_SdCardInitializeCards(); //初始化SD卡

SDIO_SdCardGetInfo(&SDCardInfo); //获取卡信息

SDIO_SdCardSelectAddr((u32)(SDCardInfo.RCA<<16));//选中SD卡

SDIO_SdCardEnableWideBusOperation(1); //4位宽度,如果是MMC卡,则不能用4位模式

if((errorstatus==SD_OK)||(SDIO_MULTIMEDIA_CARD==CardType))

{

if(SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0)

{

clkdiv=SDIO_TRANSFER_CLK_DIV+6; //V1.1/V2.0卡,设置最高72/12=6Mhz

}else clkdiv=SDIO_TRANSFER_CLK_DIV; //SDHC等其他卡,设置最高72/6=12Mhz

SDIO_ClockSet(clkdiv); //设置时钟频率,SDIO时钟计算公式:SDIO_CK时钟=SDIOCLK/[clkdiv+2];其中,SDIOCLK固定为48Mhz

errorstatus=SDIO_SdCardSetDeviceMode(SD_POLLING_MODE); //设置为查询模式

}

return errorstatus;

}


/*

函数功能: SDIO时钟初始化设置

函数参数:

clkdiv:时钟分频系数

CK时钟=SDIOCLK/[clkdiv+2];(SDIOCLK时钟固定为48Mhz)

*/

void SDIO_ClockSet(u8 clkdiv)

{

u32 tmpreg=SDIO->CLKCR;

tmpreg&=0XFFFFFF00;

tmpreg|=clkdiv;

SDIO->CLKCR=tmpreg;

}


/*

函数功能: SDIO发送命令函数

函数参数:

cmdindex:命令索引,低六位有效

waitrsp:期待的相应.00/10,无响应;01,短响应;11,长响应

arg:参数

*/

void SDIO_SendCmd(u8 cmdindex,u8 waitrsp,u32 arg)

{

u32 tmpreg;

SDIO->ARG=arg;

tmpreg=SDIO->CMD;

tmpreg&=0XFFFFF800; //清除index和waitrsp

tmpreg|=cmdindex&0X3F; //设置新的index

tmpreg|=waitrsp<<6; //设置新的wait rsp

tmpreg|=0<<8; //无等待

tmpreg|=1<<10; //命令通道状态机使能

SDIO->CMD=tmpreg;

}


/*

函数功能: SDIO发送数据配置函数

函数参数:

datatimeout:超时时间设置

datalen:传输数据长度,低25位有效,必须为块大小的整数倍

blksize:块大小.实际大小为:2^blksize字节

dir:数据传输方向:0,控制器到卡;1,卡到控制器;

*/

void SDIO_SendDataConfig(u32 datatimeout,u32 datalen,u8 blksize,u8 dir)

{

u32 tmpreg;

SDIO->DTIMER=datatimeout;

SDIO->DLEN=datalen&0X1FFFFFF; //低25位有效

tmpreg=SDIO->DCTRL;

tmpreg&=0xFFFFFF08; //清除之前的设置.

tmpreg|=blksize<<4; //设置块大小

tmpreg|=0<<2; //块数据传输

tmpreg|=(dir&0X01)<<1; //方向控制

tmpreg|=1<<0; //数据传输使能,DPSM状态机

SDIO->DCTRL=tmpreg;

}


/*

函数功能:卡上电

查询所有SDIO接口上的卡设备,并查询其电压和配置时钟

返回值:错误代码;(0,无错误)

*/

SDIO_SD_ERROR_INFO SDIO_SdPowerON(void)

{

u8 i=0;

SDIO_SD_ERROR_INFO errorstatus=SD_OK;

u32 response=0,count=0,validvoltage=0;

u32 SDType=SD_STD_CAPACITY;

//配置CLKCR寄存器

SDIO->CLKCR=0; //清空CLKCR之前的设置

SDIO->CLKCR|=0<<9; //非省电模式

SDIO->CLKCR|=0<<10; //关闭旁路,CK根据分频设置输出

SDIO->CLKCR|=0<<11; //1位数据宽度

SDIO->CLKCR|=0<<13; //SDIOCLK上升沿产生SDIOCK

SDIO->CLKCR|=0<<14; //关闭硬件流控制

SDIO_ClockSet(SDIO_INIT_CLK_DIV);//设置时钟频率(初始化的时候,不能超过400Khz)

SDIO->POWER=0X03; //上电状态,开启卡时钟

SDIO->CLKCR|=1<<8; //SDIOCK使能

for(i=0;i<74;i++)

{

SDIO_SendCmd(SD_CMD_GO_IDLE_STATE,0,0);//发送CMD0进入IDLE STAGE模式命令.

errorstatus=SDIO_CmdErrorCheck();

if(errorstatus==SD_OK)break;

}

if(errorstatus)return errorstatus;//返回错误状态

SDIO_SendCmd(SDIO_SEND_IF_COND,1,SD_CHECK_PATTERN);//发送CMD8,短响应,检查SD卡接口特性.

//arg[11:8]:01,支持电压范围,2.7~3.6V

//arg[7:0]:默认0XAA

//返回响应7

errorstatus=SDIO_CmdResp7Error(); //等待R7响应

if(errorstatus==SD_OK) //R7响应正常

{

CardType=SDIO_STD_CAPACITY_SD_CARD_V2_0; //SD 2.0卡

SDType=SD_HIGH_CAPACITY; //高容量卡

}

SDIO_SendCmd(SD_CMD_APP_CMD,1,0); //发送CMD55,短响应

errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应

if(errorstatus==SD_OK)//SD2.0/SD 1.1

{

//SD卡,发送ACMD41 SD_APP_OP_COND,参数为:0x80100000

while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL))

{

SDIO_SendCmd(SD_CMD_APP_CMD,1,0); //发送CMD55,短响应

errorstatus=SDIO_CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应

if(errorstatus!=SD_OK)return errorstatus; //响应错误

SDIO_SendCmd(SD_CMD_SD_APP_OP_COND,1,SD_VOLTAGE_WINDOW_SD|SDType);//发送ACMD41,短响应

errorstatus=SDIO_CmdResp3Error(); //等待R3响应

if(errorstatus!=SD_OK)return errorstatus; //响应错误

response=SDIO->RESP1;; //得到响应

validvoltage=(((response>>31)==1)?1:0); //判断SD卡上电是否完成

count++;

}


主题

回复
  • 温馨提示: 标题不合格、重复发帖、发布广告贴,将会被删除帖子或禁止发言。 详情请参考: 社区发帖规则
  • 您当前输入了 0 个文字。还可以输入 8000 个文字。 已添加复制上传图片功能,该功能目前仅支持chrome和火狐

禁言/删除

X
请选择禁言时长:
是否清除头像:
禁言/删除备注:
昵 称:
 
温馨提示:昵称只能设置一次,设置后无法修改。
只支持中文、英文和数字。

举报

X
请选择举报类型:
请输入详细内容:

顶部