本次主要实现SPI接口的外接TF卡实现FATFS文件系统。
首先是SPI接口初始化。使用SPI1外设,对应引脚如下:
SPI初始化如下:
void drv_spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure={0};
SPI_InitTypeDef SPI_InitStructure={0};
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );
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_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init( SPI1, &SPI_InitStructure );
SPI_Cmd( SPI1, ENABLE );
}
SPI发送接收数据
uint8_t spi_read_write(uint8_t _data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) ;
SPI_I2S_SendData(SPI1, _data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) ;
return SPI_I2S_ReceiveData(SPI1);
}
接下来是实现TF的初始化和读写数据接口。主要参考以前移植的代码,实现很容易。
int MSD_Init(void)
{
uint8_t r1;
uint8_t buff[6] = {0};
uint16_t retry;
MSD_SPI_Configuration();
for(retry=0; retry<0x100; retry++)
{
_card_power_on();
}
for(retry=0; retry<10; retry++)
{
spi_read_write(DUMMY_BYTE);
}
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD0, 0, 0x95);
if(r1 == 0x01)
{
retry = 0;
break;
}
}
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Reset card into IDLE state failed!\r\n");
#endif
return 1;
}
r1 = _send_command_hold(CMD8, 0x1AA, 0x87);
if(r1 == 0x05)
{
CardInfo.CardType = CARDTYPE_SDV1;
_card_disable();
spi_read_write(DUMMY_BYTE);
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD55, 0, 0);
if(r1 != 0x01)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
#endif
return r1;
}
r1 = _send_command(ACMD41, 0, 0);
if(r1 == 0x00)
{
retry = 0;
break;
}
}
if(retry == 0xFFF)
{
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD1, 0, 0);
if(r1 == 0x00)
{
retry = 0;
break;
}
}
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD1 should return 0x00, response=0x%02x\r\n", r1);
#endif
return 2;
}
CardInfo.CardType = CARDTYPE_MMC;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 111 : MMC\r\n");
#endif
}
#ifdef PRINT_INFO
else
{
DEBUG_OUT("Card Type 111 : SD V1\r\n");
}
#endif
_card_speed_high();
r1 = _send_command(CMD59, 0, 0x01);
if(r1 != 0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD59 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1;
}
r1 = _send_command(CMD16, MSD_BLOCKSIZE, 0xFF);
if(r1 != 0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD16 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1;
}
}
else if(r1 == 0x01)
{
buff[0] = spi_read_write(DUMMY_BYTE);
buff[1] = spi_read_write(DUMMY_BYTE);
buff[2] = spi_read_write(DUMMY_BYTE);
buff[3] = spi_read_write(DUMMY_BYTE);
_card_disable();
spi_read_write(DUMMY_BYTE);
if(buff[2]==0x01 && buff[3]==0xAA)
{
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD55, 0, 0);
if(r1!=0x01)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
#endif
return r1;
}
r1 = _send_command(ACMD41, 0x40000000, 0);
if(r1 == 0x00)
{
retry = 0;
break;
}
}
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send ACMD41 should return 0x00, response=0x%02x\r\n", r1);
#endif
return 3;
}
r1 = _send_command_hold(CMD58, 0, 0);
if(r1!=0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD58 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1;
}
buff[0] = spi_read_write(DUMMY_BYTE);
buff[1] = spi_read_write(DUMMY_BYTE);
buff[2] = spi_read_write(DUMMY_BYTE);
buff[3] = spi_read_write(DUMMY_BYTE);
_card_disable();
spi_read_write(DUMMY_BYTE);
if(buff[0] & 0x40)
{
CardInfo.CardType = CARDTYPE_SDV2HC;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 222 : SD V2HC\r\n");
#endif
}
else
{
CardInfo.CardType = CARDTYPE_SDV2;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 222 : SD V2\r\n");
#endif
}
_card_speed_high();
}
}
return 0;
}
int _read_buffer(uint8_t *buff, uint16_t len, uint8_t release)
{
uint8_t r1;
uint16_t retry;
_card_enable();
for(retry=0; retry<2000; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 == 0xFE)
{
retry = 0;
break;
}
}
if(retry == 2000)
{
#ifdef PRINT_INFO
DEBUG_OUT("_read_buffer Timeout return.\n");
#endif
_card_disable();
return 1;
}
for(retry=0; retry<len; retry++)
{
*(buff+retry) = spi_read_write(DUMMY_BYTE);
}
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
if(release)
{
_card_disable();
spi_read_write(DUMMY_BYTE);
}
return 0;
}
int MSD_ReadSingleBlock(uint32_t sector, uint8_t *buffer)
{
uint8_t r1;
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
r1 = _send_command_hold(CMD17, sector, 0);
if(r1 != 0x00)
{
return 1;
}
r1 = _read_buffer(buffer, MSD_BLOCKSIZE, RELEASE);
_send_command(CMD12, 0, 0);
return r1;
}
int MSD_ReadMultiBlock(uint32_t sector, uint8_t *buffer, uint32_t NbrOfSector)
{
uint8_t r1;
uint32_t i;
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
r1 = _send_command_hold(CMD18, sector, 0);
if(r1 != 0x00)
{
return 1;
}
for(i=0; i<NbrOfSector; i++)
{
if(_read_buffer(buffer+i*MSD_BLOCKSIZE, MSD_BLOCKSIZE, HOLD))
{
_send_command(CMD12, 0, 0);
_card_disable();
return 2;
}
}
_send_command(CMD12, 0, 0);
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
int MSD_WriteSingleBlock(uint32_t sector, uint8_t *buffer)
{
uint8_t r1;
uint16_t i;
uint32_t retry;
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
r1 = _send_command(CMD24, sector, 0);
if(r1 != 0x00)
{
return 1;
}
_card_enable();
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
spi_read_write(0xFE);
for(i=0; i<MSD_BLOCKSIZE; i++)
{
spi_read_write(*buffer++);
}
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
r1 = spi_read_write(DUMMY_BYTE);
if((r1&0x1F) != 0x05)
{
_card_disable();
return 2;
}
retry = 0;
while(spi_read_write(DUMMY_BYTE) == 0x00)
{
if(retry++ == 0x40000)
{
_card_disable();
return 3;
}
}
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
int MSD_WriteMultiBlock(uint32_t sector, uint8_t *buffer, uint32_t NbrOfSector)
{
uint8_t r1;
uint16_t i;
uint32_t n;
uint32_t retry;
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
if(CardInfo.CardType != CARDTYPE_MMC)
{
_send_command(ACMD23, NbrOfSector, 0x00);
}
r1 = _send_command(CMD25, sector, 0);
if(r1 != 0x00)
{
return 1;
}
_card_enable();
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
for(n=0; n<NbrOfSector; n++)
{
spi_read_write(0xFC);
for(i=0; i<MSD_BLOCKSIZE; i++)
{
spi_read_write(*buffer++);
}
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
r1 = spi_read_write(DUMMY_BYTE);
if((r1&0x1F) != 0x05)
{
_card_disable();
return 2;
}
retry = 0;
while(spi_read_write(DUMMY_BYTE) != 0xFF)
{
if(retry++ == 0x40000)
{
_card_disable();
return 3;
}
}
}
r1 = spi_read_write(0xFD);
if(r1 == 0x00)
{
return 4;
}
retry = 0;
while(spi_read_write(DUMMY_BYTE) != 0xFF)
{
if(retry++ == 0x40000)
{
_card_disable();
return 5;
}
}
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
int _send_command(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t retry;
spi_read_write(DUMMY_BYTE);
_card_enable();
spi_read_write(cmd | 0x40);
spi_read_write(arg >> 24);
spi_read_write(arg >> 16);
spi_read_write(arg >> 8);
spi_read_write(arg);
spi_read_write(crc);
for(retry=0; retry<200; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 != 0xFF)
{
break;
}
}
_card_disable();
spi_read_write(DUMMY_BYTE);
return r1;
}
int _send_command_hold(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t retry;
spi_read_write(DUMMY_BYTE);
_card_enable();
spi_read_write(cmd | 0x40);
spi_read_write(arg >> 24);
spi_read_write(arg >> 16);
spi_read_write(arg >> 8);
spi_read_write(arg);
spi_read_write(crc);
for(retry=0; retry<200; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 != 0xFF)
{
break;
}
}
return r1;
}
下面就是FATFS文件系统的移植了。实现一下移植接口:
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize,
SD_status,
SD_read,
SD_write,
SD_ioctl,
};
DSTATUS SD_initialize(BYTE lun)
{
if(MSD_Init() == 0)
{
MSD_GetCardInfo(&CardInfo);
return 0;
}else return STA_NOINIT;
}
DSTATUS SD_status(BYTE lun)
{
return 0;
}
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
int res;
if( !count )
{
return RES_PARERR;
}
if(count==1)
{
res = MSD_ReadSingleBlock(sector ,buff );
}else
{
res = MSD_ReadMultiBlock(sector , buff ,count);
}
if(res == 0)
{
return RES_OK;
}else
{
return RES_ERROR;
}
}
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
int res;
if( !count )
{
return RES_PARERR;
}
if(count==1)
{
res = MSD_WriteSingleBlock(sector , (uint8_t *)(&buff[0]) );
}else
{
res = MSD_WriteMultiBlock(sector , (uint8_t *)(&buff[0]) , count );
}
if(res == 0)
{
return RES_OK;
}else
{
return RES_ERROR;
}
}
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
switch (cmd)
{
case CTRL_SYNC :
res = RES_OK;
break;
case GET_SECTOR_COUNT :
*(DWORD*)buff = CardInfo.Capacity/CardInfo.BlockSize;
res = RES_OK;
break;
case GET_SECTOR_SIZE :
*(WORD*)buff = CardInfo.BlockSize;
res = RES_OK;
break;
case GET_BLOCK_SIZE :
*(WORD*)buff = CardInfo.BlockSize;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
return res;
}
下面是FATFS初始化:
int FATFS_Init(FATFS *fs,char * _path)
{
FRESULT res;
FATFS_LinkDriver(&SD_Driver,_path, 0);
res = f_mount(fs,_path,1);
printf("f_mount return =%d\r\n",res);
return res;
}
FATFS文件系统配置在ffconf.h文件内,按照自己需求配置,需要修改的部分挺少的。
下面就是main主函数测试移植效果了,主要是测试扫描TF卡内所有文件并在串口打印出来。
FATFS fs;
char path[256]="0:/";
uint8_t textFileBuffer[] = "Thank you for using Development Board ^_^ \r\n";
FRESULT scan_files (char* path)
{
FRESULT res;
FILINFO fno;
DIR dir;
int i;
char *fn;
res = f_opendir(&dir, path);
if (res == FR_OK)
{
i = strlen(path);
for (;;)
{
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0) break;
if (fno.fname[0] == '.') continue;
#if FF_USE_LFN
fn = *fno.fname ? fno.fname : fno.altname;
#else
fn = fno.fname;
#endif
if (fno.fattrib & AM_DIR)
{
sprintf(&path[i], "%s/", fn);
res = scan_files(path);
if (res != FR_OK) break;
path[i] = 0;
} else
{
printf("%s%s \r\n", path, fn);
}
}
f_closedir(&dir);
}else printf("f_opendir %s error %d!\r\n",path,res);
return res;
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
GPIO_INIT();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("This is TF card example.\r\n");
if(FATFS_Init(&fs,path) == 0)
{
printf("FATFS Init OK.\r\n");
scan_files(path);
}else {
printf("FATFS Init Fail!!!\r\n");
}
while(1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0 | GPIO_Pin_1);
printf("led on.\r\n");
Delay_Ms(500);
GPIO_ResetBits(GPIOA,GPIO_Pin_0 | GPIO_Pin_1);
printf("led off.\r\n");
Delay_Ms(500);
}
}
开发板接线如下:
串口打印数据: