SPI 时序分析 SPI Flash 读写

SPI 时序分析:主从模式与时钟特性(CPOL/CPHA)


一、SPI 主从模式基础


SPI(Serial Peripheral Interface)是一种全双工同步串行通信协议,支持 1 主多从 架构。
1. 主模式配置(Host as Master)
o 生成时钟信号(SCK),控制数据传输速率。
o 通过 MOSI 发送数据,通过 MISO 接收数据。
o 管理从机片选信号(SS/CS,低电平有效)。


2. 从模式配置(Device as Slave)
• 核心职责:
o 响应主机时钟信号,在时钟边沿采样 / 发送数据。
o 仅当片选信号(SS/CS)有效时,参与数据传输。
• 关键特性:
o 从机无法主动发起传输,必须由主机通过 SS/CS 激活。
o 从机时钟频率由主机决定,需支持主机的最大时钟速率。


二、时钟极性(CPOL)与相位(CPHA)


SPI 定义了 4 种时序模式,通过 CPOL(Clock Polarity) 和 CPHA(Clock Phase) 配置,决定时钟空闲状态和数据采样时机。


1. 时钟极性(CPOL,SPI_CR1_CPOL)
• 定义:时钟信号在空闲状态(无传输时)的电平。
o CPOL = 0:空闲时 SCK 为 低电平(默认)。
o CPOL = 1:空闲时 SCK 为 高电平。


2. 时钟相位(CPHA,SPI_CR1_CPHA)
• 定义:数据在时钟的 第几个边沿 被采样。
o CPHA = 0:数据在 第一个时钟边沿(上升沿或下降沿,取决于 CPOL)采样,第二个边沿移位。
o CPHA = 1:数据在 第二个时钟边沿 采样,第一个边沿移位。


三、时序分析与配置步骤


主机配置示例(STM32 HAL)


// 配置 SPI 为模式 0(CPOL=0,CPHA=0)
SPI_HandleTypeDef hspi;
hspi.Instance = SPI1;
hspi.Init.Mode = SPI_MODE_MASTER;
hspi.Init.CLKPolarity = SPI_POLARITY_LOW;    // CPOL=0
hspi.Init.CLKPhase = SPI_PHASE_1EDGE;       // CPHA=0(第一个边沿采样)
hspi.Init.NSS = SPI_NSS_SOFT;               // 软件片选
HAL_SPI_Init(&hspi);

// 片选使能(拉低 SS 引脚)
HAL_GPIO_WritePin(CS_GPIO_PORT, CS_PIN, GPIO_PIN_RESET);

// 发送数据(主发从收)
uint8_t tx_data = 0xAA;
uint8_t rx_data;
HAL_SPI_TransmitReceive(&hspi, &tx_data, &rx_data, 1, 1000);

// 片选禁用(拉高 SS 引脚)
HAL_GPIO_WritePin(CS_GPIO_PORT, CS_PIN, GPIO_PIN_SET);


从机时序适配(以自定义从机为例)
时钟同步:从机需在主机时钟边沿(根据 CPHA/CPOL)锁存数据


// Verilog 从机时序逻辑(模式 0)
always @(posedge sck) begin  // CPOL=0,上升沿采样(CPHA=0)
    if (cs_n == 0) begin      // 片选有效
        data_in <= mosi;      // 主机发送数据,从机接收
        miso <= data_out;     // 从机发送数据,主机接收
    end
end

项目 3:SPI Flash 读写


- 实现页编程与擦除操作
- 验证数据完整性(CRC 校验)


1. 硬件无关性设计
• 通过spi_flash_dev_t结构体配置 SPI 参数(时钟频率、CPOL/CPHA),支持不同硬件平台
• 依赖底层spi_transfer_byte()函数实现具体 SPI 通信(需根据实际平台如 STM32/ESP32 实现)


2. 安全机制
• 地址对齐检查:页编程要求地址按页对齐(address % PAGE_SIZE == 0)
• 操作超时处理:spi_flash_wait_ready()包含超时机制,避免死锁
• 写使能强制:所有写操作(编程 / 擦除)前必须调用写使能

// 写入流程(带CRC校验)
bool flash_write_with_crc(uint32_t address, const uint8_t *data, uint32_t len) {
    // 1. 计算原始数据CRC
    uint32_t crc = crc32_calculate(data, len);
    
    // 2. 分页编程数据
    for (uint32_t i = 0; i < len; i += SPI_FLASH_PAGE_SIZE) {
        uint32_t page_addr = address + i;
        uint16_t page_len = MIN(len - i, SPI_FLASH_PAGE_SIZE);
        if (!spi_flash_page_program(page_addr, data + i, page_len)) {
            return false;
        }
    }
    
    // 3. 单独存储CRC值(建议存放在数据块末尾或专用区域)
    uint8_t crc_bytes[4] = {
        (crc >> 24) & 0xFF,
        (crc >> 16) & 0xFF,
        (crc >> 8) & 0xFF,
        crc & 0xFF
    };
    return spi_flash_page_program(address + len, crc_bytes, 4);
}

// 读取校验流程
bool flash_verify_with_crc(uint32_t address, const uint8_t *expected_data, uint32_t len) {
    uint8_t read_data[len + 4];  // 包含数据和CRC
    if (!spi_flash_read_data(address, read_data, len + 4)) {
        return false;
    }
    
    // 分离数据和存储的CRC
    uint32_t stored_crc = ((uint32_t)read_data[len] << 24) |
                          ((uint32_t)read_data[len+1] << 16) |
                          ((uint32_t)read_data[len+2] << 8) |
                          read_data[len+3];
    
    // 计算读取数据的CRC
    uint32_t current_crc = crc32_calculate(read_data, len);
    return (stored_crc == current_crc);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值