前言
此篇文章將講述使用以SPI讀取SD card並實現FatFS,最終的成果如下圖能以UART CLI輸入指令來操作SD card
實驗內容簡略
本篇文章不會講的;但是需要的功能
- SPI四線式收發
- Timer
- UART blocking收發
- RTC(選項)
操作環境
- Board:NUCLEO-F401RE
- micro SD card模組
- IDE:STM32CubeIDE
- Code:STM32CubeMX
- Software Packs:ffsample.zip (從FatFS官網下戴)
MCU I/O配置
- SPI:CS(PB5),SCK(PA5),MOSI(PA7),MISO(PA6)
- UART:TX(PA2),RX(PA3)
- Timer:1ms
- RTC(選項)
- Button:PC13(選項)
FatFS使用到的檔案
從下戴的ffsample.zip裡找尋資料夾 \stm32,會須要用到下列幾個檔案
mmc_stm32f1_spi.c(diskio.c)使用SPI與SD card溝通,已建置SD card溝通的protocol,
大部分檔案都無須修改,就diskio.c要把SPI相關函式配合使用的平台做修改
- main.c
- mmc_stm32f1_spi.c (建議將檔案名改回diskio.c)
- diskio.h
- ff.c
- ff.h
- ffconf.h
- ffunicode.c
- xprintf.c
- xprintf.h
SD card小常識
SPI連接時的設定
- Vdd 3.3V
- 訊號線上要加 10k~100k的升壓電阻
上電時SD card的一連串初始化程序這邊不一一介紹,就先講頭二個(下面會用到)
1. 先以不超過400Khz給予至少74個clock(CS pin為High)
2. 發送 CMD0進入idle state(0x40 0x00 0x00 0x00 0x00 0x95)(CS pin為Low)
CMD0在spec裡雖然是沒有response,但在command 6個bytes後再送2個0xFF正常第2個byte是可以收到0以外的值
3. CS pin回復為High
驗証MCU與SD socket間的硬體連接
在把FatFS檔案加入project之前要先驗証MCU的SPI跟SD card socket能正常溝通
所以先試著用SPI發送CMD0指令看是否能正常
- SPI頻率小於等於400Khz
- 送出74個以上的clock:0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
- CMD0指令:0x40 0x00 0x00 0x00 0x00 0x95
- Response:0xFF 0xFF
下圖最後的0x01表示有接收到SD card返回的資料
加入UART CLI介面
如果上面的驗証沒問題,接下來就把ffsample.zip下面列出的檔案加入project
先完成UART的Command Line Interface(CLI)。(UART功能請先自行驗証能否正常發送/接收)
- diskio.h
- ff.c
- ff.h
- ffconf.h
- ffunicode.c
- xprintf.c
- xprintf.h
main.c
加入include
#include <stdio.h>
#include <string.h>
#include "ff.h"
#include "xprintf.h"
#include "diskio.h"
全域變數宣告
char Line[256]; /* Console input buffer */
uint8_t Buff[4096] __attribute__ ((aligned (4))) ; /* Working buffer */
FATFS FatFs; /* File system object for each logical drive */
FIL File[2]; /* File objects */
DIR Dir; /* Directory object */
FILINFO Finfo;
volatile UINT Timer;
CLI菜單顯示內容
const char HelpMsg[] =
"[Disk contorls]\n"
" di <pd#> - Initialize disk\n"
" dd [<pd#> <lba>] - Dump a secrtor\n"
" ds <pd#> - Show disk status\n"
"[Buffer controls]\n"
" bd <ofs> - Dump working buffer\n"
" be <ofs> [<data>] ... - Edit working buffer\n"
" br <pd#> <lba> [<count>] - Read disk into working buffer\n"
" bw <pd#> <lba> [<count>] - Write working buffer into disk\n"
" bf <val> - Fill working buffer\n"
"[File system controls]\n"
" fi <ld#> [<mount>]- Force initialized the volume\n"
" fs [<path>] - Show volume status\n"
" fl [<path>] - Show a directory\n"
" fo <mode> <file> - Open a file\n"
" fc - Close the file\n"
" fe <ofs> - Move fp in normal seek\n"
" fd <len> - Read and dump the file\n"
" fr <len> - Read the file\n"
" fw <len> <val> - Write to the file\n"
" fn <org.name> <new.name> - Rename an object\n"
" fu <name> - Unlink an object\n"
" fv - Truncate the file at current fp\n"
" fk <name> - Create a directory\n"
" fa <atrr> <mask> <object name> - Change attribute of an object\n"
" ft <year> <month> <day> <hour> <min> <sec> <name> - Change timestamp of an object\n"
" fx <src.file> <dst.file> - Copy a file\n"
" fg <path> - Change current directory\n"
" fq - Show current directory\n"
" fb <name> - Set volume label\n"
" fm <ld#> <type> <csize> - Create file system\n"
" fz [<len>] - Change/Show R/W length for fr/fw/fx command\n"
"[Misc commands]\n"
" md[b|h|w] <addr> [<count>] - Dump memory\n"
" mf <addr> <value> <count> - Fill memory\n"
" me[b|h|w] <addr> [<value> ...] - Edit memory\n"
" t [<year> <mon> <mday> <hour> <min> <sec>] - Set/Show RTC\n"
"\n";
UART發送/接收模組,這是根據使用平台不同自行增加的,在main()裡會用指標定義給xprintf的函式
void uart2_putc(uint8_t x)
{
uint8_t tx[2] = {x};
HAL_UART_Transmit(&huart2, tx, 1, 0xFF);
}
uint8_t uart2_getc(void)
{
uint8_t rx[2];
HAL_UART_Receive(&huart2, rx, 1, HAL_MAX_DELAY);
return rx[0];
}
main.c
static const char *ft[] = {"", "FAT12", "FAT16", "FAT32", "exFAT"};
static const char days[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
char *ptr, *ptr2;
long p1, p2, p3;
BYTE res, b, drv = 0;
UINT s1, s2, cnt, blen = sizeof Buff, acc_files, acc_dirs;
DWORD ofs = 0, sect = 0, blk[2], dw;
QWORD acc_size;
FATFS *fs;
int main(void)
{
xdev_in(uart2_getc);//將UART接收模組指到xprintf.c的xfunc_input
xdev_out(uart2_putc);//將UART接收模組指到xprintf.c的xfunc_output
while (1)
{
xputc('>');
xgets(Line, sizeof Line);
ptr = Line;
switch (*ptr++) {
case '?' : /* Show Command List */
xputs(HelpMsg);
break;
}
}
}
完成以上步驟後,在超級終端機輸入 符號? 接下Enter就可以顯示出菜單(目前只有顯示;但無功能)
實現SD card溝通的協議
把mmc_stm32f1_spi.c (diskio.c)加入project
diskio.c
加進diskio.c後編譯會出現不少error,下列I/O接口依您使用平台做修改
#include "main.h"
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include "stm32f401xe.h"
#define _BV(bit) (1<<(bit))
#define SPI_CH 1 /* SPI channel to use = 1: SPI1, 11: SPI1/remap, 2: SPI2 */
#define FCLK_SLOW() { SPI1->CR1 = (SPIx_CR1 & ~0x38) | SPI_BAUDRATEPRESCALER_256; } /* Set SCLK = PCLK / 256 */
#define FCLK_FAST() { SPI1->CR1 = (SPIx_CR1 & ~0x38) | SPI_BAUDRATEPRESCALER_16; } /* Set SCLK = PCLK / 16 */
#if SPI_CH == 1 /* PA4:MMC_CS, PA5:MMC_SCLK, PA6:MMC_DO, PA7:MMC_DI, PC4:MMC_CD */
#define CS_HIGH() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET)//GPIOA_BSRR = _BV(4)
#define CS_LOW() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET)//GPIOA_BSRR = _BV(4+16)
#define MMC_CD 1//!(GPIOC_IDR & _BV(4)) /* Card detect (yes:true, no:false, default:true) */
#define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */
#define SPIx_CR1 SPI1->CR1//SPI1_CR1
#define SPIx_SR SPI1->SR//SPI1_SR
#define SPIx_DR SPI1->DR//SPI1_DR
#define SPIxENABLE() {\
}
diskio.c
SPI發送/接收模組依使用平台做修改
static BYTE xchg_spi (
BYTE dat /* Data to send */
)
{
// SPIx_DR = dat; /* Start an SPI transaction */
// while ((SPIx_SR & 0x83) != 0x03) ; /* Wait for end of the transaction */
// return (BYTE)SPIx_DR; /* Return received byte */
uint8_t tx[2] = {dat};
uint8_t rx[2] = {0};
extern SPI_HandleTypeDef hspi1;
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 1, 0xFFFF);
return rx[0];
}
main.c
新增RTC模組(從ffsample.zip \stm32\main.c複製),內容依使用平台修改
uint32_t get_fattime (void)
{
RTC_TimeTypeDef GetTime;
RTC_DateTypeDef GetData;
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
return ((uint32_t)(GetData.Year - 1980) << 25)
| ((uint32_t)GetData.Month << 21)
| ((uint32_t)GetData.Date << 16)
| ((uint32_t)GetTime.Hours << 11)
| ((uint32_t)GetTime.Minutes << 5)
| ((uint32_t)GetTime.Seconds >> 1);
}
main.c
新增(從ffsample.zip \stm32\main.c複製)
FRESULT scan_files (
char* path, /* Pointer to the path name working buffer */
UINT* n_dir,
UINT* n_file,
QWORD* sz_file
)
{
DIR dirs;
FRESULT res;
BYTE i;
if ((res = f_opendir(&dirs, path)) == FR_OK) {
i = strlen(path);
while (((res = f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) {
if (Finfo.fattrib & AM_DIR) {
(*n_dir)++;
*(path+i) = '/'; strcpy(path+i+1, Finfo.fname);
res = scan_files(path, n_dir, n_file, sz_file);
*(path+i) = '\0';
if (res != FR_OK) break;
} else {
/* xprintf("%s/%s\n", path, fn); */
(*n_file)++;
*sz_file += Finfo.fsize;
}
}
}
return res;
}
void put_rc (FRESULT rc)
{
const char *str =
"OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
"INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0" "WRITE_PROTECTED\0"
"INVALID_DRIVE\0" "NOT_ENABLED\0" "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
"LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0" "INVALID_PARAMETER\0";
FRESULT i;
for (i = 0; i != rc && *str; i++) {
while (*str++) ;
}
xprintf("rc=%u FR_%s\n", (UINT)rc, str);
}
main.c
在1ms Timer的callback加入
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
extern void disk_timerproc (void);
Timer++;
disk_timerproc();
}
main.c
完善CLI菜單的選項,基本上都不用修改;case 't' RTC的功能,依使用者平台自行調整
while(1)
{
xputc('>');
xgets(Line, sizeof Line);
ptr = Line;
switch (*ptr++) {
case '?' : /* Show Command List */
xputs(HelpMsg);
break;
case 'm' : /* Memory dump/fill/edit */
switch (*ptr++) {
case 'd' : /* md[b|h|w] <address> [<count>] - Dump memory */
switch (*ptr++) {
case 'w': p3 = 4; break;
case 'h': p3 = 2; break;
default: p3 = 1;
}
if (!xatoi(&ptr, &p1)) break;
if (!xatoi(&ptr, &p2)) p2 = 128 / p3;
for (ptr = (char*)p1; p2 >= 16 / p3; ptr += 16, p2 -= 16 / p3)
put_dump(ptr, (DWORD)ptr, 16 / p3, p3);
if (p2) put_dump((BYTE*)ptr, (UINT)ptr, p2, p3);
break;
case 'f' : /* mf <address> <value> <count> - Fill memory */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) break;
while (p3--) {
*(BYTE*)p1 = (BYTE)p2;
p1++;
}
break;
case 'e' : /* me[b|h|w] <address> [<value> ...] - Edit memory */
switch (*ptr++) { /* Get data width */
case 'w': p3 = 4; break;
case 'h': p3 = 2; break;
default: p3 = 1;
}
if (!xatoi(&ptr, &p1)) break; /* Get start address */
if (xatoi(&ptr, &p2)) { /* 2nd parameter is given (direct mode) */
do {
switch (p3) {
case 4: *(DWORD*)p1 = (DWORD)p2; break;
case 2: *(WORD*)p1 = (WORD)p2; break;
default: *(BYTE*)p1 = (BYTE)p2;
}
p1 += p3;
} while (xatoi(&ptr, &p2)); /* Get next value */
break;
}
for (;;) { /* 2nd parameter is not given (interactive mode) */
switch (p3) {
case 4: xprintf("%08X 0x%08X-", p1, *(DWORD*)p1); break;
case 2: xprintf("%08X 0x%04X-", p1, *(WORD*)p1); break;
default: xprintf("%08X 0x%02X-", p1, *(BYTE*)p1);
}
ptr = Line; xgets(ptr, sizeof Line);
if (*ptr == '.') break;
if ((BYTE)*ptr >= ' ') {
if (!xatoi(&ptr, &p2)) continue;
switch (p3) {
case 4: *(DWORD*)p1 = (DWORD)p2; break;
case 2: *(WORD*)p1 = (WORD)p2; break;
default: *(BYTE*)p1 = (BYTE)p2;
}
}
p1 += p3;
}
break;
}
break;
case 'd' : /* Disk I/O layer controls */
switch (*ptr++) {
case 'd' : /* dd [<pd#> <sect>] - Dump secrtor */
if (!xatoi(&ptr, &p1)) {
p1 = drv; p2 = sect;
} else {
if (!xatoi(&ptr, &p2)) break;
}
drv = (BYTE)p1; sect = p2;
res = disk_read(drv, Buff, sect, 1);
if (res) { xprintf("rc=%d\n", (WORD)res); break; }
xprintf("PD#:%u LBA:%lu\n", drv, sect++);
for (ptr=(char*)Buff, ofs = 0; ofs < 0x200; ptr += 16, ofs += 16)
put_dump((BYTE*)ptr, ofs, 16, 1);
break;
case 'i' : /* di <pd#> - Initialize disk */
if (!xatoi(&ptr, &p1)) break;
xprintf("rc=%d\n", (WORD)disk_initialize((BYTE)p1));
break;
case 's' : /* ds <pd#> - Show disk status */
if (!xatoi(&ptr, &p1)) break;
if (disk_ioctl((BYTE)p1, GET_SECTOR_COUNT, &p2) == RES_OK)
{ xprintf("Drive size: %lu sectors\n", p2); }
if (disk_ioctl((BYTE)p1, GET_BLOCK_SIZE, &p2) == RES_OK)
{ xprintf("Block size: %lu sectors\n", p2); }
if (disk_ioctl((BYTE)p1, MMC_GET_TYPE, &b) == RES_OK)
{ xprintf("Media type: %u\n", b); }
if (disk_ioctl((BYTE)p1, MMC_GET_CSD, Buff) == RES_OK)
{ xputs("CSD:\n"); put_dump(Buff, 0, 16, 1); }
if (disk_ioctl((BYTE)p1, MMC_GET_CID, Buff) == RES_OK)
{ xputs("CID:\n"); put_dump(Buff, 0, 16, 1); }
if (disk_ioctl((BYTE)p1, MMC_GET_OCR, Buff) == RES_OK)
{ xputs("OCR:\n"); put_dump(Buff, 0, 4, 1); }
if (disk_ioctl((BYTE)p1, MMC_GET_SDSTAT, Buff) == RES_OK) {
xputs("SD Status:\n");
for (s1 = 0; s1 < 64; s1 += 16) put_dump(Buff+s1, s1, 16, 1);
}
break;
case 'c' : /* Disk ioctl */
switch (*ptr++) {
case 's' : /* dcs <pd#> - CTRL_SYNC */
if (!xatoi(&ptr, &p1)) break;
xprintf("rc=%d\n", disk_ioctl((BYTE)p1, CTRL_SYNC, 0));
break;
case 'e' : /* dce <pd#> <s.lba> <e.lba> - CTRL_TRIM */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, (long*)&blk[0]) || !xatoi(&ptr, (long*)&blk[1])) break;
xprintf("rc=%d\n", disk_ioctl((BYTE)p1, CTRL_TRIM, blk));
break;
}
break;
}
break;
case 'b' : /* Buffer controls */
switch (*ptr++) {
case 'd' : /* bd <ofs> - Dump R/W buffer */
if (!xatoi(&ptr, &p1)) break;
for (ptr=(char*)&Buff[p1], ofs = p1, cnt = 32; cnt; cnt--, ptr+=16, ofs+=16)
put_dump((BYTE*)ptr, ofs, 16, 1);
break;
case 'e' : /* be <ofs> [<data>] ... - Edit R/W buffer */
if (!xatoi(&ptr, &p1)) break;
if (xatoi(&ptr, &p2)) {
do {
Buff[p1++] = (BYTE)p2;
} while (xatoi(&ptr, &p2));
break;
}
for (;;) {
xprintf("%04X %02X-", (WORD)(p1), (WORD)Buff[p1]);
xgets(Line, sizeof Line);
ptr = Line;
if (*ptr == '.') break;
if (*ptr < ' ') { p1++; continue; }
if (xatoi(&ptr, &p2))
Buff[p1++] = (BYTE)p2;
else
xputs("???\n");
}
break;
case 'r' : /* br <pd#> <lba> [<num>] - Read disk into R/W buffer */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
if (!xatoi(&ptr, &p3)) p3 = 1;
xprintf("rc=%u\n", (WORD)disk_read((BYTE)p1, Buff, p2, p3));
break;
case 'w' : /* bw <pd#> <lba> [<num>] - Write R/W buffer into disk */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
if (!xatoi(&ptr, &p3)) p3 = 1;
xprintf("rc=%u\n", (WORD)disk_write((BYTE)p1, Buff, p2, p3));
break;
case 'f' : /* bf <val> - Fill working buffer */
if (!xatoi(&ptr, &p1)) break;
memset(Buff, (BYTE)p1, sizeof Buff);
break;
}
break;
case 'f' : /* FatFS API controls */
switch (*ptr++) {
case 'i' : /* fi [<opt>]- Initialize logical drive */
if (!xatoi(&ptr, &p2)) p2 = 0;
put_rc(f_mount(&FatFs, "", (BYTE)p2));
break;
case 's' : /* fs [<path>] - Show volume status */
while (*ptr == ' ') ptr++;
res = f_getfree(ptr, (DWORD*)&p1, &fs);
if (res) { put_rc(res); break; }
xprintf("FAT type = %s\n", ft[fs->fs_type]);
xprintf("Bytes/Cluster = %lu\n", (DWORD)fs->csize * 512);
xprintf("Number of FATs = %u\n", fs->n_fats);
if (fs->fs_type < FS_FAT32) xprintf("Root DIR entries = %u\n", fs->n_rootdir);
xprintf("Sectors/FAT = %lu\n", fs->fsize);
xprintf("Number of clusters = %lu\n", (DWORD)fs->n_fatent - 2);
xprintf("Volume start (lba) = %lu\n", fs->volbase);
xprintf("FAT start (lba) = %lu\n", fs->fatbase);
xprintf("DIR start (lba,clustor) = %lu\n", fs->dirbase);
xprintf("Data start (lba) = %lu\n\n", fs->database);
#if FF_USE_LABEL
res = f_getlabel(ptr, (char*)Buff, (DWORD*)&p2);
if (res) { put_rc(res); break; }
xprintf(Buff[0] ? "Volume name is %s\n" : "No volume label\n", (char*)Buff);
xprintf("Volume S/N is %04X-%04X\n", (DWORD)p2 >> 16, (DWORD)p2 & 0xFFFF);
#endif
acc_size = acc_files = acc_dirs = 0;
xprintf("...");
res = scan_files(ptr, &acc_dirs, &acc_files, &acc_size);
if (res) { put_rc(res); break; }
xprintf("\r%u files, %llu bytes.\n%u folders.\n"
"%lu KiB total disk space.\n%lu KiB available.\n",
acc_files, acc_size, acc_dirs,
(fs->n_fatent - 2) * (fs->csize / 2), (DWORD)p1 * (fs->csize / 2)
);
break;
case 'l' : /* fl [<path>] - Directory listing */
while (*ptr == ' ') ptr++;
res = f_opendir(&Dir, ptr);
if (res) { put_rc(res); break; }
acc_size = acc_dirs = acc_files = 0;
for(;;) {
res = f_readdir(&Dir, &Finfo);
if ((res != FR_OK) || !Finfo.fname[0]) break;
if (Finfo.fattrib & AM_DIR) {
acc_dirs++;
} else {
acc_files++; acc_size += Finfo.fsize;
}
xprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n",
(Finfo.fattrib & AM_DIR) ? 'D' : '-',
(Finfo.fattrib & AM_RDO) ? 'R' : '-',
(Finfo.fattrib & AM_HID) ? 'H' : '-',
(Finfo.fattrib & AM_SYS) ? 'S' : '-',
(Finfo.fattrib & AM_ARC) ? 'A' : '-',
(Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15, Finfo.fdate & 31,
(Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
Finfo.fsize, Finfo.fname);
}
xprintf("%4u File(s),%10llu bytes total\n%4u Dir(s)", acc_files, acc_size, acc_dirs);
res = f_getfree(ptr, &dw, &fs);
if (res == FR_OK) {
xprintf(", %10llu bytes free\n", (QWORD)dw * fs->csize * 512);
} else {
put_rc(res);
}
break;
case 'o' : /* fo <mode> <file> - Open a file */
if (!xatoi(&ptr, &p1)) break;
while (*ptr == ' ') ptr++;
put_rc(f_open(&File[0], ptr, (BYTE)p1));
break;
case 'c' : /* fc - Close a file */
put_rc(f_close(&File[0]));
break;
case 'e' : /* fe - Seek file pointer */
if (!xatoi(&ptr, &p1)) break;
res = f_lseek(&File[0], p1);
put_rc(res);
if (res == FR_OK) {
xprintf("fptr=%lu(0x%lX)\n", File[0].fptr, File[0].fptr);
}
break;
case 'd' : /* fd <len> - read and dump file from current fp */
if (!xatoi(&ptr, &p1)) break;
ofs = File[0].fptr;
while (p1) {
if ((UINT)p1 >= 16) { cnt = 16; p1 -= 16; }
else { cnt = p1; p1 = 0; }
res = f_read(&File[0], Buff, cnt, &cnt);
if (res != FR_OK) { put_rc(res); break; }
if (!cnt) break;
put_dump(Buff, ofs, cnt, 1);
ofs += 16;
}
break;
case 'r' : /* fr <len> - read file */
if (!xatoi(&ptr, &p1)) break;
p2 = 0;
Timer = 0;
while (p1) {
if ((UINT)p1 >= blen) {
cnt = blen; p1 -= blen;
} else {
cnt = p1; p1 = 0;
}
res = f_read(&File[0], Buff, cnt, &s2);
if (res != FR_OK) { put_rc(res); break; }
p2 += s2;
if (cnt != s2) break;
}
xprintf("%lu bytes read with %lu kB/sec.\n", p2, Timer ? (p2 / Timer) : 0);
break;
case 'w' : /* fw <len> <val> - write file */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
memset(Buff, (BYTE)p2, blen);
p2 = 0;
Timer = 0;
while (p1) {
if ((UINT)p1 >= blen) {
cnt = blen; p1 -= blen;
} else {
cnt = p1; p1 = 0;
}
res = f_write(&File[0], Buff, cnt, &s2);
if (res != FR_OK) { put_rc(res); break; }
p2 += s2;
if (cnt != s2) break;
}
xprintf("%lu bytes written with %lu kB/sec.\n", p2, Timer ? (p2 / Timer) : 0);
break;
case 'n' : /* fn <org.name> <new.name> - Change name of an object */
while (*ptr == ' ') ptr++;
ptr2 = strchr(ptr, ' ');
if (!ptr2) break;
*ptr2++ = 0;
while (*ptr2 == ' ') ptr2++;
put_rc(f_rename(ptr, ptr2));
break;
case 'u' : /* fu <name> - Unlink an object */
while (*ptr == ' ') ptr++;
put_rc(f_unlink(ptr));
break;
case 'v' : /* fv - Truncate file */
put_rc(f_truncate(&File[0]));
break;
case 'k' : /* fk <name> - Create a directory */
while (*ptr == ' ') ptr++;
put_rc(f_mkdir(ptr));
break;
case 'a' : /* fa <atrr> <mask> <name> - Change attribute of an object */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
while (*ptr == ' ') ptr++;
put_rc(f_chmod(ptr, p1, p2));
break;
case 't' : /* ft <year> <month> <day> <hour> <min> <sec> <name> - Change timestamp of an object */
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) break;
Finfo.fdate = ((p1 - 1980) << 9) | ((p2 & 15) << 5) | (p3 & 31);
if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) break;
Finfo.ftime = ((p1 & 31) << 11) | ((p2 & 63) << 5) | ((p3 >> 1) & 31);
put_rc(f_utime(ptr, &Finfo));
break;
case 'x' : /* fx <src.name> <dst.name> - Copy a file */
while (*ptr == ' ') ptr++;
ptr2 = strchr(ptr, ' ');
if (!ptr2) break;
*ptr2++ = 0;
while (*ptr2 == ' ') ptr2++;
xprintf("Opening \"%s\"", ptr);
res = f_open(&File[0], ptr, FA_OPEN_EXISTING | FA_READ);
xputc('\n');
if (res) {
put_rc(res);
break;
}
xprintf("Creating \"%s\"", ptr2);
res = f_open(&File[1], ptr2, FA_CREATE_ALWAYS | FA_WRITE);
xputc('\n');
if (res) {
put_rc(res);
f_close(&File[0]);
break;
}
xprintf("Copying file...");
Timer = 0;
p1 = 0;
for (;;) {
res = f_read(&File[0], Buff, blen, &s1);
if (res || s1 == 0) break; /* error or eof */
res = f_write(&File[1], Buff, s1, &s2);
p1 += s2;
if (res || s2 < s1) break; /* error or disk full */
}
xprintf("\n%lu bytes copied with %lu kB/sec.\n", p1, p1 / Timer);
f_close(&File[0]);
f_close(&File[1]);
break;
#if FF_FS_RPATH
case 'g' : /* fg <path> - Change current directory */
while (*ptr == ' ') ptr++;
put_rc(f_chdir(ptr));
break;
#if FF_FS_RPATH >= 2
case 'q' : /* fq - Show current dir path */
res = f_getcwd(Line, sizeof Line);
if (res)
put_rc(res);
else
xprintf("%s\n", Line);
break;
#endif
#endif
#if FF_USE_LABEL
case 'b' : /* fb <name> - Set volume label */
while (*ptr == ' ') ptr++;
put_rc(f_setlabel(ptr));
break;
#endif /* FF_USE_LABEL */
#if FF_USE_MKFS
case 'm' : /* fm [<fs type> [<au size> [<align> [<n_fats> [<n_root>]]]]] - Create filesystem */
{
MKFS_PARM opt, *popt = 0;
if (xatoi(&ptr, &p2)) {
memset(&opt, 0, sizeof opt);
popt = &opt;
popt->fmt = (BYTE)p2;
if (xatoi(&ptr, &p2)) {
popt->au_size = p2;
if (xatoi(&ptr, &p2)) {
popt->align = p2;
if (xatoi(&ptr, &p2)) {
popt->n_fat = (BYTE)p2;
if (xatoi(&ptr, &p2)) {
popt->n_root = p2;
}
}
}
}
}
xprintf("The volume will be formatted. Are you sure? (Y/n)=");
xgets(Line, sizeof Line);
if (Line[0] == 'Y') put_rc(f_mkfs("", popt, Buff, sizeof Buff));
break;
}
#endif /* FF_USE_MKFS */
case 'z' : /* fz [<size>] - Change/Show R/W length for fr/fw/fx command */
if (xatoi(&ptr, &p1) && p1 >= 1 && p1 <= (long)sizeof Buff)
blen = p1;
xprintf("blen=%u\n", blen);
break;
}
break;
case 't' : /* t [<year> <mon> <mday> <hour> <min> <sec>] - Set/Show RTC */
if (xatoi(&ptr, &p1)) {
rtc.year = (WORD)p1;
xatoi(&ptr, &p1); rtc.month = (BYTE)p1;
xatoi(&ptr, &p1); rtc.mday = (BYTE)p1;
xatoi(&ptr, &p1); rtc.hour = (BYTE)p1;
xatoi(&ptr, &p1); rtc.min = (BYTE)p1;
if (!xatoi(&ptr, &p1)) break;
rtc.sec = (BYTE)p1;
rtc_settime(&rtc);
}
rtc_gettime(&rtc);
xprintf("%u/%u/%u %s %02u:%02u:%02u\n", rtc.year, rtc.month, rtc.mday, &days[rtc.wday*4], rtc.hour, rtc.min, rtc.sec);
break;
}
}
驗收成果
1. 初始化裝置
指令:di
參數:0 (裝置0)
返回:rc=0 (0 即成功;大於0的值皆為失敗)
2. 顯示裝置狀態
指令:ds
參數:0 (裝置0)
返回:顯示即成功
最後附上程式連結 F401_SDcard.7z
針對diskio.c的send_cmd()增加CRC計算 F401_SDcard_v04.7z
修正fw只能寫入一個字元的issue F401_SDcard_v05.7z