AI嵌入式K210项目(14)-TF卡读取

文章目录


前言

本章介绍一些读取TF卡的方法,本章内容和前面的项目非常类似,都是采用了SPI接口AI嵌入式K210项目(11)-SPI Flash读写

一、TF卡介绍

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的SPI接口

对应的头文件 spi.h

SPI是一种高速、高效率的串行接口技术。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少4根线(事实上在单向传输时3根线也可以),它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。

(1)MISO-- Master Input Slave Output,主设备数据输入,从设备数据输出;

(2)MOSI-- Master Output Slave Input,主设备数据输出,从设备数据输入;

(3)SCLK -- Serial Clock,时钟信号,由主设备产生;

(4)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 传输数据。

实验过程

代码比较多,demo会统一放到gitee上,这里只展示部分核心代码。

首先根据上面的硬件连接引脚图,完成硬件引脚和软件功能的映射关系

fpioa引脚映射

c 复制代码
#include "fpioa.h"
#include <stdio.h>
#include "sysctl.h"
#include "dmac.h"
#include "fpioa.h"
#include "sdcard.h"
#include "ff.h"
#include "i2s.h"
#include "plic.h"
#include "uarths.h"
#include "bsp.h"

/*****************************HARDWARE-PIN*********************************/
// 硬件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)

static int check_sdcard(void);
static int check_fat32(void);
FRESULT sd_write_file(TCHAR *path);
FRESULT sd_read_file(TCHAR *path);


/**
* Function       hardware_init
* @brief         硬件初始化,绑定GPIO口
* @param[in]     void
* @param[out]    void
* @retval        void
* @par History   无
*/
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);
}

/**
* Function       main
* @brief         主函数,程序的入口
* @param[in]     void
* @param[out]    void
* @retval        0
* @par History   无
*/
int main(void)
{
    // 硬件引脚初始化
    hardware_init();

    /* 设置系统时钟频率 */
    sysctl_pll_set_freq(SYSCTL_PLL0, 800000000UL);
    sysctl_pll_set_freq(SYSCTL_PLL1, 300000000UL);
    sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL);
    uarths_init();

    if (check_sdcard())
    {
        printf("SD card err\n");
        return -1;
    }

    if (check_fat32())
    {
        printf("FAT32 err\n");
        return -1;
    }

    sleep(1);
    if (sd_write_file(_T("0:test.txt")))
    {
        printf("SD write err\n");
        return -1;
    }

    if (sd_read_file(_T("0:test.txt")))
    {
        printf("SD read err\n");
        return -1;
    }
    
    while (1);
    return 0;
}

/**
* Function       check_sdcard
* @brief         检测TF是否正常
* @param[in]     void
* @param[out]    void
* @retval        0
* @par History   无
*/
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;
}

/**
* Function       check_fat32
* @brief         检测TF的格式是否FAT32
* @param[in]     void
* @param[out]    void
* @retval        0
* @par History   无
*/
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);
        else
            printf("file:%s\n", fno.fname);
        status = f_findnext(&dj, &fno);
    }
    f_closedir(&dj);
    return 0;
}

/**
* Function       sd_write_file
* @brief         写入文件到TF卡
* @param[in]     void
* @param[out]    void
* @retval        0
* @par History   无
*/
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','e','l','l','o',',','W','o','r','l','d','!','T','e','s','t',',','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;
}

/**
* Function       sd_read_file
* @brief         从TF卡读取文件
* @param[in]     void
* @param[out]    void
* @retval        0
* @par History   无
*/
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;
}

代码完成后,进行编译

c 复制代码
cd build

cmake .. -DPROJ=tf -G "MinGW Makefiles"

make

编译完成后,在build文件夹下会生成tf.bin文件。

使用type-C数据线连接电脑与K210开发板,打开kflash,选择对应的设备,再将程序固件烧录到K210开发板上。

烧录后重启开发板,实验结果如下:


总结

TF卡的读写和flash的读写类似,只不过TF卡的读写引入了文件系统

相关推荐
NAGNIP10 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab11 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab11 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
哥不是小萝莉12 小时前
OpenClaw 架构设计全解析
ai
AngelPP15 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年15 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼15 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS15 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
warm3snow15 小时前
Claude Code 黑客马拉松:5 个获奖项目,没有一个是"纯码农"做的
ai·大模型·llm·agent·skill·mcp
天翼云开发者社区16 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