YTM32B1M SDK解析 - SPI串行外设接口

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的主要应用场景

  1. 存储器接口: Flash、EEPROM、SD卡等存储设备通信

  2. 显示器驱动: LCD、OLED等显示设备控制

  3. 传感器接口: 数字传感器数据读取

  4. ADC/DAC: 外部模数/数模转换器通信

  5. 无线模块: WiFi、蓝牙等无线通信模块接口

  6. 实时时钟: 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. 最佳实践建议

  1. 时钟配置: 根据从设备规格选择合适的时钟频率和时序

  2. 片选管理: 合理管理多设备的片选信号,避免冲突

  3. 数据对齐: 确保数据缓冲区按照帧大小正确对齐

  4. 错误处理: 实现完善的传输错误检测和恢复机制

  5. DMA优化: 大数据量传输时使用DMA提高效率

  6. 功耗考虑: 不使用时及时关闭SPI模块以降低功耗

9. 调试技巧

  1. 信号分析: 使用逻辑分析仪检查SPI时序和数据

  2. 波特率验证: 确认实际波特率与配置值一致

  3. 片选时序: 检查片选信号的建立和保持时间

  4. FIFO状态: 监控FIFO的使用情况,避免溢出

  5. 中断调试: 验证中断服务程序正确执行

10. 总结

YTM32B1M的SPI控制器提供了灵活而强大的串行通信功能,支持多种传输模式和丰富的配置选项。通过合理配置和使用SPI驱动,可以实现与各种外设的高速可靠通信,满足不同应用场景的需求。掌握SPI的配置和使用方法对于开发需要外设扩展的嵌入式应用至关重要。


本文档基于YTM32B1M SDK v1.0,如有更新请参考最新版本文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值