DSP_28002x的SPI代码注释_文1

笔记整理于deepseek.

注意

###############################

第 5 行.SPICTL.bit.CLK_PHASE时钟相位

复制代码
// 原来程序用的:
//时钟相位:CPHA=0,配合 CPOL=1 组成 SPI 模式 2
// 数据在下降沿输出,在上升沿采样
SpiaRegs.SPICTL.bit.CLK_PHASE = 0;     // CLK_PHASE=0:下降沿输出,上升沿采样
复制代码
// 待改为正确的:
// 时钟相位:CPHA=1,配合 CPOL=1 组成 SPI 模式 3
// 数据在下降沿输出,在上升沿采样(FM25CL64B 官方支持的模式)
SpiaRegs.SPICTL.bit.CLK_PHASE = 1;     // CLK_PHASE=1:下降沿输出,上升沿采样

##############################

一、器件背景

项目 内容
主控芯片 TMS320F28002x(C2000系列DSP)
铁电存储器 FM25CL64(Cypress,64Kbit = 8KByte,SPI接口)
SPI外设 SPI-A(SpiaRegs)
片选信号 GPIO11(低有效)
SPI时钟 LSPCLK / (SPIBRR+1)

FM25CL64 专属要点

要点 内容
容量 64Kbit = 8K×8,地址范围 0x0000~0x1FFF
地址位数 13 位(A12~A0) ,需要 2 个地址字节(高 8 位在前,低 8 位在后)
命令字节 纯命令,不嵌地址位:WRITE=0x02,READ=0x03,WREN=0x06,WRDI=0x04,RDSR=0x05,WRSR=0x01
写操作序列 WREN → CS拉低 → WRITE(0x02) → 地址高字节 → 地址低字节 → 数据 → CS拉高
读操作序列 CS拉低 → READ(0x03) → 地址高字节 → 地址低字节 → 读数据 → CS拉高
地址自增 连续读写时,芯片内部地址指针自动+1,不用每次重发地址
支持模式 SPI Mode 0(CPOL=0, CPHA=0)和 Mode 3(CPOL=1, CPHA=1)

三、FM25CL64B 关键参数速查(补充)

项目 值 备注
容量 64Kbit = 8KByte
地址范围 0x0000 ~ 0x1FFF(13 位地址)
最高 SPI 时钟 20MHz
本工程 SPI 时钟 6.25MHz 在《SP_28002x的SPI代码注释_文0》里有详解
支持模式 Mode 0 (0,0)、Mode 3 (1,1)
不支持模式 Mode 1 (0,1)、Mode 2 (1,0)
指令长度 8 位
数据宽度 8 位字节,按字节读写

四、FM25CL64B 指令速查(补充)

指令 编码 作用 需要 WREN
WREN 0x06 写使能,置 WEL=1 ---
WRDI 0x04 写禁止,清 WEL=0 ---
RDSR 0x05 读状态寄存器 否
WRSR 0x01 写状态寄存器 是
READ 0x03 读数据 否
WRITE 0x02 写数据 是

宏定义与全局变量

cpp 复制代码
#define WREN_INST    0x0600    // 写使能指令 (Write Enable)
#define WRDI_INST    0x0400    // 写禁止指令 (Write Disable)
#define RDSR_INST    0x0500    // 读状态寄存器 (Read Status Register)
#define WRSR_INST    0x0100    // 写状态寄存器 (Write Status Register)
#define READ_INST    0x0300    // 读数据指令 (Read)
#define WRITE_INST   0x0200    // 写数据指令 (Write)
#define STATUS_REG   0x0000    // 状态寄存器写值
#define XP_ADDR      0x0010    // 参数区起始地址
#define XP_NUM       210       // 参数个数
#define SET_ADDR     43        // 参数在数组中的起始下标

Uint16 spixdat;        // SPI发送数据缓存
int32  spixdatInt32;   // 32位数据缓存(本函数未用)
Uint32 spixdatUint32;  // 32位数据缓存(本函数未用)
Uint16 spiimg;         // SPI接收数据缓存(从SPIRXBUF读出)
Uint16 spirec;         // 读回校验值
Uint16 spiercun;       // 读回错误计数
Uint16 wdata;          // 写数据缓存
Uint16 xaddress;       // 地址缓存
Uint16 xstut;          // 状态缓存
Uint16 sdata;          // 发送数据缓存
Uint16 rdata;          // 接收数据缓存

关键点:FM25CL64 的指令是 8位 ,但这里定义成 0x0600、0x0300 等16位值,原因是 F28002x 的 SPI 配置为 8位字符长度 ,发送时只取低8位有效(高8位被忽略或作为第二个字节发送)。这是"把两个字节拼在一个16位字里"的简化写法,实际每个 spisend() 发一个16位字,SPI硬件按8位字符长度发送,会先发高8位再发低8位------这一点非常关键。

二、函数InitSpi()

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

2.1、骨架

第一层:一句话主线

关 SPI → 配 SPI → 开 SPI → 操作 FRAM。

一关二配三开四用。

配里九项:回环极性位长相位,主从发送中断波特率,再加仿真。

再展开一点:

关 SPI(复位)→ 配 SPI(回环/极性/位长/相位/主从/发送/中断/波特率/仿真)→ 开 SPI(退复位)→ 操作 FRAM(WREN → WRSR → 读参数)。

这一层是骨架。

第二层:分组 + 每组一句话

组 行 一句话
A 关 1 清 SPISWRESET,进入复位,才能改配置
B 配 2~10 配 SPI 怎么工作:不回环、空闲高、8 位、下降沿输出上升沿采样、主机、能发、不中断、6.25MHz、调试不停
C 开 11 置 SPISWRESET,退出复位,SPI 开始工作
D 用 12~14 对 FRAM:先 WREN,再 WRSR,再批量读参数

这一层是"目录",看到某行能归到哪组。

配 SPI(9 项)

  • 第 2 行:不回环,因为要接外部 FRAM。

    第 3 行:空闲高,CPOL = 1。

    第 4 行:8 位,SPICHAR = 0x7,实际位数 = 值 + 1。

    第 5 行 :相位 1,CPHA = 1,配合 CPOL = 1 是模式 3(FM25CL64 支持模式 0 和模式 3)。

    第 6 行:主机,主动产生时钟和片选。

    第 7 行:能发,TALK = 1。

    第 8 行:不中断,查询方式。

    第 9 行:6.25MHz,SPIBRR = 3,公式 LSPCLK/(SPIBRR+1)。

    第 10 行:调试不停,FREE = 1。

再记两个关键组合:

  • 模式 3 = CPOL 1 + CPHA 1 → 下降沿输出,下降沿采样。(FM25CL64 支持模式 0 和模式 3,这里选模式 3)

    SPICHAR 规则:实际位数 = 值 + 1,0x7 就是 8 位。

第三层:每行的"三件套"

每行只记三件事,不要更多:

  1. 写什么值

  2. 为什么写这个值

  3. 写错会怎样

序 行 寄存器/位 值 一句话 写错会怎样
1 2 SPICCR.SPILBK 0 不回环,要接外部 FRAM 写 1 内部短接,FRAM 收不到
2 3 SPICCR.CLKPOLARITY 1 空闲高,CPOL=1 极性反了,FRAM 采样错
3 4 SPICCR.SPICHAR 0x7 8 位,实际位数=值+1 写 0x0 只发 1 位,命令全错
4 5 SPICTL.CLK_PHASE 1 相位 1,CPHA=1,模式 3 相位反了,数据错位
5 6 SPICTL.MASTER_SLAVE 1 主机,产生时钟和片选 写 0 变从机,没时钟
6 7 SPICTL.TALK 1 能发,SPISOMI 接通 写 0 高阻,发不出去
7 8 SPICTL.SPIINTENA 0 不中断,用查询 写 1 没 ISR,反复进中断
8 9 SPIBRR.all 3 6.25MHz,LSPCLK/(3+1) 写 0/1/2 都是 LSPCLK/4
9 10 SPIPRI.FREE 1 调试不停,断点也跑 写 0 断点打断 SPI 时序

操作 FRAM 三步(顺序不能反)

序 行 调用 一句话 为什么
1 12 wren_cmd() WREN 写使能 FRAM 写前必须写使能
2 13 wrsr_cmd() WRSR 写状态寄存器 清写保护,允许后续读写
3 14 sread_FRAM() 批量读参数到 RAM 上电把 FRAM 参数加载到 paramet\[\]

顺序口诀:先使能,再状态,再读参。

四、怎么验证自己记住了

合上笔记,回答这 6 个问题:

  1. InitSpi 分哪四步?
  2. 配 SPI 的 9 项口诀是什么?
  3. SPICHAR = 0x7 是几位?写错会怎样?
  4. CPOL=1、CPHA=1 是什么模式?谁决定选它?
  5. FM25CL64 的地址是多少位?发几个地址字节?
  6. 为什么最后要 SPISWRESET = 1?

参考答案:

  1. 关 → 配 → 开 → 用。
  2. 回环极性位长相位,主从发送中断波特率,再加仿真。
  3. 8 位;写 0x0 只发 1 位,命令全错。
  4. 模式 3 ;由 FM25CL64 数据手册决定(支持模式 0 和模式 3)。
  5. 13 位地址 ,发 2 个地址字节(高 8 位在前,低 8 位在后),命令字节是纯的 0x02/0x03,不嵌地址位。
  6. 不退复位 SPI 不工作,spisend 会卡在等 INT_FLAG。

能答出来,就是真记住了。答不出来,说明还在"字典层",没到"骨架层"。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

一句话总结:

InitSpi() 先清 SPISWRESET 进入复位,再配置禁止回环、CPOL=1、8 位字符、CPHA=0、主机模式、发送使能、禁止中断、SPIBRR=3 得 6.25MHz、FREE=1,最后置 SPISWRESET=1 退出复位;随后对 FRAM 执行写使能、写状态寄存器、批量读取参数。核心点是 复位-配置-退复位顺序、SPICHAR 位数换算、SPI 模式 2、SPIBRR 公式、查询式收发、FRAM 命令时序。

2.2、整段注释

一句话总结

