1. 概述
SPI(Serial Peripheral Interface)是YTM32B1M微控制器中的高速同步串行通信接口,支持全双工通信和多种传输模式。本文档详细解析YTM32B1M SDK中SPI驱动的实现,包括主从模式配置、传输控制、时序参数设置和DMA支持等功能。
2. 文件组织结构
2.1 头文件
-
spi_master_driver.h: SPI主机模式驱动头文件
-
spi_slave_driver.h: SPI从机模式驱动头文件
-
spi_shared_function.h: SPI共享功能和数据结构定义
-
spi_hw_access.h: SPI硬件访问层头文件
2.2 源文件
-
spi_master_driver.c: SPI主机模式驱动实现
-
spi_slave_driver.c: SPI从机模式驱动实现
-
spi_shared_function.c: SPI共享功能实现
-
spi_hw_access.c: SPI硬件抽象层实现
2.3 架构层次
应用层 ↓ spi_master_driver.h/c + spi_slave_driver.h/c (高层API) ↓ spi_shared_function.h/c (共享功能层) ↓ spi_hw_access.h/c (硬件抽象层) ↓ SPI寄存器定义 ↓ SPI硬件控制器
3. 核心数据结构解析
3.1 片选信号枚举
typedef enum { SPI_PCS0 = 0U, // 片选信号0 SPI_PCS1 = 1U, // 片选信号1 SPI_PCS2 = 2U, // 片选信号2 SPI_PCS3 = 3U, // 片选信号3 SPI_PCS4 = 4U, // 片选信号4(可选) SPI_PCS5 = 5U, // 片选信号5(可选) SPI_PCS6 = 6U, // 片选信号6(可选) SPI_PCS7 = 7U, // 片选信号7(可选) } spi_which_pcs_t;
3.2 信号极性枚举
typedef enum { SPI_ACTIVE_HIGH = 1U, // 信号高电平有效(空闲低电平) SPI_ACTIVE_LOW = 0U // 信号低电平有效(空闲高电平) } spi_signal_polarity_t;
3.3 时钟相位枚举
typedef enum { SPI_CLOCK_PHASE_1ST_EDGE = 0U, // 第1个边沿采样,第2个边沿改变 SPI_CLOCK_PHASE_2ND_EDGE = 1U // 第1个边沿改变,第2个边沿采样 } spi_clock_phase_t;
3.4 时钟极性枚举
typedef enum { SPI_SCK_ACTIVE_HIGH = 0U, // 时钟高电平有效(空闲低电平) SPI_SCK_ACTIVE_LOW = 1U // 时钟低电平有效(空闲高电平) } spi_sck_polarity_t;
3.5 传输类型枚举
typedef enum { SPI_USING_DMA = 0, // 使用DMA进行传输 SPI_USING_INTERRUPTS, // 使用中断进行传输 } spi_transfer_type;
3.6 传输宽度枚举
typedef enum { SPI_SINGLE_BIT_XFER = 0U, // 1位传输(标准模式) SPI_TWO_BIT_XFER = 1U, // 2位传输(双线模式) SPI_FOUR_BIT_XFER = 2U // 4位传输(四线模式) } spi_transfer_width_t;
3.7 传输状态枚举
typedef enum { SPI_TRANSFER_OK = 0U, // 传输正常 SPI_TRANSMIT_FAIL, // 发送失败 SPI_RECEIVE_FAIL // 接收失败 } transfer_status_t;
3.8 SPI状态结构
typedef struct { uint16_t bitsPerFrame; // 每帧位数(8-4096位) uint16_t bytesPerFrame; // 每帧字节数(1-512字节) bool isPcsContinuous; // 片选连续保持标志 bool isBlocking; // 阻塞传输标志 uint32_t spiSrcClk; // 模块源时钟 volatile bool isTransferInProgress; // 传输进行中标志 const uint8_t *txBuff; // 发送缓冲区指针 uint8_t *rxBuff; // 接收缓冲区指针 volatile uint16_t txCount; // 剩余发送字节数 volatile uint16_t rxCount; // 剩余接收字节数 volatile uint16_t txFrameCnt; // 当前帧已发送字节数 volatile uint16_t rxFrameCnt; // 当前帧已接收字节数 volatile bool lsb; // LSB优先标志 uint8_t fifoSize; // FIFO大小 uint8_t rxDMAChannel; // 接收DMA通道 uint8_t txDMAChannel; // 发送DMA通道 spi_transfer_type transferType; // 传输类型 semaphore_t spiSemaphore; // 阻塞传输信号量 transfer_status_t status; // 传输状态 spi_callback_t callback; // 传输完成回调函数 void *callbackParam; // 回调函数参数 uint32_t dummy; // DMA模式下的虚拟数据 } spi_state_t;
3.9 SPI主机配置结构
typedef struct { uint32_t bitsPerSec; // 波特率(位/秒) spi_which_pcs_t whichPcs; // 片选信号选择 spi_signal_polarity_t pcsPolarity; // 片选极性 bool isPcsContinuous; // 片选连续保持 uint16_t bitcount; // 每帧位数(最少8位) spi_clock_phase_t clkPhase; // 时钟相位 spi_sck_polarity_t clkPolarity; // 时钟极性 bool lsbFirst; // LSB优先传输 spi_transfer_type transferType; // 传输类型 uint8_t rxDMAChannel; // 接收DMA通道号 uint8_t txDMAChannel; // 发送DMA通道号 spi_callback_t callback; // 传输完成回调 void *callbackParam; // 回调参数 spi_transfer_width_t width; // 传输宽度 } spi_master_config_t;
4. SPI通信协议框架图
主机设备 从机设备 ┌─────────────┐ ┌─────────────┐ │ 主机 │ │ 从机 │ │ │ │ │ │ MOSI ────┼────────────────────────────────►│ MOSI │ │ │ │ │ │ MISO ◄───┼─────────────────────────────────┤ MISO │ │ │ │ │ │ SCK ────┼────────────────────────────────►│ SCK │ │ │ │ │ │ PCS0 ────┼────────────────────────────────►│ PCS │ │ │ │ │ └─────────────┘ └─────────────┘ 时序图: PCS ────┐ ┌──── └────────────────────────────────────┘ SCK ────┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──── └───┘ └───┘ └───┘ └───┘ └───┘ MOSI ──── D7 ── D6 ── D5 ── D4 ── D3 ── D2 ── D1 ── D0 ──── MISO ──── D7 ── D6 ── D5 ── D4 ── D3 ── D2 ── D1 ── D0 ────
5. 核心API接口详解
5.1 初始化和配置接口
// 获取默认配置 void SPI_DRV_MasterGetDefaultConfig(spi_master_config_t *spiConfig); // 初始化SPI主机 status_t SPI_DRV_MasterInit(uint32_t instance, spi_state_t *spiState, const spi_master_config_t *spiConfig); // 去初始化SPI status_t SPI_DRV_MasterDeinit(uint32_t instance); // 配置总线参数 status_t SPI_DRV_MasterConfigureBus(uint32_t instance, const spi_master_config_t *spiConfig, uint32_t *calculatedBaudRate);
5.2 时序配置接口
// 设置传输延时 status_t SPI_DRV_MasterSetDelay(uint32_t instance, uint32_t delayBetwenTransfers, uint32_t delaySCKtoPCS, uint32_t delayPCStoSCK); // 设置片选信号 status_t SPI_DRV_SetPcs(uint32_t instance, spi_which_pcs_t whichPcs, spi_signal_polarity_t polarity);
5.3 轮询传输接口
// 轮询传输 status_t SPI_DRV_MasterTransferPolling(uint32_t instance, const uint8_t *sendBuffer, uint8_t *receiveBuffer, uint16_t transferByteCount);
5.4 阻塞传输接口
// 阻塞传输 status_t SPI_DRV_MasterTransferBlocking(uint32_t instance, const uint8_t *sendBuffer, uint8_t *receiveBuffer, uint16_t transferByteCount, uint32_t timeout);
5.5 非阻塞传输接口
// 非阻塞传输 status_t SPI_DRV_MasterTransfer(uint32_t instance, const uint8_t *sendBuffer, uint8_t *receiveBuffer, uint16_t transferByteCount); // 获取传输状态 status_t SPI_DRV_MasterGetTransferStatus(uint32_t instance, uint32_t *bytesRemained); // 中止传输 status_t SPI_DRV_MasterAbortTransfer(uint32_t instance);
6. 外设应用描述
6.1 SPI的主要应用场景
-
存储器接口: Flash、EEPROM、SD卡等存储设备通信
-
显示器驱动: LCD、OLED等显示设备控制
-
传感器接口: 数字传感器数据读取
-
ADC/DAC: 外部模数/数模转换器通信
-
无线模块: WiFi、蓝牙等无线通信模块接口
-
实时时钟: RTC芯片时间设置和读取
6.2 典型应用示例
6.2.1 基本SPI主机初始化
#include "spi_master_driver.h" // SPI状态结构 spi_state_t spiState; void spi_master_init_example(void) { spi_master_config_t spiConfig; // 获取默认配置 SPI_DRV_MasterGetDefaultConfig(&spiConfig); // 修改配置参数 spiConfig.bitsPerSec = 1000000; // 1MHz波特率 spiConfig.whichPcs = SPI_PCS0; // 使用PCS0 spiConfig.pcsPolarity = SPI_ACTIVE_LOW; // 片选低电平有效 spiConfig.isPcsContinuous = false; // 片选不连续 spiConfig.bitcount = 8; // 8位数据帧 spiConfig.clkPhase = SPI_CLOCK_PHASE_1ST_EDGE; // 第1边沿采样 spiConfig.clkPolarity = SPI_SCK_ACTIVE_HIGH; // 时钟高电平有效 spiConfig.lsbFirst = false; // MSB优先 spiConfig.transferType = SPI_USING_INTERRUPTS; // 中断模式 spiConfig.width = SPI_SINGLE_BIT_XFER; // 单线传输 // 初始化SPI主机 status_t status = SPI_DRV_MasterInit(0, &spiState, &spiConfig); if (status != STATUS_SUCCESS) { // 初始化失败处理 } }
6.2.2 轮询模式传输示例
void spi_polling_transfer_example(void) { uint8_t txData[4] = {0x01, 0x02, 0x03, 0x04}; uint8_t rxData[4]; // 轮询传输 status_t status = SPI_DRV_MasterTransferPolling(0, txData, rxData, sizeof(txData)); if (status == STATUS_SUCCESS) { // 传输成功,处理接收到的数据 processReceivedData(rxData, sizeof(rxData)); } else { // 传输失败处理 } }
6.2.3 阻塞传输示例
void spi_blocking_transfer_example(void) { uint8_t command = 0x9F; // 读取设备ID命令 uint8_t deviceId[3]; // 发送命令 status_t status = SPI_DRV_MasterTransferBlocking(0, &command, NULL, 1, 1000); if (status == STATUS_SUCCESS) { // 读取设备ID status = SPI_DRV_MasterTransferBlocking(0, NULL, deviceId, sizeof(deviceId), 1000); if (status == STATUS_SUCCESS) { // 处理设备ID printf("Device ID: %02X %02X %02X\n", deviceId[0], deviceId[1], deviceId[2]); } } }
6.2.4 非阻塞传输示例
volatile bool transferComplete = false; void spiCallback(void *driverState, spi_event_t event, void *userData) { if (event == SPI_EVENT_END_TRANSFER) { transferComplete = true; } } void spi_nonblocking_transfer_example(void) { uint8_t txData[16] = {0x01, 0x02, 0x03, /* ... */}; uint8_t rxData[16]; // 设置回调函数 spi_master_config_t spiConfig; SPI_DRV_MasterGetDefaultConfig(&spiConfig); spiConfig.callback = spiCallback; spiConfig.callbackParam = NULL; // 重新配置SPI SPI_DRV_MasterConfigureBus(0, &spiConfig, NULL); // 启动非阻塞传输 status_t status = SPI_DRV_MasterTransfer(0, txData, rxData, sizeof(txData)); if (status == STATUS_SUCCESS) { // 等待传输完成 while (!transferComplete) { // 可以执行其他任务 } transferComplete = false; // 处理接收到的数据 processReceivedData(rxData, sizeof(rxData)); } }
6.2.5 DMA模式传输示例
void spi_dma_setup_example(void) { spi_master_config_t spiConfig; // 获取默认配置 SPI_DRV_MasterGetDefaultConfig(&spiConfig); // 配置DMA模式 spiConfig.transferType = SPI_USING_DMA; spiConfig.rxDMAChannel = 0; // 接收DMA通道0 spiConfig.txDMAChannel = 1; // 发送DMA通道1 spiConfig.bitsPerSec = 5000000; // 5MHz高速传输 // 初始化SPI SPI_DRV_MasterInit(0, &spiState, &spiConfig); // 大数据量传输 uint8_t largeTxBuffer[1024]; uint8_t largeRxBuffer[1024]; // 填充发送数据 for (int i = 0; i < sizeof(largeTxBuffer); i++) { largeTxBuffer[i] = i & 0xFF; } // DMA传输 status_t status = SPI_DRV_MasterTransfer(0, largeTxBuffer, largeRxBuffer, sizeof(largeTxBuffer)); if (status == STATUS_SUCCESS) { // 检查传输状态 uint32_t bytesRemained; do { SPI_DRV_MasterGetTransferStatus(0, &bytesRemained); } while (bytesRemained > 0); // 传输完成,处理数据 processLargeData(largeRxBuffer, sizeof(largeRxBuffer)); } }
6.2.6 Flash存储器操作示例
// Flash存储器操作示例 #define FLASH_CMD_READ_ID 0x9F #define FLASH_CMD_READ_DATA 0x03 #define FLASH_CMD_WRITE_ENABLE 0x06 #define FLASH_CMD_PAGE_PROGRAM 0x02 #define FLASH_CMD_READ_STATUS 0x05 uint8_t flash_read_id(void) { uint8_t cmd = FLASH_CMD_READ_ID; uint8_t id[3]; // 发送读ID命令 SPI_DRV_MasterTransferBlocking(0, &cmd, NULL, 1, 1000); // 读取ID SPI_DRV_MasterTransferBlocking(0, NULL, id, sizeof(id), 1000); return id[0]; // 返回制造商ID } void flash_read_data(uint32_t address, uint8_t *data, uint16_t length) { uint8_t cmd[4]; // 构造读命令 cmd[0] = FLASH_CMD_READ_DATA; cmd[1] = (address >> 16) & 0xFF; cmd[2] = (address >> 8) & 0xFF; cmd[3] = address & 0xFF; // 发送读命令和地址 SPI_DRV_MasterTransferBlocking(0, cmd, NULL, sizeof(cmd), 1000); // 读取数据 SPI_DRV_MasterTransferBlocking(0, NULL, data, length, 1000); } void flash_write_page(uint32_t address, const uint8_t *data, uint16_t length) { uint8_t cmd; uint8_t writeCmd[4]; // 写使能 cmd = FLASH_CMD_WRITE_ENABLE; SPI_DRV_MasterTransferBlocking(0, &cmd, NULL, 1, 1000); // 构造页编程命令 writeCmd[0] = FLASH_CMD_PAGE_PROGRAM; writeCmd[1] = (address >> 16) & 0xFF; writeCmd[2] = (address >> 8) & 0xFF; writeCmd[3] = address & 0xFF; // 发送页编程命令和地址 SPI_DRV_MasterTransferBlocking(0, writeCmd, NULL, sizeof(writeCmd), 1000); // 写入数据 SPI_DRV_MasterTransferBlocking(0, data, NULL, length, 1000); // 等待写入完成 uint8_t status; do { cmd = FLASH_CMD_READ_STATUS; SPI_DRV_MasterTransferBlocking(0, &cmd, NULL, 1, 1000); SPI_DRV_MasterTransferBlocking(0, NULL, &status, 1, 1000); } while (status & 0x01); // 等待忙标志清除 }
6.2.7 多设备管理示例
// 多设备SPI管理 typedef struct { spi_which_pcs_t pcs; uint32_t baudRate; spi_clock_phase_t phase; spi_sck_polarity_t polarity; } spi_device_config_t; // 设备配置表 spi_device_config_t devices[] = { {SPI_PCS0, 1000000, SPI_CLOCK_PHASE_1ST_EDGE, SPI_SCK_ACTIVE_HIGH}, // Flash {SPI_PCS1, 500000, SPI_CLOCK_PHASE_2ND_EDGE, SPI_SCK_ACTIVE_LOW}, // LCD {SPI_PCS2, 2000000, SPI_CLOCK_PHASE_1ST_EDGE, SPI_SCK_ACTIVE_HIGH}, // ADC }; void spi_select_device(uint8_t deviceIndex) { if (deviceIndex >= sizeof(devices) / sizeof(devices[0])) { return; } spi_master_config_t spiConfig; SPI_DRV_MasterGetDefaultConfig(&spiConfig); // 配置设备特定参数 spiConfig.whichPcs = devices[deviceIndex].pcs; spiConfig.bitsPerSec = devices[deviceIndex].baudRate; spiConfig.clkPhase = devices[deviceIndex].phase; spiConfig.clkPolarity = devices[deviceIndex].polarity; // 重新配置总线 SPI_DRV_MasterConfigureBus(0, &spiConfig, NULL); } void multi_device_example(void) { // 与Flash通信 spi_select_device(0); uint8_t flashId = flash_read_id(); // 与LCD通信 spi_select_device(1); lcd_send_command(0x01); // 清屏命令 // 与ADC通信 spi_select_device(2); uint16_t adcValue = adc_read_channel(0); }
7. 与数据手册的对应关系
根据YTM32B1ME0数据手册中的SPI章节:
-
时钟配置: SDK中的波特率和时钟极性/相位直接对应硬件寄存器
-
片选控制: PCS信号配置对应硬件的片选控制逻辑
-
FIFO管理: 发送和接收FIFO的深度和水位配置
-
DMA支持: DMA请求信号与DMA控制器的请求源匹配
-
中断系统: 传输完成、FIFO状态等中断与硬件中断源对应
8. 最佳实践建议
-
时钟配置: 根据从设备规格选择合适的时钟频率和时序
-
片选管理: 合理管理多设备的片选信号,避免冲突
-
数据对齐: 确保数据缓冲区按照帧大小正确对齐
-
错误处理: 实现完善的传输错误检测和恢复机制
-
DMA优化: 大数据量传输时使用DMA提高效率
-
功耗考虑: 不使用时及时关闭SPI模块以降低功耗
9. 调试技巧
-
信号分析: 使用逻辑分析仪检查SPI时序和数据
-
波特率验证: 确认实际波特率与配置值一致
-
片选时序: 检查片选信号的建立和保持时间
-
FIFO状态: 监控FIFO的使用情况,避免溢出
-
中断调试: 验证中断服务程序正确执行
10. 总结
YTM32B1M的SPI控制器提供了灵活而强大的串行通信功能,支持多种传输模式和丰富的配置选项。通过合理配置和使用SPI驱动,可以实现与各种外设的高速可靠通信,满足不同应用场景的需求。掌握SPI的配置和使用方法对于开发需要外设扩展的嵌入式应用至关重要。
本文档基于YTM32B1M SDK v1.0,如有更新请参考最新版本文档。