为了在 PSoC6 (CY8CPROTO-063-BLE) 上通过 SPI 接口驱动 SD 卡并与 emFile 库集成,你需要实现一个自定义的 SPI 驱动层。以下是完整的解决方案:
步骤 1:配置 SPI 外设(PSoC Creator / ModusToolbox)
在 PSoC 开发环境中配置 SPI 外设:
- 配置 SPI 主模式(MOSI, MISO, SCK)
- 分配引脚:
SD_SCK -> SCK (P6.0)
SD_MOSI -> MOSI (P6.1)
SD_MISO -> MISO (P6.2)
SD_CS -> 自定义 GPIO (P6.3)
- (可选)
SD_CD 卡检测引脚 (P6.4)
- 设置 SPI 参数:
- 模式: 0 (CPOL=0, CPHA=0)
- 初始化频率:400 kHz (SD 卡初始化要求)
- 最大频率:20-25 MHz(初始化后切换)
步骤 2:实现 SPI 驱动接口 (spi_sd.c)
#include "cyhal.h"
#include "cybsp.h"
#include "emFile_config.h" // emFile 配置文件
// 定义 SPI 对象和引脚
static cyhal_spi_t spi_sd;
static cyhal_gpio_t cs_pin = P6_3;
// SPI 初始化 (由 emFile 调用)
int SD_Init(void) {
cy_rslt_t result;
// 配置 SPI
cyhal_spi_cfg_t spi_cfg = {
.data_mode = CYHAL_SPI_MODE_00_MSB,
.submode = CYHAL_SPI_MOTOROLA,
.sclk_mode = CYHAL_SPI_SCLK_LOW_EDGE,
.ssel = CYHAL_SPI_SSEL_NONE, // 手动控制 CS
.is_slave = false,
.fifo_width = CYHAL_SPI_WIDTH_8,
.bits_per_frame = 8,
};
// 初始化 SPI
result = cyhal_spi_init(&spi_sd, P6_1, P6_2, P6_0, NC, NULL, &spi_cfg);
if(result != CY_RSLT_SUCCESS) return -1;
// 配置片选引脚
result = cyhal_gpio_init(cs_pin, CYHAL_GPIO_DIR_OUTPUT, CYHAL_GPIO_DRIVE_STRONG, 1);
if(result != CY_RSLT_SUCCESS) return -2;
// 设置低速(初始化阶段)
cyhal_spi_set_frequency(&spi_sd, SD_INIT_SPI_FREQ);
return 0;
}
// 发送/接收单字节 (emFile 基本操作)
BYTE SD_ReadWrite(BYTE out) {
uint8_t in = 0;
cyhal_spi_transfer(&spi_sd, &out, 1, &in, 1, 0);
return in;
}
// 控制片选信号 (emFile 要求)
void SD_CS(BOOL state) {
cyhal_gpio_write(cs_pin, state ? 0 : 1); // 低电平有效
}
// 高速切换 (初始化完成后调用)
void SD_SetHighSpeed(void) {
cyhal_spi_set_frequency(&spi_sd, SD_HIGH_SPI_FREQ); // 20-25 MHz
}
// 块数据传输 (优化性能)
void SD_ReadWriteMulti(const BYTE* out, BYTE* in, int count) {
cyhal_spi_transfer(&spi_sd, out, count, in, count, 0);
}
// 延迟函数
void SD_Delay(unsigned int ms) {
cyhal_system_delay_ms(ms);
}
步骤 3:配置 emFile (FS_ConfigIO.h)
/* SPI 接口配置 */
#define FS_SPI_INTERFACE_SUPPORT 1 // 启用 SPI 接口
#define FS_USE_SPI_DRIVER 1 // 使用自定义驱动
/* 连接到 spi_sd.c 的实现 */
extern int SD_Init(void);
extern void SD_CS(BOOL state);
extern BYTE SD_ReadWrite(BYTE data);
extern void SD_ReadWriteMulti(const BYTE* pOut, BYTE* pIn, int count);
extern void SD_Delay(unsigned int ms);
extern void SD_SetHighSpeed(void);
/* 接口映射 */
#define SPI_INIT SD_Init
#define SPI_CS_LOW() SD_CS(1)
#define SPI_CS_HIGH() SD_CS(0)
#define SPI_RW SD_ReadWrite
#define SPI_RW_MULTI SD_ReadWriteMulti
#define SPI_DELAY SD_Delay
#define SPI_SET_HIGH_SPEED SD_SetHighSpeed
步骤 4:SD 卡初始化流程
在应用程序中实现 SD 卡初始化序列:
#include "FS.h"
void init_sdcard() {
// 1. 初始化SPI接口
if(FS_InitInterface(0) != 0) {
printf("SPI init failed!");
return;
}
// 2. 检测并初始化SD卡
if(FS_Init(0) != 0) {
printf("SD init failed!");
return;
}
// 3. 切换到高速模式(成功初始化后)
SPI_SET_HIGH_SPEED();
// 4. 挂载文件系统
if(FS_Mount(0, 0) != 0) {
printf("Mount failed!");
}
}
关键注意事项:
SPI 模式要求:
- 必须支持模式0 (CPOL=0, CPHA=0)
- CS 信号在数据传输期间保持低电平
初始化序列:
- 先以 ≤400 kHz 频率初始化
- 执行 CMD0 (GO_IDLE_STATE)
- 处理 CMD8, ACMD41 等初始化命令
- 初始化完成后切换到高速模式
数据处理:
- 单字节传输用于命令响应
- 块传输(多字节)用于数据读写
- 读写期间保持 CS 有效
硬件连接确认:
PSoC6 SD卡模块
-------------------
P6.0 --> SCK
P6.1 --> MOSI
P6.2 <-- MISO
P6.3 --> CS
3.3V --> VCC
GND --> GND
5. 调试技巧:
- 使用逻辑分析仪检查 SPI 信号
- 初始阶段确保时钟 ≤400 kHz
- 检查所有命令的响应:
- CMD0 应返回 0x01 (IDLE)
- CMD8 确认电压范围
- ACMD41 完成初始化(返回0x00)
完成上述步骤后,emFile 库就能通过 SPI 驱动访问 SD 卡。实际使用中可能需要根据 SD 卡型号调整初始化参数和时序,具体参考 SD 物理层规范文档。