InitSpi() 先清 SPISWRESET 进入复位,再配置禁止回环、CPOL=1、8 位字符、CPHA=1(模式 3) 、主机模式、发送使能、禁止中断、SPIBRR=3 得 6.25MHz、FREE=1,最后置 SPISWRESET=1 退出复位;随后对 FM25CL64 执行写使能、写状态寄存器、批量读取参数。核心点是复位-配置-退复位顺序、SPICHAR 位数换算、SPI 模式 3、SPIBRR 公式、查询式收发、FM25CL64 的 13 位地址 + 2 字节地址格式 + 纯命令码(不嵌地址位)。

cpp 复制代码
/*******************************************************************************
 * 函数名   : InitSpi()
 * 简述       : SPI-A 初始化 + 铁电上电初始化 + 参数加载
 *
 * 功能       : 初始化 SPI-A 外设并对 FRAM 执行上电初始化,具体分 4 步:
 *            1. 配置 F28002x 的 SPI-A 外设为主机模式、8 位字符、CPOL=?、
 *               CPHA=?、波特率 6.25MHz、禁止中断、自由运行;
 *            2. 发送 WREN(0x06) 写使能指令,让铁电允许写;
 *            3. 写状态寄存器为 0x00,解除 FM25CL64 的块保护;
 *            4. 从铁电批量读参数到 paramet[] 数组。
 *
 * 参数         : 无
 * 返回值     : 无
 * 调用关系 : 上层初始化流程调用:在 main() 里 InitGpio()、InitPieCtrl() 之后调用,
 *            必须在 InitSci() 之前,因为 SCI 参数要从铁电加载;
 *            内部调用 wren_cmd()、wrsr_cmd()、sread_FRAM()。
 *
 * 公式计算 : 主机模式,8 位字符长度
 *            SPICLK = LSPCLK / (SPIBRR+1) = 25MHz / 4 = 6.25MHz
 *            CLKPOLARITY = 1, CLK_PHASE = 0
 *            查询方式收发,不使用 SPI 中断
 *
 * 片选     : GPIO11 手动控制,低有效(GPACLEAR 拉低,GPASET 拉高)
 *
 * 注意事项 : 1. 配置前必须先 SPISWRESET=0 进入复位,配完再置 1 退出复位;
 *            2. 配置完成后必须置 SPISWRESET = 1 才能收发;
 *            3. 片选 GPIO11 方向需在 GPIO 初始化中配置为输出;
 *            4. FRAM 的 WREN/WRSR/READ 时序依赖 delay(),不能省。
 ******************************************************************************/
void InitSpi(void)
{
    // 进入复位状态:配置 SPI 前必须先清 SPISWRESET,保证配置位可写
    SpiaRegs.SPICCR.bit.SPISWRESET = 0;    // SPISWRESET=0:SPI 进入复位状态

    // 禁止回环:正常外部收发,TX 不内部短接到 RX
    SpiaRegs.SPICCR.bit.SPILBK = 0;        // SPILBK=0:禁止 SPI 回环模式

    // 时钟极性:SPICLK 空闲时为高电平
    SpiaRegs.SPICCR.bit.CLKPOLARITY = 1;   // CLKPOLARITY=1:空闲高电平

    // 字符长度:SPICHAR=0x7,实际位数 = 0x7 + 1 = 8 位
    SpiaRegs.SPICCR.bit.SPICHAR = 0x7;     // SPICHAR=0x7:每次收发 8 位

    // 时钟相位:CPHA=0,配合 CPOL=1 组成 SPI 模式 2
    // 数据在下降沿输出,在上升沿采样
    SpiaRegs.SPICTL.bit.CLK_PHASE = 0;     // CLK_PHASE=0:下降沿输出,上升沿采样

    // 主机模式:SPI 作为主机,主动产生 SPICLK 和片选
    SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1;  // MASTER_SLAVE=1:SPI 配置为主机

    // 发送使能:允许发送,SPISOMI 接通
    SpiaRegs.SPICTL.bit.TALK = 1;          // TALK=1:使能发送

    // 禁止 SPI 中断:当前驱动使用查询方式,不用中断
    SpiaRegs.SPICTL.bit.SPIINTENA = 0;     // SPIINTENA=0:禁止 SPI 发送/接收中断

    // 波特率分频:SPIBRR=3,SPI_CLK = LSPCLK/(SPIBRR+1) = 25MHz/4 = 6.25MHz
    SpiaRegs.SPIBRR.all = 0x03;            // SPIBRR=3:25MHz/(3+1)=6.25MHz

    // 自由运行:仿真挂起时 SPI 继续运行,避免调试断点打断时序
    SpiaRegs.SPIPRI.bit.FREE = 1;          // FREE=1:仿真挂起时 SPI 继续运行

    // 退出复位:配置完成,SPI 准备接收或发送
    SpiaRegs.SPICCR.bit.SPISWRESET = 1;    // SPISWRESET=1:SPI 退出复位,开始工作

    // FRAM 初始化:写使能 → 写状态寄存器 → 批量读取参数
    wren_cmd();                            // 发送 WREN:FRAM 写使能
    wrsr_cmd();                            // 发送 WRSR:写 FRAM 状态寄存器
    sread_FRAM();                          // 批量读取 FRAM 参数到 paramet[]
}

第 1 行:.SPICCR.bit.SPISWRESET

