查看:
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++; } |
|