開發板
亞博K210開發板
實驗目的
本實驗主要學習 K210 通過 SPI 讀寫內存卡文件的功能
實驗準備
實驗元件
開發板自帶的 TF 卡、LCD 顯示屏
(提前準備好 FAT32 格式的TF 卡。TF 插入 TF 卡槽的時候注意方向,TF 卡的金手指那一面需要面向開發板,如果方向插反了是無法讀寫數據的。TF卡尺寸小,性能好,傳輸速度快,最初是用于支持內存擴展的手機上,比較適合儲存高清攝像和高音質音頻內容,因而被廣泛應用于各種多媒體設備上)
硬件連接
K210 開發板出廠默認已經焊接 TF 卡槽,需要插入 TF 卡才可以使用,其中TF 卡槽的 TF_MISO 連接到 IO26,TF_CLK 連接到 IO27,TF_MOSI 連接到 IO28,TF_CSO連接 IO29。
SDK中相關API
SPI 是一種高速、高效率的串行接口技術。通常由一個主模塊和一個或多個從模塊組成,主模塊選擇一個從模塊進行同步通信,從而完成數據的交換。SPI是一個環形結構,通信時需要至少 4 根線(事實上在單向傳輸時 3 根線也可以),它們是 MISO(主設備數據輸入)、MOSI(主設備數據輸出)、SCLK(時鐘)、CS(片選)。
- MISO– Master Input Slave Output,主設備數據輸入,從設備數據輸出;
- MOSI– Master Output Slave Input,主設備數據輸出,從設備數據輸入;
- SCLK – Serial Clock,時鐘信號,由主設備產生;
- CS – Chip Select,從設備使能信號,由主設備控制。當有多個從設備的時候,因為每個從設備上都有一個片選引腳接入到主設備機中,當我們的主設備和某個從設備通信時將需要將從設備對應的片選引腳電平拉低或者是拉高。
相關接口
spi_init:設置 SPI 工作模式、多線模式和位寬。
spi_init_non_standard:多線模式下設置指令長度、地址長度、等待時鐘數、指令地址傳輸模式。
spi_send_data_standard:SPI 標準模式傳輸數據。
spi_send_data_standard_dma:SPI 標準模式下使用 DMA 傳輸數據。
spi_receive_data_standard:標準模式下接收數據。
spi_receive_data_standard_dma:標準模式下通過 DMA 接收數據。
spi_send_data_multiple:多線模式發送數據。
spi_send_data_multiple_dma:多線模式使用 DMA 發送數據。
spi_receive_data_multiple:多線模式接收數據。
spi_receive_data_multiple_dma:多線模式通過 DMA 接收。
spi_fill_data_dma:通過 DMA 始終發送同一個數據,可以用于刷新數據。
spi_send_data_normal_dma:通過 DMA 發送數據。不用設置指令地址。
spi_set_clk_rate:設置 SPI 的時鐘頻率。
spi_handle_data_dma:SPI 通過 DMA 傳輸數據。
實驗原理
TF 有 4 個數據傳輸端,DAT0,DAT1,DAT2,DAT3。還有一個 CMD 腳,是用來讀取卡內信息的。
TF 卡主要管腳的功能:
CLK:時鐘信號,每個時鐘周期傳輸一個命令或數據位,頻率可在 0~25MHz之間變化,TF 卡的總線管理器可以不受任何限制的自由產生 0~25MHz 的頻率;
CMD:雙向命令和回復線,命令是主機到從卡操作的開始,命令可以是從主機到單卡尋址,也可以是到所有卡;回復是對之前命令的回答,回復可以來自單卡或所有卡;
DAT0~3:數據線,數據可以從 TF 卡傳向主機也可以從主機傳向 TF 卡。TF 卡傳輸數據一般有兩種模式,SD 模式和 SPI 模式,這里我們以 SPI 模式的方式傳輸數據。SPI 模式引腳如下:1:CS,2:DI,3:VSS,4:VDD,5:SCLK,6:VSS2,7:DO,8:RSV,9:RSV。
實驗過程
- 首先初始化 K210 的硬件引腳和軟件功能使用的是 FPIOA 映射關系。
void hardware_init(void)
{/*** io26--miso--d1** io27--clk---sclk** io28--mosi--d0** io29--cs----cs*/fpioa_set_function(PIN_TF_MISO, FUNC_TF_SPI_MISO);fpioa_set_function(PIN_TF_CLK, FUNC_TF_SPI_CLK);fpioa_set_function(PIN_TF_MOSI, FUNC_TF_SPI_MOSI);fpioa_set_function(PIN_TF_CS, FUNC_TF_SPI_CS);
}
// 硬件IO口,與原理圖對應
#define PIN_TF_MISO (26)
#define PIN_TF_CLK (27)
#define PIN_TF_MOSI (28)
#define PIN_TF_CS (29)/*****************************SOFTWARE-GPIO********************************/
// 軟件GPIO口,與程序對應
#define TF_CS_GPIONUM (2)/*****************************FUNC-GPIO************************************/
// GPIO口的功能,綁定到硬件IO口
#define FUNC_TF_SPI_MISO (FUNC_SPI1_D1)
#define FUNC_TF_SPI_CLK (FUNC_SPI1_SCLK)
#define FUNC_TF_SPI_MOSI (FUNC_SPI1_D0)
#define FUNC_TF_SPI_CS (FUNC_GPIOHS0 + TF_CS_GPIONUM)
- 設置系統時鐘頻率,由于 uarths 的時鐘來自 PLL0,所以設置 PLL0 之后需
要重新初始化以下 uarths,否則 printf 可能會打印亂碼。
/* 設置系統時鐘頻率 */
sysctl_pll_set_freq(SYSCTL_PLL0, 800000000UL);
sysctl_pll_set_freq(SYSCTL_PLL1, 300000000UL);
sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL);
uarths_init();
- 檢測是否有 TF 卡,或者 TF 卡是否正常,如果不正常則退出。
if (check_sdcard())
{printf("SD card err\n");return -1;
}
如果初始化 TF 卡成功,則把 TF 的容量打印出來,單位為 G。
static int check_sdcard(void)
{uint8_t status;printf("/******************check_sdcard*****************/\n");status = sd_init();printf("sd init :%d\n", status);if (status != 0){return status;}printf("CardCapacity:%.1fG \n", (double)cardinfo.CardCapacity / 1024 / 1024 / 1024);printf("CardBlockSize:%d\n", cardinfo.CardBlockSize);return 0;
}
- 檢測 TF 卡的格式是否是 FAT32,如果不是則退出。
if (check_fat32())
{printf("FAT32 err\n");return -1;
}
如果檢測符合 FAT32 格式的 TF 卡,則把 TF 卡掛在到“0:”,并且把 TF 卡根
目錄下的文件和文件夾的名稱都打印出來。
static int check_fat32(void)
{static FATFS sdcard_fs;FRESULT status;DIR dj;FILINFO fno;printf("/********************check_fat32*******************/\n");status = f_mount(&sdcard_fs, _T("0:"), 1);printf("mount sdcard:%d\n", status);if (status != FR_OK)return status;printf("printf filename\n");status = f_findfirst(&dj, &fno, _T("0:"), _T("*"));while (status == FR_OK && fno.fname[0]){if (fno.fattrib & AM_DIR)printf("dir:%s\n", fno.fname);elseprintf("file:%s\n", fno.fname);status = f_findnext(&dj, &fno);}f_closedir(&dj);return 0;
}
- 向 TF 卡寫入文件,保存在 TF 卡跟目錄下的 test.txt,如果寫入失敗則退
出。
sleep(1);
if (sd_write_file(_T("0:test.txt")))
{printf("SD write err\n");return -1;
}
在寫入文件之前需要先打開文件,如果文件不存在則需要創建新文件,寫入的數據都需要轉化成 uint8_t 類型,寫完數據后必須執行 f_close 關閉文件。
FRESULT sd_write_file(TCHAR *path)
{FIL file;FRESULT ret = FR_OK;printf("/*******************sd_write_file*******************/\n");uint32_t v_ret_len = 0;/* 打開文件,如果文件不存在,則新建 */if ((ret = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK){printf("open file %s err[%d]\n", path, ret);return ret;}else{printf("Open %s ok\n", path);}/* 要寫入的數據 */uint8_t data[] = {'H','i',',','D','a','t','a',' ','W','r','i','t','e',' ','O','k','!'};/* 寫入數據 */ret = f_write(&file, data, sizeof(data), &v_ret_len);if (ret != FR_OK){printf("Write %s err[%d]\n", path, ret);}else{printf("Write %d bytes to %s ok\n", v_ret_len, path);}/* 關閉文件 */f_close(&file);return ret;
}
- 從 TF 卡讀取文件,文件為在 TF 卡根目錄下的 test.txt。
if (sd_read_file(_T("0:test.txt")))
{printf("SD read err\n");return -1;}
讀文件前需要判斷文件的狀態,并且打開文件,如果文件不存在或者出錯,則返回,如果正常則以只讀方式打開文件,并且把讀到的數據通過串口發送出來。
FRESULT sd_read_file(TCHAR *path)
{FIL file;FRESULT ret = FR_OK;printf("/*******************sd_read_file*******************/\n");uint32_t v_ret_len = 0;/* 檢測文件狀態 */FILINFO v_fileinfo;if ((ret = f_stat(path, &v_fileinfo)) == FR_OK){printf("%s length is %lld\n", path, v_fileinfo.fsize);}else{printf("%s fstat err [%d]\n", path, ret);return ret;}/* 只讀方式打開文件 */if ((ret = f_open(&file, path, FA_READ)) == FR_OK){char v_buf[64] = {0};ret = f_read(&file, (void *)v_buf, 64, &v_ret_len);if (ret != FR_OK){printf("Read %s err[%d]\n", path, ret);}else{printf("Read :> %s \n", v_buf);printf("total %d bytes lenth\n", v_ret_len);}/* 關閉文件 */f_close(&file);}return ret;
}
- 編譯調試,燒錄運行
進入 build 目錄,運行以下命令編譯。
cmake .. -DPROJ=sdcard -G "MinGW Makefiles"
make
編譯完成后,在 build 文件夾下會生成 sdcard.bin 文件。使用type-C 數據線連接電腦與 K210開發板,打開kflash,選擇對應的設備,再將程序固件燒錄到 K210 開發板上。
實驗現象
燒錄完成固件后,系統會彈出一個終端界面,如果沒有彈出終端界面的可以打開串口助手顯示調試內容。
實驗總結
1.TF 讀或寫文件前都必須先打開文件,讀寫操作結束后也必須關閉文件。
2.TF 卡通過 SPI 通訊的方式,讀寫數據以 uint8_t 為基本單位。
3.每次燒錄完固件后,都需要重新給 K210 開發板上電,否則會出現 TF 卡初始化
失敗而退出系統的問題。