复制代码
// 进入复位状态:配置 SPI 前必须先清 SPISWRESET,保证配置位可写
SpiaRegs.SPICCR.bit.SPISWRESET = 0;    // SPISWRESET=0:SPI 进入复位状态

位含义

位 名称 值 含义
bit7 SPISWRESET 0 SPI软件复位。 0=复位状态,所有SPI逻辑被强制到已知状态; 1=正常工作。SPI 退出复位,准备接收/发送,配置SPI前必须先写0。

为什么先写 0

TI SPI 要求:修改 SPI 配置前,先把 SPISWRESET 清 0,否则某些位可能不生效或行为异常。

Q:SPISWRESET 置 0 后,SPI 还能收发吗?

A:不能。此时 SPI 处于复位状态,必须配置完成后置 1 才能收发。

Q:SPISWRESET 和 SPICCR 其他位谁先配?

A:先清 SPISWRESET,再配其他位,最后置 SPISWRESET = 1。


第 2 行:.SPILBK回环模式位

复制代码
// 禁止回环:正常外部收发,TX 不内部短接到 RX
SpiaRegs.SPICCR.bit.SPILBK = 0;        // SPILBK=0:禁止 SPI 回环模式

位含义

  • SPICCR:SPI 配置控制寄存器。

  • SPILBK:SPI 回环模式位。

    • 0:禁止回环,正常外部收发。

    • 1:回环,TX 内部连到 RX,用于自测。

位 名称 值 含义
bit6 SPILBK 0 SPI 回环模式。 0=禁止,SIMO 和 SOMI 走外部引脚,正常接 FM25CL64; 1=使能,SIMO 内部短接到 SOMI,用于自测。

为什么写 0

本工程要接外部 FM25CL64 铁电,数据必须走 SIMO/SOMI 引脚。写 1 会把 TX 内部短接到 RX,FM25CL64 收不到任何数据。

Q&A

  • Q:回环模式有什么用?

    A:调试 SPI 外设本身时,用来验证"发出去的数据能不能收回来",不用外部接线。本工程用不到。

  • Q:写错会怎样?

    A:写 1 时,SPI 发出去的数据立刻从内部回到接收缓冲,spiimg 会等于 spixdat,但 FM25CL64 完全收不到,读写铁电全失败。


第 3 行.CLKPOLARITY空闲电平极性// 时钟极性:SPICLK 空闲时为高电平

复制代码
SpiaRegs.SPICCR.bit.CLKPOLARITY = 1;   // CLKPOLARITY=1:空闲高电平

位含义

位 名称 值 含义
bit5 CLKPOLARITY 1 时钟极性(CPOL)。 0=SPICLK 空闲为低; 1=SPICLK 空闲为高。

为什么写 1

配合 CLK_PHASE=1 组成 SPI Mode 3 (CPOL=1, CPHA=1):空闲高电平,下降沿切换数据,上升沿采样数据。FM25CL64B 数据手册明确支持 Mode 0 和 Mode 3,Mode 3 是官方推荐之一。

Q&A

  • Q:CPOL=1 和 CPHA=1 组合是什么模式?

    A:SPI Mode 3。对应"空闲高、下降沿换数据、上升沿采样"。

  • Q:FM25CL64B 支持哪些模式?

    A:官方数据手册明确写 Mode 0 (CPOL=0, CPHA=0) 和 Mode 3 (CPOL=1, CPHA=1) 两种。两者共同点:下降沿换数据,上升沿采样。

  • Q:写错会怎样?

    A:极性反了,FM25CL64B 在错误的边沿采样,读到的数据全错。

Q:SPI 四种模式怎么对应 CPOL/CPHA?


第 4 行.SPICHAR字符长度选择

复制代码
// 字符长度:SPICHAR=0x7,实际位数 = 0x7 + 1 = 8 位
SpiaRegs.SPICCR.bit.SPICHAR = 0x7;     // SPICHAR=0x7:每次收发 8 位

位含义

位 名称 值 含义
bit3:0 SPICHAR 0x7 字符长度选择,4 位。 编码规则:实际位数 = 值 + 1。 0x0=1 位,0x1=2 位,...,0x7=8 位,0xF=16 位。

为什么写 0x7

FM25CL64B 的指令、地址、数据都是按 8 位字节传输,所以 SPI 必须配 8 位。0x7 + 1 = 8,正确。

Q&A

  • Q:SPICHAR=0x7 是几位?

    A:8 位。因为实际位数 = 值 + 1。

  • Q:写 0x0 会怎样?

    A:只发 1 位,FM25CL64B 收到的指令全错,读写全失败。

  • Q:写 0xF 会怎样?

    A:发 16 位,与 FM25CL64B 的 8 位时序不匹配,命令错位。


第 5 行.SPICTL.bit.CLK_PHASE时钟相位

复制代码
// 原来程序用的:
//时钟相位:CPHA=0,配合 CPOL=1 组成 SPI 模式 2
// 数据在下降沿输出,在上升沿采样
SpiaRegs.SPICTL.bit.CLK_PHASE = 0;     // CLK_PHASE=0:下降沿输出,上升沿采样
复制代码
// 待改为正确的:
// 时钟相位:CPHA=1,配合 CPOL=1 组成 SPI 模式 3
// 数据在下降沿输出,在上升沿采样(FM25CL64B 官方支持的模式)
SpiaRegs.SPICTL.bit.CLK_PHASE = 1;     // CLK_PHASE=1:下降沿输出,上升沿采样
位 名称 值 含义
bit2 CLK_PHASE 1 时钟相位(CPHA)。 0=Mode 0/2,数据在第一个边沿输出; 1=Mode 1/3,数据在第二个边沿输出。具体哪个边沿取决于 CLKPOLARITY。

为什么写 1(而不是原来的 0)

FM25CL64B 数据手册明确要求:

"The FM25CL64B operates in SPI Mode 0 and Mode 3 . In both modes, data changes on the falling edge and is stable on the rising edge."

代码用 CPOL=1,要匹配 FM25CL64B,必须再配 CPHA=1 → Mode 3 。原来的 CLK_PHASE=0 配出的是 Mode 2 (CPOL=1, CPHA=0),不在 FM25CL64B 官方支持列表里。虽然 6.25MHz 低时钟下可能"能跑",但属于在规格边缘工作,存在隐患。

Q&A

  • Q:Mode 2 和 Mode 3 有什么区别?

    A:两者空闲都是高电平(CPOL=1),区别在 CPHA。Mode 2 在片选拉低后第一个下降沿才开始驱动数据;Mode 3 在第一个下降沿之前数据就已准备好。FM25CL64B 要求"第一个上升沿就要采到数据",Mode 3 更稳妥。

  • Q:为什么原来写 0 也能跑?

    A:6.25MHz 时钟周期 160ns,第一个下降沿到第一个上升沿约 80ns,数据建立时间勉强够。但温度、电压、批次变化时可能偶发失败。

  • Q:改成 1 后需要改别的吗?

    A:不需要。只改这一位即可。CPOL=1 + CPHA=1 = Mode 3,与 FM25CL64B 完全匹配。


第 6 行.MASTER_SLAVE

复制代码
// 主机模式:SPI 作为主机,主动产生 SPICLK 和片选
SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1;  // MASTER_SLAVE=1:SPI 配置为主机
位 名称 值 含义
bit1 MASTER_SLAVE 1 主从选择。0=从机 ,接收外部 SPICLK;1=主机,主动产生 SPICLK。

为什么写 1

F28002x 要主动给 FM25CL64B 提供时钟,所以必须做主机。片选 GPIO11 也是主机控制的。

Q&A

  • Q:主机和从机的区别?

    A:主机产生 SPICLK 和片选;从机接收外部 SPICLK,片选由主机给。

  • Q:写错会怎样?

    A:写 0 变从机,SPI 不产生时钟,FM25CL64B 收不到任何时钟,spisend 会卡在等 INT_FLAG。


第 7 行.TALK ?

复制代码
// 发送使能:允许发送,SPISOMI 接通
SpiaRegs.SPICTL.bit.TALK = 1;          // TALK=1:使能发送

TALK 是 SPI 发送的"静音键":TALK=1 正常发,TALK=0 输出高阻不发但能收。4线全双工永远置1,3线半双工收发切换时要动态开关。

TALK 值 效果
1(使能) 允许发送,SPISOMI 接通。数据输出引脚正常驱动,SPI 可以发送数据
0(禁止) 禁止发送,SPISOMI 高阻。数据输出引脚置于高阻态(Hi-Z),不发送任何数据,但仍可接收数据、更新状态标志

TALK = 发送使能开关,控制 SPI 数据输出引脚(MOSI/MISO)是否驱动总线。

关键特性

  • 不影响接收:TALK=0 时,SPI 仍然能接收从机数据、更新 RX 标志位,时钟和片选信号也照常工作。
  • 不影响 FIFO/移位器:如果禁用 TALK 时 FIFO 里有数据,移位器仍会继续工作,只是输出引脚被"静音"。
  • 复位默认值:系统复位后 TALK 自动清零(禁止发送)。

典型应用场景

  • 3线 SPI(半双工):收发共用一根数据线,发送时 TALK=1,接收前必须 TALK=0,避免主从同时驱动总线冲突。
  • 多从机共享总线:防止未选中的从机干扰总线。
  • 纯接收模式:DSP 作为从机只收不发时,可关闭 TALK 避免 MISO 回传垃圾数据。

4线全双工场景(如 W25Q64)

始终 TALK=1 即可,因为 MOSI 和 MISO 是独立的两根线,不存在总线冲突问题。

为什么写 1

当前需要向 FRAM 发命令、地址、数据,必须使能发送。

Q:四线 SPI 的 SPISTE 作用?

A:从机片选。主机通过 SPISTE 选中从机。

Q&A

  • Q:TALK=0 时还能收吗?

    A:能收,接收通道不受 TALK 影响。但本工程既要发又要收,TALK 必须为 1。

  • Q:写错会怎样?

    A:写 0 时 SIMO 高阻,FM25CL64B 收不到指令,读写全失败。


第 8 行.SPIINTENA中断总使能开关

复制代码
// 禁止 SPI 中断:当前驱动使用查询方式,不用中断
SpiaRegs.SPICTL.bit.SPIINTENA = 0;     // SPIINTENA=0:禁止 SPI 发送/接收中断

SPIINTENA = SPI 中断总使能开关,控制 SPI 模块是否向 PIE 模块发起中断请求。

SPIINTENA 是 SPI 中断的"总闸门":=0 用查询方式轮询状态位,=1 用中断方式自动响应。C2000 中断需三级使能(外设→PIE→CPU)缺一不可,查询方式无需配置 PIE,代码更简单。

位 名称 值 含义
bit3 SPIINTENA 0 SPI 中断使能。 0=禁止发送/接收中断;SPI 事件(发送/接收完成)触发时,向 PIE 发送中断请求 1=使能 ,收发完成时产生中断。禁止 SPI 发送/接收中断,只能用查询方式检测 SPI 状态

为什么写 0

当前驱动用查询方式:

复制代码
while(SpiaRegs.SPISTS.bit.INT_FLAG==0);不用中断,所以关掉。

关键特性

  • 只控制"是否中断",不影响通信本身:SPIINTENA=0 时,SPI 收发功能完全正常,只是不会触发中断。
  • 复位默认值:系统复位后 SPIINTENA 自动清零(禁止中断)。
  • 需要三级使能才能生效 :C2000 的中断体系是三级架构,仅设置 SPIINTENA=1 还不够,必须同时满足:
    1. 外设级:SPIINTENA = 1(本位)
    2. PIE 级:PIEIER 对应组位 = 1 + PIEACK 应答
    3. CPU 级:IER 对应 INTx = 1 + 全局中断 EINT 开启
查询方式 vs 中断方式
对比项 查询方式(SPIINTENA=0) 中断方式(SPIINTENA=1)
实现复杂度 简单,无需配置 PIE/IER/EINT 复杂,需三级使能 + 编写 ISR
CPU 占用 高,主循环需轮询状态位 低,事件触发才响应
实时性 取决于轮询频率 高,硬件自动响应
适用场景 低速、简单通信(如 W25Q64 初始化) 高速、大数据量、实时性要求高
查询方式如何检测收发完成?

SPIINTENA=0 时,通过查询 SPISTS 状态寄存器 的 SPI INT FLAG 位(bit6)来判断:

复制代码
// 等待发送/接收完成
while (SpiaRegs.SPISTS.bit.SPI_INT_FLAG != 1);
// 读取接收数据
data = SpiaRegs.SPIRXBUF.all;

Q:查询和中断方式各自优缺点?

A:

  • 查询:简单,但 CPU 空转,实时性差。

  • 中断:CPU 利用率高,但需要 ISR,复杂度高。

Q:INT_FLAG 什么时候置 1?

A:一个字符收发完成后置 1,读 SPIRXBUF 可清标志。

  • Q:查询和中断哪个好?

    A:数据量小、实时性要求不高时,查询简单可靠;数据量大或要并行处理时,用中断。

  • Q:写错会怎样?

    A:写 1 但没写 ISR,每次收发完成都会反复进中断,可能死机。


第 9 行.SPIBRR波特率寄存器

复制代码
// 波特率分频:SPIBRR=3,SPI_CLK = LSPCLK/(SPIBRR+1) = 25MHz/4 = 6.25MHz
SpiaRegs.SPIBRR.all = 0x03;            // SPIBRR=3:25MHz/(3+1)=6.25MHz
位 名称 值 含义
bit6:0 SPIBRR 3 波特率分频。SPI_CLK = LSPCLK / (SPIBRR+1)。 特殊规则:SPIBRR=0→分频 2;=1→分频 4;=2→分频 8; ≥3→分频 = SPIBRR+1。

寄存器说明

  • SPIBRR:SPI 波特率寄存器,7 位(0~127),决定主模式下的 SPI 时钟频率。
  • LSPCLK:低速外设时钟,由 SYSCLK 经 LOSPCP 分频得到(默认 /4)。
波特率公式(两段式)
  • SPIBRR = 3 ~ 127 :SPI_CLK = LSPCLK / (SPIBRR + 1)
  • SPIBRR = 0 / 1 / 2 :SPI_CLK = LSPCLK / 4(最低分频固定为 4,无法更高)

示例解析

复制代码
SpiaRegs.SPIBRR.all = 0x03;  // SPIBRR = 3
  • LSPCLK = 25MHz,SPIBRR = 3 落入第一段公式
  • SPI_CLK = 25MHz / (3+1) = 6.25MHz

为什么写 3 而不是其他值?

LSPCLK = 25MHz(代码注释写 25M),SPIBRR=3 → 分频 4 → SPICLK = 6.25MHz。6.25MHz 对 FM25CL64B(最高支持 20MHz)来说安全,信号完整性好

  • SPIBRR=0/1/2 被"锁死"在 LSPCLK/4,无法通过减小 SPIBRR 获得更高频率。
  • 要获得更高 SPI 频率,只能提高 LSPCLK(修改 LOSPCP 寄存器降低分频比)。
  • SPIBRR=3 是第一段公式的最小值 ,对应 LSPCLK/4,恰好与第二段上限衔接,无跳变。

反向计算

已知目标 SPI_CLK 和 LSPCLK,求 SPIBRR:

复制代码
SPIBRR = (LSPCLK / SPI_CLK) - 1   // 结果需 ≥ 3

例:LSPCLK=25MHz,目标 SPI_CLK=1MHz → SPIBRR = 25/1 - 1 = 24

注意事项

  • 最大 SPI 频率 = LSPCLK / 4,这是硬件上限,任何 SPIBRR 都无法突破。
  • 从机模式下 SPIBRR 无效,时钟由主机提供。
  • 动态修改 SPIBRR 需先清除 SPISWRESET,改完再置位,防止配置过程中产生毛刺。
  • 与 STM32 对比:STM32 的 BR 位是 2/4/8/16... 固定分频档位;C2000 的 SPIBRR 是连续可编程分频(3~127),粒度更细。

Q:LSPCLK 是什么?

A:低速外设时钟,由系统时钟分频而来。

Q:SPIBRR = 0、1、2 时为什么都是 LSPCLK/4?

A:TI 规定低值有最小分频限制。

Q:6.25MHz 对 FRAM 是否合适?

A:要看 FRAM 最大 SPI 时钟。FM25CL64B 最高频率(20MHz),安全;若有的型号只支持 5MHz,就超了。

  • Q:SPIBRR=3 时分频是多少?

    A:4。因为 ≥3 时,分频 = SPIBRR+1 = 4。

  • Q:SPICLK 太快会怎样?

    A:超过 ,或线长导致信号完整性差,读写会出错。

####################################################################

"7位(0~127)"的含义

7位 指的是 SPIBRR 寄存器中真正参与波特率计算的位宽,即 bit6:0 这 7 个二进制位。

SPIBRR 是 7 位寄存器(bit6:0),取值 0~127,但 0/1/2 效果相同,所以实际有 126 种波特率可选。

为什么是 7 位?

SPIBRR 是一个 16 位寄存器,但只有低 7 位(bit6:0)是有效的波特率控制位,其余高位(bit15:7)是保留位,写无效、读返回 0。

复制代码
SPIBRR 寄存器位域:
bit15 ~ bit7:  保留位(读0,写无效)
bit6  ~ bit0:  SPI_BIT_RATE[6:0] ← 这7位决定波特率

为什么范围是 0~127?

7 位二进制能表示的最大值:

复制代码
bit6 bit5 bit4 bit3 bit2 bit1 bit0
 1    1    1    1    1    1    1  = 2⁷ - 1 = 127

所以 SPIBRR 的合法取值范围是 0 ~ 127,共 128 个值。

实际有效分频档位

虽然能写 0~127 共 128 个值,但由于 0/1/2 三个值都被"锁死"在 LSPCLK/4,所以实际可用的分频档位是:

SPIBRR 值 分频结果 有效档位数
0, 1, 2 LSPCLK / 4(三个值效果相同) 1 档
3 ~ 127 LSPCLK / (SPIBRR+1),即 /4 ~ /128 125 档
合计 --- 126 种有效波特率

第 10 行.SPIPRI.bit.FREE优先级/仿真控制寄存器

复制代码
// 自由运行:仿真挂起时 SPI 继续运行,避免调试断点打断时序
SpiaRegs.SPIPRI.bit.FREE = 1;          // FREE=1:仿真挂起时 SPI 继续运行
位 名称 值 含义
bit5 FREE 1 自由运行。 0=软挂起,仿真器暂停 CPU 时 SPI 停止; 1=自由运行,仿真器暂停 CPU 时 SPI 继续工作。
  • SPIPRI:SPI 优先级/仿真控制寄存器。

为什么写 1

调试时打断点,CPU 停了但 SPI 还在发数据。如果写 0,SPI 被冻结在中间状态,可能打断 FM25CL64B 的时序,导致铁电读写异常。写 1 让 SPI 不受断点影响。

  • Q:仿真挂起是什么?

    A:用 CCS 调试时,CPU 被仿真器暂停(断点、单步),但外设可以继续运行,取决于 FREE/SOFT 位。

  • Q:写错会怎样?

    A:写 0 时断点打断 SPI 时序,FM25CL64B 可能收到半个字节,读写失败。

Q:FREE 位实际调试中有什么用?

A:调试断点/挂起时,SPI 是否继续跑。

Q:SPIPRI 还有什么位?

A:还有 SOFT、SPIPRI 等,控制仿真行为和优先级。


第 11 行.SPISWRESET 软件复位位

复制代码
// 退出复位:配置完成,SPI 准备接收或发送
SpiaRegs.SPICCR.bit.SPISWRESET = 1;    // SPISWRESET=1:SPI 退出复位,开始工作
位 名称 值 含义
bit7 SPISWRESET 1 SPI 软件复位。 0=复位状态; 1=正常工作,SPI 退出复位,可以收发。

为什么写 1

前面 10 行都在配置,SPI 处于复位状态。配置完成后必须置 1 退出复位,SPI 才能开始工作。

Q&A

  • Q:不退复位会怎样?

    A:SPI 不工作,spisend() 会卡在 while(SPISTS.bit.INT_FLAG==0),死循环。

  • Q:为什么最后才置 1?

    A:配置过程中 SPI 不能工作,否则可能产生错误时钟或误发送。先配好,最后开。

Q:如果忘记置 1 会怎样?

A:SPI 一直处于复位状态,无法收发,spisend() 会卡在等待 INT_FLAG。

Q:SPISWRESET 置 1 后还能改配置吗?

A:一般要先清 0 再改,改完再置 1。

配置顺序口诀(11 行)

复制代码
先清复位(1),
再配九项:回环(2)极性(3)位长(4)相位(5),主从(6)发送(7)中断(8)波特率(9),再加仿真(10),
最后退复位(11)。

本次修正 :第 5 行 CLK_PHASE 由 0 → 1,SPI 由 Mode 2 → Mode 3,匹配 FM25CL64B 官方支持。


第 12 行wren_cmd()

复制代码
// FRAM 初始化:写使能 → 写状态寄存器 → 批量读取参数
wren_cmd();                            // 发送 WREN:FRAM 写使能

功能含义

  • 发送 WREN 命令,FRAM 写使能。

  • FRAM 写操作前必须先 WREN。

为什么调用

后面要写状态寄存器、写参数,必须先写使能。

Q:WREN 之后如果没写操作,WREN 会自动清除吗?

A:会。WREN 是一次性使能,写操作完成后自动清除。


第 13 行wrsr_cmd()

复制代码
wrsr_cmd();                            // 发送 WRSR:写 FRAM 状态寄存器

功能含义

  • 发送 WRSR 命令,写 FRAM 状态寄存器。

  • 这里写 STATUS_REG = 0,清除写保护等。

为什么调用

确保 FRAM 状态寄存器配置正确,允许后续读写。

Q:为什么先 WREN 再 WRSR?

A:FRAM 写状态寄存器前必须先写使能,否则 WRSR 被忽略。


第 14 行sread_FRAM()

复制代码
sread_FRAM();                          // 批量读取 FRAM 参数到 paramet[]

功能含义

  • 批量读取 FRAM 参数到 paramet[]。

  • 循环调用 FRAM_rd()。

为什么调用

上电初始化时把 FRAM 中保存的参数加载到 RAM。

Q:sread_FRAM() 放在初始化里合适吗?

A:如果参数必须上电加载,合适;但会增加初始化时间,也可放到独立任务。

Q:sread_FRAM() 里地址为什么是 XP_ADDR + (i<<1)?

A:i 是字索引,FRAM 按字节编址,每个 16 位字占 2 字节,所以地址乘 2。


总结表

顺序 代码 寄存器/位 值 中文含义 面试要点
1 SPISWRESET = 0 SPICCR.SPISWRESET 0 SPI 进入复位 配置前必须先清 0
2 SPILBK = 0 SPICCR.SPILBK 0 禁止回环 回环用于自测
3 CLKPOLARITY = 1 SPICCR.CLKPOLARITY 1 空闲高 与 CPHA=0 组成模式 2
4 SPICHAR = 0x7 SPICCR.SPICHAR 0x7 8 位 实际位数 = 值 + 1
5 CLK_PHASE = 0 SPICTL.CLK_PHASE 0 相位 0 下降沿输出,上升沿采样
6 MASTER_SLAVE = 1 SPICTL.MASTER_SLAVE 1 主机 主机产生时钟
7 TALK = 1 SPICTL.TALK 1 发送使能 0 时 SPISOMI 高阻
8 SPIINTENA = 0 SPICTL.SPIINTENA 0 禁中断 查询方式
9 SPIBRR = 0x03 SPIBRR.all 3 分频 25M/(3+1)=6.25MHz
10 FREE = 1 SPIPRI.FREE 1 自由运行 仿真挂起继续跑
11 SPISWRESET = 1 SPICCR.SPISWRESET 1 退出复位 必须最后置 1
12 wren_cmd() --- --- FRAM 写使能 WREN 后才能写
13 wrsr_cmd() --- --- 写状态寄存器 先 WREN 再 WRSR
14 sread_FRAM() --- --- 批量读参数 地址按字索引乘 2

相关推荐
1wpc1 小时前
BCIKit M1 脑电开发板使用教程(一):认识采集板与首次上电
单片机·嵌入式硬件·硬件工程
dtq04241 小时前
嵌入式5 - GPIO工作模式2
单片机·嵌入式硬件·学习
weixin_404380632 小时前
ESP-IDF v6.1:SoftAP 配网在干什么(手机连开发板热点)
单片机·嵌入式硬件·物联网
TechVoyager_82462 小时前
IT66122FN 解析:并行视频输入的引脚映射与位宽适配
单片机·嵌入式硬件·音视频·引脚映射·位宽适配·并行接口
Sakura_j_h_2 小时前
STM32 采集 NTC 温度抖动:RC 硬件滤波与软件滑动平均怎么选
stm32·单片机·嵌入式硬件·滑动平均·传感器滤波·rc低通
糖糖单片机设计13 小时前
基于 STM32 与中移 OneNet 的智慧电梯监控平台系统
stm32·单片机·嵌入式硬件·51单片机·语音识别
创界工坊工作室15 小时前
55-基于STC89C52宠物智能饲喂控制系统设计
stm32·单片机·嵌入式硬件·51单片机·proteus·宠物
极客 - L U19 小时前
卡尔曼滤波从零到精通
c语言·数据结构·单片机·嵌入式硬件·mcu
码小文20 小时前
CAM350使用笔记(6)---Gerber文件铺铜间距检查
嵌入式硬件·cam350软件·cam350使用笔记