API GUIDE / SFC / LL
SFC LL 接口
sfc ll api
本文面向 BSP、SoC 驱动、SFC HAL 维护者和高级调试开发者,说明 G32S10 SFC 的 LL(Low Level)寄存器访问接口。ll_sfc.h 只提供 static inline 的寄存器读写和位域打包 helper,不是可直接承担 Flash 协议、DMA、cache、超时或并发管理的事务驱动。
API 头文件:
code/include/drivers/g32s10/sfc/ll_sfc.hcode/include/drivers/g32s10/sfc/sfc_regs.h(寄存器布局、SFC实例、位域掩码和少量 CDT 常量)
ll_sfc.h 没有对应 .c 文件,所有接口在包含头文件后内联展开。寄存器地址、字段宽度和控制序列必须与 G32S10 SFC 规格一致;本文不展开 SPI Flash 命令语义、DMA descriptor 格式、cache 维护、GPIO pinmux、时钟树和板级电气参数。
LL 层不负责参数校验、错误码、状态机、互斥、超时、时钟使能、pinmux、Flash 上下电/复位、DMA 地址转换、cache 操作或中断注册。普通应用应优先使用
hal_sfc.h或 Common SFC 中间件;直接调用 LL 接口时,调用方承担全部正确性责任。
1. 模块概览
ll_sfc.h 是 SFC HAL 和中间件的寄存器级基础层,只有一个受支持的实例 SFC。接口不维护软件句柄,也不判断控制器是否正在传输;每次调用都直接访问当前寄存器值。
| 组件 | 作用 |
|---|---|
ll_sfc |
提供 static inline 寄存器读写、位域 set/get、状态检查和 CPU 数据端口访问 |
sfc_regs |
定义 sfc_regs_t、SFC 实例、寄存器偏移、字段 mask/shift 和 CDT 布局常量 |
hal_sfc |
在 LL 层之上提供参数检查、句柄状态、事务配置、中断回调和 XIP API |
im_sfc / im_sfc_device |
在 HAL 之上封装 Flash 命令、DMA、cache、IRQ、超时和板级参数 |
LL 层的使用边界如下:
- 所有寄存器接口只接受有效的
SFC寄存器块指针;除ll_sfc_is_instance()外均不检查指针、索引或字段范围。 - 返回值只有
void、bool和uint32_t,没有error_code_t。写接口成功与否无法通过返回值判断。 - 字段 setter 通常使用读改写,只修改目标位域;全寄存器 setter(如
ll_sfc_set_glb0())会覆盖整个寄存器,包括保留位。 TRIG和SCR是只写寄存器,SR、FSM和设备实时状态寄存器是只读寄存器。SCR为写 1 清零,写 0 无效果。- LL 层不提供锁、原子操作或访问顺序保证。多个执行上下文共享
SFC时,读改写序列必须由调用方串行化。 - 控制器正在传输或 XIP 已开启时,不应改动作业中的 phase、地址、数据长度、CDT 或 XIP 相关配置。
- 配置完成后,传输由
ll_sfc_trigger_start()启动;LL 层不等待END、不服务 FIFO,也不清理 Flash 内部 busy 状态。
本文覆盖 ll_sfc.h 的 195 个公开内联接口,按其访问的寄存器分为 18 个功能组:
| 功能组 | 主要寄存器 | 用途 |
|---|---|---|
| 实例检查 | — | 判断指针是否为 SFC |
| 全局配置 | GLB0 |
方向、phase 数、FIFO 阈值、CPU/DMA、CDT、描述符、burst |
| 设备公共配置 | DEV_CONF |
端序、采样延时/边沿、命令宽度、状态宽度、CE timing、引脚无效电平 |
| 设备状态 | DEV_STA_EXP、DEV0_STA_RT、DEV_STA_MSK |
硬件轮询期望值、实时值和比较掩码 |
| Phase 配置 0 | TRAN_CFG0[0..5] |
phase 的命令、地址宽度、poll、dummy、format、数据使能 |
| 传输长度和地址 | TRAN_LEN、DEV_ADDR、DEV_ADDR_PLUS、MEM_ADDR |
Direct 模式的 Flash 地址、DMA 内存地址和字节长度 |
| 触发 | TRIG |
启动、停止、FIFO flush |
| 状态 | SR |
working、busy、END、FIFO 请求、overflow/underflow、FIFO 字数 |
| 状态清除 | SCR |
写 1 清除 SR 状态和对应中断标志 |
| 中断控制 | INTC |
按事件屏蔽/解除屏蔽中断 |
| 状态机快照 | FSM |
AHB、SPI、DMAC、RMC 状态,用于调试 |
| 时钟门控 | CGE |
flow、DMA、RMC、SPI、REG 子模块门控 |
| CDT 执行选择 | CMD_IDX |
起始 CDT entry、方向和本次是否传输数据 |
| CDT 地址 | COL_ADDR、ROW_ADDR、STA_ADDR0/1、DES_ADDR |
CDT entry 使用的列/行/状态地址和 DMA 描述符地址 |
| 全局配置 1 | GLB1 |
软复位、XIP、DQS、连续读 mode bits、chip select |
| Device 1 状态 | DEV1_STA_RT |
第二设备端口的实时状态值 |
| Phase 配置 1 | TRAN_CFG1[0..5] |
phase 数据端序、word unit、SPI transfer mode |
| CDT 和数据端口 | CDT[0..255]、DR |
64 条 CDT entry 及 CPU 32-bit 数据端口 |
2. 典型流程
2.1 前置条件与包含方式
LL 头文件没有独立的 Kconfig 选项和源文件。通过 SFC HAL 使用时,hal_sfc.h 已经包含 ll_sfc.h;直接使用寄存器接口时,可显式包含:
#include "ll_sfc.h"
调用任何会访问硬件的接口前,板级必须完成以下准备:
- 使能 SFC 总线时钟和门控时钟,确认 CPM 没有关闭控制器时钟。
- 配置 CS#、SCLK、MOSI/MISO、WP#、HOLD#/RESET# 等 pinmux,并保证外部 Flash 已上电且退出复位。
- 确认当前没有其它任务、IRQ 或 DMA 路径在操作同一控制器。
- 根据 Flash 和板级时序确定
DEV_CONF、phase、dummy、采样点和 XIP mode bits。 - DMA 模式下确认 buffer 物理地址、对齐、连续性和 cache 维护策略;LL 层只写地址寄存器。
LL 头文件本身不会打开时钟,也不会把控制器从复位、XIP 或 busy 状态中安全退出。若控制器状态未知,应通过 HAL 初始化/复位流程或平台复位建立已知起点。
2.2 Direct 模式配置顺序
Direct 模式使用 GLB0.CDT_EN=0,按 GLB0.PHASE_NUM 指定的 phase 数执行 TRAN_CFG0[n] 和 TRAN_CFG1[n]。推荐的配置顺序如下:
- 确认
SR.WORKING=0且GLB1.XIP_EN=0;必要时先停止、flush 和清除旧状态。 - 配置
DEV_CONF:命令/状态端序、状态宽度、采样延时、采样边沿、CPOL/CPHA 和 CE timing。 - 配置本次使用的
TRAN_CFG0[n]、TRAN_CFG1[n],并写入相应的DEV_ADDR[n]/DEV_ADDR_PLUS[n]。 - 写
TRAN_LEN,单位为字节;没有数据 phase 时必须为 0。 - 选择 CPU 或 DMA 数据通路,并在 DMA 非 descriptor 模式下写
MEM_ADDR,descriptor 模式下写DES_ADDR。 - 在
GLB0中设置方向、phase 数、CDT_EN=0、DES_EN和OP_MODE。不要无条件覆盖不相关字段。 - 必要时配置
DEV_STA_EXP、DEV_STA_MSK、POLL_TIME和INTC。 - 清除旧的
END/TREQ/RREQ/OVER/UNDER状态,flush FIFO,然后写TRIG.START=1。
TRAN_CFG0[n]、TRAN_CFG1[n]、DEV_ADDR[n] 和 DEV_ADDR_PLUS[n] 的 n 范围都是 0 到 5。GLB0.PHASE_NUM 的有效值为 1 到 6,不能写 0。
2.3 CPU FIFO 数据服务
CPU 模式下,GLB0.OP_MODE=0,数据通过 32-bit 寄存器 DR 访问。典型服务流程如下:
- 写入
TRAN_LEN后启动事务;SR.FIFONUM[6:0]表示当前 FIFO 中的 word 数。 - 发送方向:等待
SR.TRAN_REQ,检查 FIFO 空间,再调用ll_sfc_write_data()写入数据。 - 接收方向:等待
SR.RECE_REQ,检查SR.FIFONUM,再调用ll_sfc_read_data()读取数据。 - 传输完成时处理
SR.END;出现SR.OVER或SR.UNDER时停止事务并清理状态。 - 消费请求后,按中断/轮询策略清除对应的
TREQ/RREQ状态,最后清除END。
FIFO 深度、阈值和一次可搬运的 word 数由控制器规格决定。LL 层不会拦截空读、满写或超长 burst;读取空 FIFO 可能得到无效旧数据,写入已满 FIFO 可能造成 overflow。SR.FIFONUM 的读取值只代表采样时刻,不能替代硬件流控。
2.4 CDT 模式配置顺序
CDT 模式使用 GLB0.CDT_EN=1,由 CMD_IDX 选择起始 entry,并沿 CDT_WORD0.LINK 执行命令链。每个 entry 固定占用 4 个 word:
- 计算
word_index = entry_index * 4,依次写 word0、word1、word2、word3;ll_sfc_set_cdt_word()接收的是 word index,不是 entry index。 - word0 配置 link、数据端序、word unit、transfer mode 和地址寄存器类型。
- word1 配置命令、地址宽度、poll、dummy、phase format、数据使能。
- word2/word3 分别是该 entry 的状态期望值和状态掩码。
- 写
CMD_IDX的起始索引、方向和CDT_DATA_EN,并写本次使用的列地址、行地址、状态地址。 - 配置本次 CDT 执行的数据长度和 CPU/DMA 数据通路,设置
GLB0.CDT_EN=1。 - 清状态、flush FIFO,写
TRIG.START=1;完成后检查END和错误状态。
最后一个 entry 的 LINK 必须为 0,索引 63 不能再链接到下一项。整条链的有效性、地址字段选择、poll 条件和数据 phase 的唯一性由调用方保证。
2.5 XIP 和软件复位
XIP 使能前必须先配置好连续读 phase、Flash mode bits 和地址映射。控制器处于 working 状态时不要打开 XIP;进入 XIP 后,不要再改动普通命令 phase 或 CDT 配置。
配置连续读 phase -> 配置 GLB1.MODE_BIT -> 置 GLB1.XIP_EN=1 -> 通过映射窗口取指/读数据
|
需要重配时 <- 清 XIP_EN 和 MODE_BIT
ll_sfc_set_reset() 向 GLB1.RESET 写入复位持续值并启动控制器软复位;硬件递减计数后自动释放,软件不需要轮询该字段。复位后应重新建立 DEV_CONF、phase、GLB0/GLB1、中断和地址配置,不要假定旧配置仍然有效。复位不涉及外部 Flash、时钟或 pinmux。
2.6 并发、错误和清理原则
- 所有字段 setter 都是“读-改-写”序列;同一寄存器被任务和 IRQ 同时修改时可能丢写。调用方必须使用临界区或统一的所有者任务。
- LL 层无法报告总线错误、参数错误或控制器拒绝。调用方需要自行检查
SR、FSM和设备状态。 - 发生 over/under、超时或命令失败时,建议先写
TRIG.STOP=1,再写TRIG.FLUSH=1,最后用SCR清除全部可清除状态。 - 不要在
WORKING=1时修改GLB0的 phase/模式字段、TRAN_CFG*、CDT 或 XIP 配置。 - 写全寄存器接口时,应先用对应 getter(若寄存器可读)取得当前值,再修改目标字段,避免误清保留位或其它配置。
3. 配置和关键类型
3.1 实例和寄存器布局
sfc_regs_t 由 struct sfc_regs 定义,唯一公开实例为 SFC:
typedef struct sfc_regs sfc_regs_t;
#define SFC ((sfc_regs_t *)(uintptr_t)SFC_IOBASE)
SFC_IOBASE 为 0x13440000。ll_sfc_is_instance() 只比较指针是否等于 SFC;其它指针对应的寄存器对象不被 LL 接口支持。
| 寄存器 | 偏移 | 访问属性 | 说明 |
|---|---|---|---|
GLB0 |
0x000 |
R/W | 全局模式、phase 数、FIFO 阈值、CPU/DMA、CDT/descriptor、burst |
DEV_CONF |
0x004 |
R/W | 设备公共时序、端序、采样和引脚无效电平 |
DEV_STA_EXP |
0x008 |
R/W | 轮询期望状态 |
DEV0_STA_RT |
0x00C |
RO | Device 0 实时状态 |
DEV_STA_MSK |
0x010 |
R/W | 状态比较掩码 |
TRAN_CFG0[6] |
0x014–0x028 |
R/W | phase 0..5 的命令/地址/dummy/数据配置 |
TRAN_LEN |
0x02C |
R/W | 传输字节数 |
DEV_ADDR[6] |
0x030–0x044 |
R/W | Direct 模式 phase 地址低 32 位 |
DEV_ADDR_PLUS[6] |
0x048–0x05C |
R/W | Direct 模式 phase 地址高 16 位 |
MEM_ADDR |
0x060 |
R/W | 非 descriptor DMA 内存地址 |
TRIG |
0x064 |
WO | start/stop/FIFO flush |
SR |
0x068 |
RO | 传输和 FIFO 状态 |
SCR |
0x06C |
WO, W1C | 清除 SR 状态和中断标志 |
INTC |
0x070 |
R/W | 中断屏蔽,1=masked |
FSM |
0x074 |
RO | 各子模块状态机快照 |
CGE |
0x078 |
R/W | 子模块时钟门控使能 |
CMD_IDX |
0x07C |
R/W | CDT 起始 entry、方向和传输数据使能 |
COL_ADDR |
0x080 |
R/W | CDT 列地址 |
ROW_ADDR |
0x084 |
R/W | CDT 行地址 |
STA_ADDR0 |
0x088 |
R/W | CDT 状态地址 0 |
STA_ADDR1 |
0x08C |
R/W | CDT 状态地址 1 |
DES_ADDR |
0x090 |
R/W | DMA descriptor 地址 |
GLB1 |
0x094 |
R/W | 软复位、XIP、DQS、mode bits、chip select |
DEV1_STA_RT |
0x098 |
RO | Device 1 实时状态 |
TRAN_CFG1[6] |
0x09C–0x0B0 |
R/W | phase 0..5 的端序、word unit、transfer mode |
CDT[256] |
0x800–0xBFF |
R/W | 64 个 entry × 4 word |
DR |
0x1000 |
R/W | CPU 32-bit 数据端口 |
3.2 通用参数和访问规则
| 规则 | 说明 |
|---|---|
| 实例 | 除 ll_sfc_is_instance() 外的所有接口都应按 ll_sfc_*(SFC, ...) 调用 |
| phase index | 0..5,所有 TRAN_CFG0/1、DEV_ADDR、DEV_ADDR_PLUS 接口共用 |
| CDT word index | 0..255,每个 entry 对应连续 4 个 word |
| CDT entry index | 0..63,经 CMD_IDX 选择,最后一 entry 不应再 link |
| FIFO threshold | 1..63,0 无效 |
| phase count | 1..6,0 无效 |
| address width | 0 无地址,1..5 对应 1..5 字节,6/7 都表示 6 字节 |
| dummy cycles | 0..63,单位为 SFC clock cycle |
| command | 16-bit 字段;实际宽度由 DEV_CONF.CMD_TYPE 选择 1 或 2 字节 |
| word unit | 0=1 字节,1=2 字节,2=4 字节,其他值保留 |
| CE hold/setup | 0..3,对应 0.5、1、1.5、2 个 SFC clock cycle |
| CE deselect | 0..15,对应 0.5 到 8 个 SFC clock cycle,步进 0.5 |
| CPU FIFO | 32-bit word 端口;字节序和 word unit 由 phase 配置决定 |
字段 setter 只清除并写入自己的字段,但读改写本身不带锁;若同一寄存器可能被 ISR 修改,必须把整个读改写序列放入临界区。全寄存器 setter 不保留旧值,调用方必须提供完整的 32-bit 值。
3.3 GLB0 全局配置
| 字段 | 位 | 编码/范围 | 说明 |
|---|---|---|---|
POLL_TIME |
31:16 |
16-bit | 硬件轮询操作之间的间隔计数 |
DES_EN |
15 |
0/1 | DMA descriptor 模式使能;使能后 DMA 使用 DES_ADDR |
CDT_EN |
14 |
0/1 | CDT 命令链模式使能 |
TRAN_DIR |
13 |
0/1 | 0=Flash 到 FIFO/内存,1=FIFO/内存到 Flash |
THRESHOLD |
12:7 |
1..63 | CPU FIFO 阈值,单位 word;0 无效 |
OP_MODE |
6 |
0/1 | 0=CPU FIFO,1=DMA |
PHASE_NUM |
5:3 |
1..6 | Direct 模式有效 phase 数 |
WP_EN |
2 |
0/1 | 硬件写保护使能 |
BURST_MD |
1:0 |
01/10/11 | DMA burst:8/16/32 beat;00 保留,复位默认 11 |
GLB0 的模式和 phase 字段应在启动新事务前完整确定。CDT 模式下 CDT_EN=1;DMA descriptor 只有在 OP_MODE=1 时才应使能。
3.4 DEV_CONF 设备公共配置
| 字段 | 位 | 编码/范围 | 说明 |
|---|---|---|---|
STA_ENDIAN |
31 |
0/1 | 状态数据 LSB-first/MSB-first |
SMP_DELAY |
20:16 |
0..31 | 采样延时编码,实际延时取决于器件、PCB/IO 和 SFC 采样时钟 |
CMD_TYPE |
15 |
0/1 | 命令字段 1/2 字节 |
STA_TYPE |
14:13 |
0..3 | 硬件轮询状态宽度:1/2/3/4 字节 |
THOLD |
12:11 |
0..3 | CE 最后一个时钟沿后的 hold:0.5/1/1.5/2 cycle |
TSETUP |
10:9 |
0..3 | 首个时钟沿前的 CE setup:0.5/1/1.5/2 cycle |
TSH |
8:5 |
0..15 | 两次 CE 有效之间的 deselect 间隔:0.5..8 cycle |
CPHA |
4 |
0/1 | 0=奇数边沿采样,1=偶数边沿采样 |
CPOL |
3 |
0/1 | 0=SCLK 高有效,1=SCLK 低有效 |
CE_DL |
2 |
0/1 | CE 无效电平:低/高 |
HOLD_DL |
1 |
0/1 | HOLD 无效电平:低/高 |
WP_DL |
0 |
0/1 | WP 无效电平:低/高 |
SMP_DELAY、CE timing 和引脚无效电平必须按实际 Flash/板级时序配置。整个 DEV_CONF 可在控制器 idle 时通过 ll_sfc_set_dev_conf() 一次写入,也可用字段 setter 保持其它位不变。
3.5 设备状态轮询
Direct 模式使用 DEV_STA_EXP、DEV0_STA_RT 和 DEV_STA_MSK 完成硬件状态轮询:
| 寄存器 | 访问 | 说明 |
|---|---|---|
DEV_STA_EXP |
R/W | 期望的状态值 |
DEV0_STA_RT |
RO | 硬件实时采样到的 Device 0 状态 |
DEV_STA_MSK |
R/W | 参与比较的状态位;具体比较规则以 SFC 规格为准 |
只有 phase 的 POLL_EN=1 时才会进入该 phase 的轮询流程。轮询会覆盖该 phase 的普通方向和 DATA_EN 行为;POLL_TIME 控制轮询间隔。CDT 模式下,每个 entry 使用自己的 word2/word3 作为期望值和掩码。
3.6 Phase 配置
每个 active phase 由 TRAN_CFG0[n] 和 TRAN_CFG1[n] 共同描述。GLB0.PHASE_NUM 必须与被使用的 phase 数一致。
TRAN_CFG0[n]:
| 字段 | 位 | 编码/范围 | 说明 |
|---|---|---|---|
CLK_MODE |
31:29 |
000 | 规格只定义 SDR,保持默认值,不要自行改写成其它编码 |
ADDR_WIDTH |
28:26 |
0..7 | 0 无地址;1..5 为 1..5 字节;6/7 为 6 字节 |
POLL_EN |
25 |
0/1 | 硬件状态轮询使能,优先于方向和 DATA_EN |
CMD_EN |
24 |
0/1 | 命令 phase 使能 |
PHASE_FORMAT |
23 |
0/1 | 硬件 phase 组件顺序格式 0/1 |
DMY_BITS |
22:17 |
0..63 | dummy SFC clock cycle 数 |
DATA_EN |
16 |
0/1 | 数据 phase 使能 |
CMD |
15:0 |
16-bit | 命令值;实际有效宽度由 CMD_TYPE 决定 |
TRAN_CFG1[n]:
| 字段 | 位 | 编码/范围 | 说明 |
|---|---|---|---|
DATA_ENDIAN |
18 |
0/1 | 数据 LSB-first/MSB-first |
WORD_UNIT |
17:16 |
0..2 | 数据单元 1/2/4 字节,3 保留 |
TRAN_MD |
7:4 |
0,1,2,3,5,6,7 | SPI line mode;4 保留 |
TRAN_MD 的常用编码为:0 标准 SPI,1 dual input/output,2 dual I/O,3 full dual I/O,5 quad input/output,6 quad I/O,7 full quad I/O。不同 Flash 命令允许的 line mode 必须按器件数据手册确认。
3.7 传输长度、地址和数据通路
| 配置项 | 说明 |
|---|---|
TRAN_LEN |
传输字节数;0 表示没有数据 phase。没有数据 phase 时必须为 0 |
DEV_ADDR[n] |
Direct 模式 phase 的 Flash 地址低 32 位 |
DEV_ADDR_PLUS[n] |
Direct 模式 phase 的 Flash 地址高 16 位,只有低 16 位有效 |
MEM_ADDR |
DMA 且 DES_EN=0 时的内存地址 |
DES_ADDR |
DMA descriptor 模式下的描述符地址;与 MEM_ADDR 互斥使用 |
Direct 模式可以把 DEV_ADDR_PLUS[n] 与 DEV_ADDR[n] 组成最多 48-bit 的 Flash 地址。CDT 模式不使用 DEV_ADDR[n];entry 的 ADDR_KIND 从 COL_ADDR、ROW_ADDR、STA_ADDR0、STA_ADDR1 中选择地址操作数。
CPU 模式只使用 DR;DMA 模式由控制器访问 MEM_ADDR 或 DES_ADDR。LL 层不检查地址对齐、物理连续性、cache 一致性或 descriptor 内容。
3.8 SR、SCR 和 INTC
SR 为只读状态:
| 字段 | 位 | 说明 |
|---|---|---|
FIFONUM |
22:16 |
CPU FIFO 当前 word 数 |
WORKING |
7 |
控制器正在执行传输 |
BUSY |
6:5 |
SPI 数据路径 busy 编码,具体取值见规格 |
END |
4 |
传输/命令链完成 |
TRAN_REQ |
3 |
发送 FIFO 请求 |
RECE_REQ |
2 |
接收 FIFO 请求 |
OVER |
1 |
FIFO overflow |
UNDER |
0 |
FIFO underflow |
SFC_SR_INT_MASK 包含 END/TREQ/RREQ/OVER/UNDER 五位。ll_sfc_get_pending_status() 返回 SR & SFC_SR_INT_MASK,不按 INTC 过滤;如果需要“已使能且挂起”的中断位,调用方应再清除 masked 位。
SCR 使用相同的事件位布局,写 1 清除,写 0 无效果:
| SCR 位 | 名称 | 清除对象 |
|---|---|---|
| 4 | CLR_END |
SR.END 和 END 中断标志 |
| 3 | CLR_TREQ |
SR.TRAN_REQ 和 TREQ 中断标志 |
| 2 | CLR_RREQ |
SR.RECE_REQ 和 RREQ 中断标志 |
| 1 | CLR_OVER |
SR.OVER 和 OVER 中断标志 |
| 0 | CLR_UNDR |
SR.UNDER 和 UNDER 中断标志 |
INTC 中每一位为 1 表示对应中断被屏蔽,为 0 表示开放。INTC 和 SR 使用相同的五位事件映射;SFC_INTC_MASK_ALL 屏蔽全部事件。
3.9 FSM 和 CGE
FSM 只读,用于调试控制器内部状态:
| 字段 | 位 | 说明 |
|---|---|---|
AHB |
19:16 |
AHB 状态机快照 |
SPI |
15:8 |
SPI 状态机快照 |
DMAC |
7:4 |
DMA 控制器状态机快照 |
RMC |
2:0 |
RMC 状态机快照 |
状态编码属于内部实现信息,不应作为稳定软件协议。生产逻辑应使用 SR.WORKING/BUSY/END 和 Flash 状态,而不是依赖 FSM 的具体数值。
CGE 控制子模块时钟门控,1 表示开启对应门控功能:
| 位 | 名称 | 子模块 |
|---|---|---|
| 5 | FLOW_EN |
flow control |
| 3 | DMA_EN |
DMAC/AHB master |
| 2 | RMC_EN |
RMC |
| 1 | SPI_EN |
SPI/clock generator |
| 0 | REG_EN |
register interface |
ll_sfc_set_clock_gate(SFC, 0U) 会关闭全部门控;SFC_CGE_EN_ALL 会全部开启。该寄存器控制的是门控功能,不是 SFC 总线时钟的开关。
3.10 CDT 四字布局和 CMD_IDX
每个 CDT entry 由连续 4 个 32-bit word 组成。entry_index 为 0..63,对应 word_index = entry_index * 4。
| Word | 字段 | 位 | 说明 |
|---|---|---|---|
| word0 | LINK |
31 | 1=继续执行下一个 entry,0=链结束 |
| word0 | ENDIAN |
18 | 数据端序 |
| word0 | WORD_UNIT |
17:16 | 数据单元 1/2/4 字节 |
| word0 | TRAN_MODE |
7:4 | SPI line mode |
| word0 | ADDR_KIND |
1:0 | 0=COL,1=ROW,2=STA0,3=STA1 |
| word1 | CLK_MODE |
31:29 | SDR,保持默认编码 |
| word1 | ADDR_WIDTH |
28:26 | 地址宽度编码,规则同 TRAN_CFG0 |
| word1 | POLL_EN |
25 | 轮询 phase 使能 |
| word1 | CMD_EN |
24 | 命令 phase 使能 |
| word1 | PHASE_FORMAT |
23 | phase 组件顺序格式 |
| word1 | DMY_BITS |
22:17 | dummy cycle 数 |
| word1 | DATA_EN |
16 | 数据 phase 使能 |
| word1 | CMD |
15:0 | 命令值 |
| word2 | STATUS_EXPECT |
31:0 | 该 entry 的状态期望值 |
| word3 | STATUS_MASK |
31:0 | 该 entry 的状态比较掩码 |
CMD_IDX:
| 字段 | 位 | 说明 |
|---|---|---|
CDT_DATA_EN |
31 | 本次 CDT 执行是否搬运数据 |
CDT_DIR |
30 | 0=Flash 读,1=Flash 写 |
CMD_IDX |
5:0 | 起始 CDT entry 索引,0..63 |
CDT 的 word0 对应 TRAN_CFG1 的语义,word1 对应 TRAN_CFG0 的语义,但字段位置不同,不能直接把寄存器值原样复制为另一种格式。
3.11 GLB1、XIP 和软复位
| 字段 | 位 | 编码/范围 | 说明 |
|---|---|---|---|
RESET |
20:15 |
0..63 | 写入即启动软复位;数值为 HCLK 周期,0 表示硬件自动释放;硬件自动递减 |
TRAN_DIR_SEL |
14:13 |
00 | 保持默认值 |
MODE_BIT |
12:5 |
8-bit | XIP 连续读 mode bits |
XIP_EN |
4 |
0/1 | XIP 使能 |
DQS_EN |
2 |
0/1 | DQS 使能;规格要求保持默认值 |
CHIP_SEL |
1:0 |
00 | 当前仅默认值 00 有效,其它值保留 |
ll_sfc_set_reset() 只触发控制器复位,不等待 Flash、时钟或 pinmux。RESET=0 表示由硬件自动释放;需要明确的复位持续时间时,应写入非 0 的 HCLK 周期值。清空该字段不是取消复位的软件接口。复位前应确认没有正在进行的事务,复位后重新配置所有寄存器。
3.12 常用常量
| 常量 | 值/含义 |
|---|---|
SFC_IOBASE |
0x13440000,SFC 寄存器基地址 |
SFC_CDT_ENTRY_COUNT |
64 个 CDT entry |
SFC_CDT_WORDS_PER_ENTRY |
每个 entry 4 个 word |
SFC_SR_INT_MASK |
END/TREQ/RREQ/OVER/UNDER 五位集合 |
SFC_SCR_CLR_ALL |
清除全部可清除状态 |
SFC_INTC_MASK_ALL |
屏蔽全部中断 |
SFC_CGE_EN_ALL |
开启全部时钟门控功能 |
SFC_TRIG_START_MASK |
启动传输 |
SFC_TRIG_STOP_MASK |
终止全部传输 |
SFC_TRIG_FLUSH_MASK |
flush FIFO |
SFC_GLB1_RESET_MASK |
软复位字段 mask |
SFC_GLB1_XIP_EN_MASK |
XIP 使能位 mask |
LL 层没有错误码表:所有寄存器写接口都没有失败返回,读取接口也不区分“读到 0”和“访问失败”。错误检测必须由调用方结合 SR、FSM、设备状态和应用超时完成。
4. API 参考
4.1 API 总览
ll_sfc.h
下表按功能组汇总全部接口。sfcx 统一表示 sfc_regs_t *,合法实例为 SFC;详细参数和约束见后续各节。
实例检查
| API | 用途 |
|---|---|
ll_sfc_is_instance() |
判断指针是否为受支持的 SFC 实例 |
GLB0 全局配置
| API | 用途 |
|---|---|
ll_sfc_get_glb0() |
读取完整 GLB0 |
ll_sfc_set_glb0() |
覆盖写入完整 GLB0 |
ll_sfc_set_poll_time() |
设置硬件轮询间隔 |
ll_sfc_get_poll_time() |
读取硬件轮询间隔 |
ll_sfc_enable_dma_descriptor() |
开启 DMA descriptor 模式 |
ll_sfc_disable_dma_descriptor() |
关闭 DMA descriptor 模式 |
ll_sfc_is_dma_descriptor_enabled() |
查询 descriptor 模式 |
ll_sfc_enable_cdt() |
开启 CDT 模式 |
ll_sfc_disable_cdt() |
关闭 CDT 模式 |
ll_sfc_is_cdt_enabled() |
查询 CDT 模式 |
ll_sfc_set_transfer_direction() |
设置 Flash 读写方向 |
ll_sfc_is_write_transfer() |
查询是否为写 Flash 方向 |
ll_sfc_set_fifo_threshold() |
设置 CPU FIFO 阈值 |
ll_sfc_get_fifo_threshold() |
读取 CPU FIFO 阈值 |
ll_sfc_set_operation_mode() |
选择 CPU FIFO 或 DMA |
ll_sfc_is_dma_operation_mode() |
查询是否为 DMA 模式 |
ll_sfc_set_phase_count() |
设置 Direct 模式 phase 数 |
ll_sfc_get_phase_count() |
读取 phase 数 |
ll_sfc_enable_write_protect() |
开启硬件写保护 |
ll_sfc_disable_write_protect() |
关闭硬件写保护 |
ll_sfc_is_write_protect_enabled() |
查询硬件写保护 |
ll_sfc_set_burst_mode() |
设置 DMA burst 长度编码 |
ll_sfc_get_burst_mode() |
读取 DMA burst 长度编码 |
DEV_CONF 设备公共配置
| API | 用途 |
|---|---|
ll_sfc_get_dev_conf() |
读取完整 DEV_CONF |
ll_sfc_set_dev_conf() |
覆盖写入完整 DEV_CONF |
ll_sfc_set_status_endian() |
设置状态端序 |
ll_sfc_is_status_msb_first() |
查询状态是否 MSB-first |
ll_sfc_set_sample_delay() |
设置采样延时编码 |
ll_sfc_get_sample_delay() |
读取采样延时编码 |
ll_sfc_set_command_type() |
设置 1/2 字节命令宽度 |
ll_sfc_is_two_byte_command() |
查询是否使用 2 字节命令 |
ll_sfc_set_status_type() |
设置轮询状态宽度 |
ll_sfc_get_status_type() |
读取轮询状态宽度 |
ll_sfc_set_ce_hold_time() |
设置 CE hold 时间编码 |
ll_sfc_get_ce_hold_time() |
读取 CE hold 时间编码 |
ll_sfc_set_ce_setup_time() |
设置 CE setup 时间编码 |
ll_sfc_get_ce_setup_time() |
读取 CE setup 时间编码 |
ll_sfc_set_ce_deselect_interval() |
设置 CE deselect 间隔 |
ll_sfc_get_ce_deselect_interval() |
读取 CE deselect 间隔 |
ll_sfc_set_sample_edge() |
设置采样边沿 |
ll_sfc_is_sampled_on_even_edge() |
查询是否在偶数边沿采样 |
ll_sfc_set_clock_polarity() |
设置 SCLK 极性 |
ll_sfc_is_clock_active_low() |
查询 SCLK 是否低有效 |
ll_sfc_set_ce_inactive_level() |
设置 CE 无效电平 |
ll_sfc_is_ce_inactive_high() |
查询 CE 无效电平是否为高 |
ll_sfc_set_hold_inactive_level() |
设置 HOLD 无效电平 |
ll_sfc_is_hold_inactive_high() |
查询 HOLD 无效电平是否为高 |
ll_sfc_set_wp_inactive_level() |
设置 WP 无效电平 |
ll_sfc_is_wp_inactive_high() |
查询 WP 无效电平是否为高 |
设备状态
| API | 用途 |
|---|---|
ll_sfc_set_dev_status_expect() |
写轮询期望状态 |
ll_sfc_get_dev_status_expect() |
读轮询期望状态 |
ll_sfc_get_dev0_status() |
读 Device 0 实时状态 |
ll_sfc_set_dev_status_mask() |
写状态比较掩码 |
ll_sfc_get_dev_status_mask() |
读状态比较掩码 |
TRAN_CFG0 phase 配置
| API | 用途 |
|---|---|
ll_sfc_get_tran_cfg0() |
读取一个 phase 的完整配置 |
ll_sfc_set_tran_cfg0() |
覆盖写入一个 phase 的完整配置 |
ll_sfc_set_tran_cfg0_clock_mode() |
设置 phase clock mode |
ll_sfc_get_tran_cfg0_clock_mode() |
读取 phase clock mode |
ll_sfc_set_tran_cfg0_address_width() |
设置 phase 地址宽度 |
ll_sfc_get_tran_cfg0_address_width() |
读取 phase 地址宽度 |
ll_sfc_enable_tran_cfg0_poll() |
开启 phase 硬件轮询 |
ll_sfc_disable_tran_cfg0_poll() |
关闭 phase 硬件轮询 |
ll_sfc_is_tran_cfg0_poll_enabled() |
查询 phase 轮询使能 |
ll_sfc_enable_tran_cfg0_command() |
开启命令 phase |
ll_sfc_disable_tran_cfg0_command() |
关闭命令 phase |
ll_sfc_is_tran_cfg0_command_enabled() |
查询命令 phase 使能 |
ll_sfc_set_tran_cfg0_phase_format() |
设置 phase 组件格式 |
ll_sfc_is_tran_cfg0_format1() |
查询是否使用 format 1 |
ll_sfc_set_tran_cfg0_dummy_cycles() |
设置 dummy cycle 数 |
ll_sfc_get_tran_cfg0_dummy_cycles() |
读取 dummy cycle 数 |
ll_sfc_enable_tran_cfg0_data() |
开启数据 phase |
ll_sfc_disable_tran_cfg0_data() |
关闭数据 phase |
ll_sfc_is_tran_cfg0_data_enabled() |
查询数据 phase 使能 |
ll_sfc_set_tran_cfg0_command() |
设置 phase 命令值 |
ll_sfc_get_tran_cfg0_command() |
读取 phase 命令值 |
传输长度和地址
| API | 用途 |
|---|---|
ll_sfc_set_transfer_length() |
写传输字节数 |
ll_sfc_get_transfer_length() |
读传输字节数 |
ll_sfc_set_device_address() |
写 phase 地址低 32 位 |
ll_sfc_get_device_address() |
读 phase 地址低 32 位 |
ll_sfc_set_device_address_plus() |
写 phase 地址高 16 位 |
ll_sfc_get_device_address_plus() |
读 phase 地址高 16 位 |
ll_sfc_set_memory_address() |
写非 descriptor DMA 内存地址 |
ll_sfc_get_memory_address() |
读非 descriptor DMA 内存地址 |
触发
| API | 用途 |
|---|---|
ll_sfc_trigger_start() |
写 TRIG.START 启动传输 |
ll_sfc_trigger_stop() |
写 TRIG.STOP 终止传输 |
ll_sfc_trigger_fifo_flush() |
写 TRIG.FLUSH 清空 FIFO |
SR 状态
| API | 用途 |
|---|---|
ll_sfc_get_status() |
读取完整 SR |
ll_sfc_get_fifo_word_count() |
读取 FIFO word 数 |
ll_sfc_is_working() |
查询 controller working |
ll_sfc_get_busy_state() |
读取 SPI busy 编码 |
ll_sfc_is_end() |
查询 END |
ll_sfc_is_tran_req() |
查询发送请求 |
ll_sfc_is_rece_req() |
查询接收请求 |
ll_sfc_is_over() |
查询 FIFO overflow |
ll_sfc_is_under() |
查询 FIFO underflow |
ll_sfc_get_pending_status() |
读取五位待处理状态 |
SCR 状态清除
| API | 用途 |
|---|---|
ll_sfc_clear_status_bits() |
按 mask 写 1 清除状态 |
ll_sfc_clear_end_status() |
清除 END |
ll_sfc_clear_tran_req_status() |
清除 TREQ |
ll_sfc_clear_rece_req_status() |
清除 RREQ |
ll_sfc_clear_over_status() |
清除 OVER |
ll_sfc_clear_under_status() |
清除 UNDER |
ll_sfc_clear_all_status_bits() |
清除全部可清除状态 |
INTC 中断控制
| API | 用途 |
|---|---|
ll_sfc_set_interrupt_mask() |
覆盖写完整中断屏蔽值 |
ll_sfc_get_interrupt_mask() |
读取中断屏蔽值 |
ll_sfc_is_end_interrupt_masked() |
查询 END 中断是否屏蔽 |
ll_sfc_is_treq_interrupt_masked() |
查询 TREQ 中断是否屏蔽 |
ll_sfc_is_rreq_interrupt_masked() |
查询 RREQ 中断是否屏蔽 |
ll_sfc_is_over_interrupt_masked() |
查询 OVER 中断是否屏蔽 |
ll_sfc_is_undr_interrupt_masked() |
查询 UNDER 中断是否屏蔽 |
ll_sfc_mask_interrupts() |
按 mask 屏蔽中断 |
ll_sfc_mask_end_interrupt() |
屏蔽 END 中断 |
ll_sfc_mask_treq_interrupt() |
屏蔽 TREQ 中断 |
ll_sfc_mask_rreq_interrupt() |
屏蔽 RREQ 中断 |
ll_sfc_mask_over_interrupt() |
屏蔽 OVER 中断 |
ll_sfc_mask_undr_interrupt() |
屏蔽 UNDER 中断 |
ll_sfc_unmask_interrupts() |
按 mask 开放中断 |
ll_sfc_unmask_end_interrupt() |
开放 END 中断 |
ll_sfc_unmask_treq_interrupt() |
开放 TREQ 中断 |
ll_sfc_unmask_rreq_interrupt() |
开放 RREQ 中断 |
ll_sfc_unmask_over_interrupt() |
开放 OVER 中断 |
ll_sfc_unmask_undr_interrupt() |
开放 UNDER 中断 |
ll_sfc_mask_all_interrupts() |
屏蔽全部中断 |
ll_sfc_unmask_all_interrupts() |
开放全部中断 |
FSM 调试状态
| API | 用途 |
|---|---|
ll_sfc_get_fsm() |
读取完整 FSM |
ll_sfc_get_ahb_fsm() |
读取 AHB 状态机快照 |
ll_sfc_get_spi_fsm() |
读取 SPI 状态机快照 |
ll_sfc_get_dmac_fsm() |
读取 DMAC 状态机快照 |
ll_sfc_get_rmc_fsm() |
读取 RMC 状态机快照 |
CGE 时钟门控
| API | 用途 |
|---|---|
ll_sfc_get_clock_gate() |
读取完整 CGE |
ll_sfc_set_clock_gate() |
覆盖写入完整 CGE |
ll_sfc_enable_flow_clock_gate() |
开启 flow-control 门控 |
ll_sfc_disable_flow_clock_gate() |
关闭 flow-control 门控 |
ll_sfc_is_flow_clock_gate_enabled() |
查询 flow-control 门控 |
ll_sfc_enable_dma_clock_gate() |
开启 DMA/AHB 门控 |
ll_sfc_disable_dma_clock_gate() |
关闭 DMA/AHB 门控 |
ll_sfc_is_dma_clock_gate_enabled() |
查询 DMA/AHB 门控 |
ll_sfc_enable_rmc_clock_gate() |
开启 RMC 门控 |
ll_sfc_disable_rmc_clock_gate() |
关闭 RMC 门控 |
ll_sfc_is_rmc_clock_gate_enabled() |
查询 RMC 门控 |
ll_sfc_enable_spi_clock_gate() |
开启 SPI/clock-generator 门控 |
ll_sfc_disable_spi_clock_gate() |
关闭 SPI/clock-generator 门控 |
ll_sfc_is_spi_clock_gate_enabled() |
查询 SPI/clock-generator 门控 |
ll_sfc_enable_reg_clock_gate() |
开启寄存器接口门控 |
ll_sfc_disable_reg_clock_gate() |
关闭寄存器接口门控 |
ll_sfc_is_reg_clock_gate_enabled() |
查询寄存器接口门控 |
CMD_IDX CDT 执行选择
| API | 用途 |
|---|---|
ll_sfc_get_cmd_idx_register() |
读取完整 CMD_IDX |
ll_sfc_set_cmd_idx_register() |
覆盖写入完整 CMD_IDX |
ll_sfc_enable_cmd_idx_cdt_data() |
允许本次 CDT 执行传输数据 |
ll_sfc_disable_cmd_idx_cdt_data() |
禁止本次 CDT 执行传输数据 |
ll_sfc_is_cmd_idx_cdt_data_enabled() |
查询 CDT 数据使能 |
ll_sfc_set_cmd_idx_transfer_direction() |
设置 CDT 数据方向 |
ll_sfc_is_cmd_idx_write_transfer() |
查询 CDT 是否写 Flash |
ll_sfc_set_cmd_idx_index() |
设置起始 CDT entry |
ll_sfc_get_cmd_idx_index() |
读取起始 CDT entry |
CDT 和 DMA 地址
| API | 用途 |
|---|---|
ll_sfc_set_column_address() |
写 CDT 列地址 |
ll_sfc_get_column_address() |
读 CDT 列地址 |
ll_sfc_set_row_address() |
写 CDT 行地址 |
ll_sfc_get_row_address() |
读 CDT 行地址 |
ll_sfc_set_status_address0() |
写 CDT 状态地址 0 |
ll_sfc_get_status_address0() |
读 CDT 状态地址 0 |
ll_sfc_set_status_address1() |
写 CDT 状态地址 1 |
ll_sfc_get_status_address1() |
读 CDT 状态地址 1 |
ll_sfc_set_descriptor_address() |
写 DMA descriptor 地址 |
ll_sfc_get_descriptor_address() |
读 DMA descriptor 地址 |
GLB1、XIP 和软复位
| API | 用途 |
|---|---|
ll_sfc_get_glb1() |
读取完整 GLB1 |
ll_sfc_set_glb1() |
覆盖写入完整 GLB1 |
ll_sfc_set_reset() |
写复位持续值并启动软复位 |
ll_sfc_get_reset() |
读取当前复位倒计时 |
ll_sfc_set_tran_dir_select() |
设置 TRAN_DIR_SEL,应保持 0 |
ll_sfc_get_tran_dir_select() |
读取 TRAN_DIR_SEL |
ll_sfc_set_mode_bit() |
设置 XIP 连续读 mode bits |
ll_sfc_get_mode_bit() |
读取 XIP 连续读 mode bits |
ll_sfc_enable_xip() |
开启 XIP |
ll_sfc_disable_xip() |
关闭 XIP |
ll_sfc_is_xip_enabled() |
查询 XIP 状态 |
ll_sfc_enable_dqs() |
开启 DQS,规格要求保持默认 |
ll_sfc_disable_dqs() |
关闭 DQS,规格要求保持默认 |
ll_sfc_is_dqs_enabled() |
查询 DQS 状态 |
ll_sfc_set_chip_select() |
设置 chip select 编码,默认 00 |
ll_sfc_get_chip_select() |
读取 chip select 编码 |
Device 1 和 TRAN_CFG1
| API | 用途 |
|---|---|
ll_sfc_get_dev1_status() |
读取 Device 1 实时状态 |
ll_sfc_get_tran_cfg1() |
读取一个 phase 的 TRAN_CFG1 |
ll_sfc_set_tran_cfg1() |
覆盖写入一个 phase 的 TRAN_CFG1 |
ll_sfc_set_tran_cfg1_data_endian() |
设置 phase 数据端序 |
ll_sfc_is_tran_cfg1_data_msb_first() |
查询数据是否 MSB-first |
ll_sfc_set_tran_cfg1_word_unit() |
设置 phase word unit |
ll_sfc_get_tran_cfg1_word_unit() |
读取 phase word unit |
ll_sfc_set_tran_cfg1_transfer_mode() |
设置 phase SPI line mode |
ll_sfc_get_tran_cfg1_transfer_mode() |
读取 phase SPI line mode |
CDT 和数据端口
| API | 用途 |
|---|---|
ll_sfc_get_cdt_word() |
读取指定 CDT word |
ll_sfc_set_cdt_word() |
写入指定 CDT word |
ll_sfc_read_data() |
从 CPU 数据端口读 32-bit word |
ll_sfc_write_data() |
向 CPU 数据端口写 32-bit word |
4.2 通用约定
- 除
ll_sfc_is_instance()外,所有接口的第一个参数都是sfc_regs_t *sfcx,且必须指向SFC。LL 层不解引用前检查空指针,也不验证实例。 void接口只表示写操作已发出,不表示硬件接受了配置;bool接口返回当前采样到的一个状态条件;uint32_t接口返回原始寄存器或字段值。- 参数类型是
uint8_t、uint16_t或uint32_t不代表字段范围已校验。调用方必须按 3.2 节和寄存器规格截断或验证。 - 字段 setter 使用读改写;如果调用者与 ISR 共享同一寄存器,必须保证读改写原子。
- 全寄存器 setter 会覆盖整个寄存器;不要把多个字段简单的 OR 值写入,除非该值已经包含所有需要保留的位。
- 所有
is_*接口只读取当前状态,不清除状态。状态和中断标志通过SCR清除。 - LL 层没有错误码、超时和重试。应用层若需要
error_code_t,应在 LL 调用外层自行检查并返回。
4.3 实例检查 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_is_instance(sfcx) |
const sfc_regs_t *sfcx |
bool |
仅当 sfcx == SFC 时返回 true;不访问寄存器 |
4.4 GLB0 API
以下接口访问 GLB0。ll_sfc_set_glb0() 覆盖完整寄存器,其余 setter 只修改对应字段并保留其它位。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_glb0(sfcx) |
— | uint32_t |
读取完整 GLB0 |
ll_sfc_set_glb0(sfcx, value) |
uint32_t value |
void |
覆盖写 GLB0;调用方负责保留有效字段和保留位 |
ll_sfc_set_poll_time(sfcx, poll_time) |
uint16_t poll_time |
void |
写 POLL_TIME[31:16],轮询间隔计数 |
ll_sfc_get_poll_time(sfcx) |
— | uint32_t |
返回 POLL_TIME |
ll_sfc_enable_dma_descriptor(sfcx) |
— | void |
置 DES_EN,DMA 使用 DES_ADDR |
ll_sfc_disable_dma_descriptor(sfcx) |
— | void |
清 DES_EN |
ll_sfc_is_dma_descriptor_enabled(sfcx) |
— | bool |
查询 DES_EN |
ll_sfc_enable_cdt(sfcx) |
— | void |
置 CDT_EN |
ll_sfc_disable_cdt(sfcx) |
— | void |
清 CDT_EN |
ll_sfc_is_cdt_enabled(sfcx) |
— | bool |
查询 CDT_EN |
ll_sfc_set_transfer_direction(sfcx, direction) |
uint8_t direction,0/1 |
void |
写 TRAN_DIR;0=读 Flash,1=写 Flash |
ll_sfc_is_write_transfer(sfcx) |
— | bool |
TRAN_DIR=1 时返回 true |
ll_sfc_set_fifo_threshold(sfcx, threshold) |
uint8_t threshold,1..63 |
void |
写 THRESHOLD[12:7],单位 word;0 无效 |
ll_sfc_get_fifo_threshold(sfcx) |
— | uint32_t |
返回 FIFO 阈值 |
ll_sfc_set_operation_mode(sfcx, mode) |
uint8_t mode,0/1 |
void |
0=CPU FIFO,1=DMA |
ll_sfc_is_dma_operation_mode(sfcx) |
— | bool |
OP_MODE=1 时返回 true |
ll_sfc_set_phase_count(sfcx, phase_count) |
uint8_t phase_count,1..6 |
void |
写 Direct 模式 phase 数;0 无效 |
ll_sfc_get_phase_count(sfcx) |
— | uint32_t |
返回 PHASE_NUM |
ll_sfc_enable_write_protect(sfcx) |
— | void |
置 WP_EN |
ll_sfc_disable_write_protect(sfcx) |
— | void |
清 WP_EN |
ll_sfc_is_write_protect_enabled(sfcx) |
— | bool |
查询 WP_EN |
ll_sfc_set_burst_mode(sfcx, burst_mode) |
uint8_t burst_mode,1..3 |
void |
01=8 beat,10=16 beat,11=32 beat;00 保留 |
ll_sfc_get_burst_mode(sfcx) |
— | uint32_t |
返回 BURST_MD |
4.5 DEV_CONF API
以下接口访问 DEV_CONF。除完整寄存器 get/set 外,字段 helper 会保留未涉及的配置。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_dev_conf(sfcx) |
— | uint32_t |
读取完整 DEV_CONF |
ll_sfc_set_dev_conf(sfcx, value) |
uint32_t value |
void |
覆盖写完整 DEV_CONF |
ll_sfc_set_status_endian(sfcx, endian) |
uint8_t endian,0/1 |
void |
状态数据 LSB/MSB-first |
ll_sfc_is_status_msb_first(sfcx) |
— | bool |
查询状态是否 MSB-first |
ll_sfc_set_sample_delay(sfcx, delay) |
uint8_t delay,0..31 |
void |
写 SMP_DELAY[20:16] |
ll_sfc_get_sample_delay(sfcx) |
— | uint32_t |
返回采样延时编码 |
ll_sfc_set_command_type(sfcx, command_type) |
uint8_t command_type,0/1 |
void |
0=1 字节命令,1=2 字节命令 |
ll_sfc_is_two_byte_command(sfcx) |
— | bool |
查询是否 2 字节命令 |
ll_sfc_set_status_type(sfcx, status_type) |
uint8_t status_type,0..3 |
void |
轮询状态 1/2/3/4 字节 |
ll_sfc_get_status_type(sfcx) |
— | uint32_t |
返回状态宽度编码 |
ll_sfc_set_ce_hold_time(sfcx, hold) |
uint8_t hold,0..3 |
void |
CE hold:0.5/1/1.5/2 cycle |
ll_sfc_get_ce_hold_time(sfcx) |
— | uint32_t |
返回 CE hold 编码 |
ll_sfc_set_ce_setup_time(sfcx, setup) |
uint8_t setup,0..3 |
void |
CE setup:0.5/1/1.5/2 cycle |
ll_sfc_get_ce_setup_time(sfcx) |
— | uint32_t |
返回 CE setup 编码 |
ll_sfc_set_ce_deselect_interval(sfcx, interval) |
uint8_t interval,0..15 |
void |
CE deselect:0.5..8 cycle |
ll_sfc_get_ce_deselect_interval(sfcx) |
— | uint32_t |
返回 CE deselect 编码 |
ll_sfc_set_sample_edge(sfcx, edge) |
uint8_t edge,0/1 |
void |
0=奇数边沿,1=偶数边沿 |
ll_sfc_is_sampled_on_even_edge(sfcx) |
— | bool |
查询是否偶数边沿采样 |
ll_sfc_set_clock_polarity(sfcx, polarity) |
uint8_t polarity,0/1 |
void |
0=高有效,1=低有效 |
ll_sfc_is_clock_active_low(sfcx) |
— | bool |
查询 SCLK 是否低有效 |
ll_sfc_set_ce_inactive_level(sfcx, level) |
uint8_t level,0/1 |
void |
设置 CE 无效电平低/高 |
ll_sfc_is_ce_inactive_high(sfcx) |
— | bool |
查询 CE 无效电平是否为高 |
ll_sfc_set_hold_inactive_level(sfcx, level) |
uint8_t level,0/1 |
void |
设置 HOLD 无效电平低/高 |
ll_sfc_is_hold_inactive_high(sfcx) |
— | bool |
查询 HOLD 无效电平是否为高 |
ll_sfc_set_wp_inactive_level(sfcx, level) |
uint8_t level,0/1 |
void |
设置 WP 无效电平低/高 |
ll_sfc_is_wp_inactive_high(sfcx) |
— | bool |
查询 WP 无效电平是否为高 |
4.6 设备状态 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_set_dev_status_expect(sfcx, value) |
uint32_t value |
void |
覆盖写 DEV_STA_EXP |
ll_sfc_get_dev_status_expect(sfcx) |
— | uint32_t |
读取 DEV_STA_EXP |
ll_sfc_get_dev0_status(sfcx) |
— | uint32_t |
读取 DEV0_STA_RT 实时值 |
ll_sfc_set_dev_status_mask(sfcx, mask) |
uint32_t mask |
void |
覆盖写 DEV_STA_MSK |
ll_sfc_get_dev_status_mask(sfcx) |
— | uint32_t |
读取 DEV_STA_MSK |
4.7 TRAN_CFG0 API
以下接口的 phase 范围均为 0..5。ll_sfc_set_tran_cfg0() 覆盖一个 phase 的完整寄存器,字段 setter 只修改目标字段。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_tran_cfg0(sfcx, phase) |
uint8_t phase |
uint32_t |
读取 TRAN_CFG0[phase] |
ll_sfc_set_tran_cfg0(sfcx, phase, value) |
uint8_t phase,uint32_t value |
void |
覆盖对应 phase 的完整配置 |
ll_sfc_set_tran_cfg0_clock_mode(sfcx, phase, clock_mode) |
uint8_t clock_mode |
void |
仅 SDR 000 有定义,保持默认 |
ll_sfc_get_tran_cfg0_clock_mode(sfcx, phase) |
— | uint32_t |
读取 CLK_MODE |
ll_sfc_set_tran_cfg0_address_width(sfcx, phase, address_width) |
uint8_t address_width,0..7 |
void |
0 无地址;1..5 为 1..5 字节;6/7 为 6 字节 |
ll_sfc_get_tran_cfg0_address_width(sfcx, phase) |
— | uint32_t |
读取地址宽度编码 |
ll_sfc_enable_tran_cfg0_poll(sfcx, phase) |
— | void |
置 POLL_EN;优先于方向和 DATA_EN |
ll_sfc_disable_tran_cfg0_poll(sfcx, phase) |
— | void |
清 POLL_EN |
ll_sfc_is_tran_cfg0_poll_enabled(sfcx, phase) |
— | bool |
查询该 phase 轮询使能 |
ll_sfc_enable_tran_cfg0_command(sfcx, phase) |
— | void |
置 CMD_EN |
ll_sfc_disable_tran_cfg0_command(sfcx, phase) |
— | void |
清 CMD_EN |
ll_sfc_is_tran_cfg0_command_enabled(sfcx, phase) |
— | bool |
查询命令 phase 使能 |
ll_sfc_set_tran_cfg0_phase_format(sfcx, phase, format) |
uint8_t format,0/1 |
void |
选择硬件 phase format 0/1 |
ll_sfc_is_tran_cfg0_format1(sfcx, phase) |
— | bool |
查询是否 format 1 |
ll_sfc_set_tran_cfg0_dummy_cycles(sfcx, phase, dummy_cycles) |
uint8_t dummy_cycles,0..63 |
void |
写 DMY_BITS |
ll_sfc_get_tran_cfg0_dummy_cycles(sfcx, phase) |
— | uint32_t |
读取 dummy cycle 数 |
ll_sfc_enable_tran_cfg0_data(sfcx, phase) |
— | void |
置 DATA_EN |
ll_sfc_disable_tran_cfg0_data(sfcx, phase) |
— | void |
清 DATA_EN |
ll_sfc_is_tran_cfg0_data_enabled(sfcx, phase) |
— | bool |
查询数据 phase 使能 |
ll_sfc_set_tran_cfg0_command(sfcx, phase, command) |
uint16_t command |
void |
写 CMD[15:0];实际宽度由 CMD_TYPE 决定 |
ll_sfc_get_tran_cfg0_command(sfcx, phase) |
— | uint32_t |
读取命令值 |
4.8 传输长度和地址 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_set_transfer_length(sfcx, length) |
uint32_t length |
void |
写 TRAN_LEN,单位为字节;0 表示无数据 |
ll_sfc_get_transfer_length(sfcx) |
— | uint32_t |
读取 TRAN_LEN |
ll_sfc_set_device_address(sfcx, phase, address) |
uint8_t phase,uint32_t address |
void |
写 Direct phase 地址低 32 位 |
ll_sfc_get_device_address(sfcx, phase) |
uint8_t phase |
uint32_t |
读 Direct phase 地址低 32 位 |
ll_sfc_set_device_address_plus(sfcx, phase, address) |
uint8_t phase,uint32_t address,建议 0..0xFFFF |
void |
写 Direct phase 地址高 16 位;寄存器只有低 16 位有效 |
ll_sfc_get_device_address_plus(sfcx, phase) |
uint8_t phase |
uint32_t |
读取地址高位,只有低 16 位有意义 |
ll_sfc_set_memory_address(sfcx, address) |
uint32_t address |
void |
写非 descriptor DMA 内存地址 |
ll_sfc_get_memory_address(sfcx) |
— | uint32_t |
读取非 descriptor DMA 内存地址 |
4.9 触发 API
TRIG 是只写寄存器;三个接口各写一个触发位,不返回执行结果,也不等待硬件状态变化。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_trigger_start(sfcx) |
— | void |
写 TRIG.START=1 启动当前配置的事务;调用前应确认 WORKING=0 |
ll_sfc_trigger_stop(sfcx) |
— | void |
写 TRIG.STOP=1 终止传输;不会自动清 FIFO/状态 |
ll_sfc_trigger_fifo_flush(sfcx) |
— | void |
写 TRIG.FLUSH=1 复位 FIFO |
4.10 SR 状态 API
SR 为只读。所有查询都直接反映采样时刻的硬件状态,不改变任何标志。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_status(sfcx) |
— | uint32_t |
读取完整 SR |
ll_sfc_get_fifo_word_count(sfcx) |
— | uint32_t |
读取 FIFONUM[22:16],单位 word |
ll_sfc_is_working(sfcx) |
— | bool |
查询 WORKING |
ll_sfc_get_busy_state(sfcx) |
— | uint32_t |
读取 BUSY[6:5] 编码 |
ll_sfc_is_end(sfcx) |
— | bool |
查询 END |
ll_sfc_is_tran_req(sfcx) |
— | bool |
查询发送 FIFO 请求 TRAN_REQ |
ll_sfc_is_rece_req(sfcx) |
— | bool |
查询接收 FIFO 请求 RECE_REQ |
ll_sfc_is_over(sfcx) |
— | bool |
查询 OVER |
ll_sfc_is_under(sfcx) |
— | bool |
查询 UNDER |
ll_sfc_get_pending_status(sfcx) |
— | uint32_t |
返回 SR & SFC_SR_INT_MASK,只含五位可清除事件 |
4.11 SCR 状态清除 API
SCR 为只写、写 1 清零。除 ll_sfc_clear_status_bits() 外,其余接口每次只写一个清零位,不能通过返回值组合多个清零请求。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_clear_status_bits(sfcx, mask) |
uint32_t mask,使用 SFC_SCR_CLR_* |
void |
按 mask 同时清除多个状态/中断标志 |
ll_sfc_clear_end_status(sfcx) |
— | void |
清除 END |
ll_sfc_clear_tran_req_status(sfcx) |
— | void |
清除 TRAN_REQ/TREQ |
ll_sfc_clear_rece_req_status(sfcx) |
— | void |
清除 RECE_REQ/RREQ |
ll_sfc_clear_over_status(sfcx) |
— | void |
清除 OVER |
ll_sfc_clear_under_status(sfcx) |
— | void |
清除 UNDER |
ll_sfc_clear_all_status_bits(sfcx) |
— | void |
写 SFC_SCR_CLR_ALL,清除全部可清除状态 |
4.12 INTC 中断控制 API
INTC 的 1 表示屏蔽、0 表示开放。字段 helper 只修改目标位;ll_sfc_set_interrupt_mask() 和两个 all 接口会覆盖整个寄存器。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_set_interrupt_mask(sfcx, mask) |
uint32_t mask |
void |
覆盖写完整 INTC,1=masked |
ll_sfc_get_interrupt_mask(sfcx) |
— | uint32_t |
读取完整 INTC |
ll_sfc_is_end_interrupt_masked(sfcx) |
— | bool |
查询 END 中断屏蔽位 |
ll_sfc_is_treq_interrupt_masked(sfcx) |
— | bool |
查询 TREQ 中断屏蔽位 |
ll_sfc_is_rreq_interrupt_masked(sfcx) |
— | bool |
查询 RREQ 中断屏蔽位 |
ll_sfc_is_over_interrupt_masked(sfcx) |
— | bool |
查询 OVER 中断屏蔽位 |
ll_sfc_is_undr_interrupt_masked(sfcx) |
— | bool |
查询 UNDER 中断屏蔽位 |
ll_sfc_mask_interrupts(sfcx, mask) |
uint32_t mask,使用 SFC_INTC_MASK_* |
void |
按 mask 置位屏蔽 |
ll_sfc_mask_end_interrupt(sfcx) |
— | void |
屏蔽 END |
ll_sfc_mask_treq_interrupt(sfcx) |
— | void |
屏蔽 TREQ |
ll_sfc_mask_rreq_interrupt(sfcx) |
— | void |
屏蔽 RREQ |
ll_sfc_mask_over_interrupt(sfcx) |
— | void |
屏蔽 OVER |
ll_sfc_mask_undr_interrupt(sfcx) |
— | void |
屏蔽 UNDER |
ll_sfc_unmask_interrupts(sfcx, mask) |
uint32_t mask,使用 SFC_INTC_MASK_* |
void |
按 mask 清除屏蔽 |
ll_sfc_unmask_end_interrupt(sfcx) |
— | void |
开放 END |
ll_sfc_unmask_treq_interrupt(sfcx) |
— | void |
开放 TREQ |
ll_sfc_unmask_rreq_interrupt(sfcx) |
— | void |
开放 RREQ |
ll_sfc_unmask_over_interrupt(sfcx) |
— | void |
开放 OVER |
ll_sfc_unmask_undr_interrupt(sfcx) |
— | void |
开放 UNDER |
ll_sfc_mask_all_interrupts(sfcx) |
— | void |
覆盖写 SFC_INTC_MASK_ALL |
ll_sfc_unmask_all_interrupts(sfcx) |
— | void |
覆盖写 0,开放全部事件 |
4.13 FSM 调试 API
以下接口只读,不修改控制器状态。返回值是内部状态机编码,不应作为稳定软件 ABI 使用。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_fsm(sfcx) |
— | uint32_t |
读取完整 FSM |
ll_sfc_get_ahb_fsm(sfcx) |
— | uint32_t |
读取 FSM_AHB[19:16] |
ll_sfc_get_spi_fsm(sfcx) |
— | uint32_t |
读取 FSM_SPI[15:8] |
ll_sfc_get_dmac_fsm(sfcx) |
— | uint32_t |
读取 FSM_DMAC[7:4] |
ll_sfc_get_rmc_fsm(sfcx) |
— | uint32_t |
读取 FSM_RMC[2:0] |
4.14 CGE 时钟门控 API
“enable”表示开启门控功能,不是开启总线时钟。ll_sfc_set_clock_gate() 覆盖整个寄存器;写 0 会关闭全部门控功能。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_clock_gate(sfcx) |
— | uint32_t |
读取完整 CGE |
ll_sfc_set_clock_gate(sfcx, value) |
uint32_t value |
void |
覆盖写 CGE;0=关闭全部门控功能 |
ll_sfc_enable_flow_clock_gate(sfcx) |
— | void |
置 FLOW_EN |
ll_sfc_disable_flow_clock_gate(sfcx) |
— | void |
清 FLOW_EN |
ll_sfc_is_flow_clock_gate_enabled(sfcx) |
— | bool |
查询 FLOW_EN |
ll_sfc_enable_dma_clock_gate(sfcx) |
— | void |
置 DMA_EN |
ll_sfc_disable_dma_clock_gate(sfcx) |
— | void |
清 DMA_EN |
ll_sfc_is_dma_clock_gate_enabled(sfcx) |
— | bool |
查询 DMA_EN |
ll_sfc_enable_rmc_clock_gate(sfcx) |
— | void |
置 RMC_EN |
ll_sfc_disable_rmc_clock_gate(sfcx) |
— | void |
清 RMC_EN |
ll_sfc_is_rmc_clock_gate_enabled(sfcx) |
— | bool |
查询 RMC_EN |
ll_sfc_enable_spi_clock_gate(sfcx) |
— | void |
置 SPI_EN |
ll_sfc_disable_spi_clock_gate(sfcx) |
— | void |
清 SPI_EN |
ll_sfc_is_spi_clock_gate_enabled(sfcx) |
— | bool |
查询 SPI_EN |
ll_sfc_enable_reg_clock_gate(sfcx) |
— | void |
置 REG_EN |
ll_sfc_disable_reg_clock_gate(sfcx) |
— | void |
清 REG_EN |
ll_sfc_is_reg_clock_gate_enabled(sfcx) |
— | bool |
查询 REG_EN |
4.15 CMD_IDX API
以下接口控制下一次 CDT 执行。索引范围 0..63;入口有效性和链路长度由调用方保证。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_cmd_idx_register(sfcx) |
— | uint32_t |
读取完整 CMD_IDX |
ll_sfc_set_cmd_idx_register(sfcx, value) |
uint32_t value |
void |
覆盖写完整 CMD_IDX |
ll_sfc_enable_cmd_idx_cdt_data(sfcx) |
— | void |
置 CDT_DATA_EN |
ll_sfc_disable_cmd_idx_cdt_data(sfcx) |
— | void |
清 CDT_DATA_EN |
ll_sfc_is_cmd_idx_cdt_data_enabled(sfcx) |
— | bool |
查询 CDT 数据使能 |
ll_sfc_set_cmd_idx_transfer_direction(sfcx, direction) |
uint8_t direction,0/1 |
void |
0=读 Flash,1=写 Flash |
ll_sfc_is_cmd_idx_write_transfer(sfcx) |
— | bool |
查询 CDT 方向是否为写 |
ll_sfc_set_cmd_idx_index(sfcx, index) |
uint8_t index,0..63 |
void |
写起始 CDT entry 索引 |
ll_sfc_get_cmd_idx_index(sfcx) |
— | uint32_t |
读取起始 entry 索引 |
4.16 CDT 和 DMA 地址 API
COL_ADDR、ROW_ADDR、STA_ADDR0/1 只在 CDT 模式使用;DES_ADDR 只在 DMA descriptor 模式使用。所有地址寄存器都是完整 32-bit 写,LL 层不做地址转换。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_set_column_address(sfcx, address) |
uint32_t address |
void |
写 CDT 列地址 |
ll_sfc_get_column_address(sfcx) |
— | uint32_t |
读 CDT 列地址 |
ll_sfc_set_row_address(sfcx, address) |
uint32_t address |
void |
写 CDT 行地址 |
ll_sfc_get_row_address(sfcx) |
— | uint32_t |
读 CDT 行地址 |
ll_sfc_set_status_address0(sfcx, address) |
uint32_t address |
void |
写 CDT 状态地址 0 |
ll_sfc_get_status_address0(sfcx) |
— | uint32_t |
读 CDT 状态地址 0 |
ll_sfc_set_status_address1(sfcx, address) |
uint32_t address |
void |
写 CDT 状态地址 1 |
ll_sfc_get_status_address1(sfcx) |
— | uint32_t |
读 CDT 状态地址 1 |
ll_sfc_set_descriptor_address(sfcx, address) |
uint32_t address |
void |
写 DMA descriptor 地址 |
ll_sfc_get_descriptor_address(sfcx) |
— | uint32_t |
读 DMA descriptor 地址 |
4.17 GLB1、XIP 和软复位 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_glb1(sfcx) |
— | uint32_t |
读取完整 GLB1 |
ll_sfc_set_glb1(sfcx, value) |
uint32_t value |
void |
覆盖写完整 GLB1 |
ll_sfc_set_reset(sfcx, reset_cycles) |
uint8_t reset_cycles,0..63 |
void |
写 RESET[20:15] 并启动软复位;0 为自动释放 |
ll_sfc_get_reset(sfcx) |
— | uint32_t |
读取当前复位倒计时 |
ll_sfc_set_tran_dir_select(sfcx, value) |
uint8_t value |
void |
写 TRAN_DIR_SEL;应保持默认 0 |
ll_sfc_get_tran_dir_select(sfcx) |
— | uint32_t |
读取 TRAN_DIR_SEL |
ll_sfc_set_mode_bit(sfcx, mode_bit) |
uint8_t mode_bit |
void |
写 XIP 连续读 mode bits |
ll_sfc_get_mode_bit(sfcx) |
— | uint32_t |
读取 XIP mode bits |
ll_sfc_enable_xip(sfcx) |
— | void |
置 XIP_EN |
ll_sfc_disable_xip(sfcx) |
— | void |
清 XIP_EN |
ll_sfc_is_xip_enabled(sfcx) |
— | bool |
查询 XIP 状态 |
ll_sfc_enable_dqs(sfcx) |
— | void |
置 DQS_EN;规格要求保持默认 |
ll_sfc_disable_dqs(sfcx) |
— | void |
清 DQS_EN;规格要求保持默认 |
ll_sfc_is_dqs_enabled(sfcx) |
— | bool |
查询 DQS 状态 |
ll_sfc_set_chip_select(sfcx, chip_select) |
uint8_t chip_select |
void |
写 CHIP_SEL;仅默认 00 有效,其它值保留 |
ll_sfc_get_chip_select(sfcx) |
— | uint32_t |
读取 CHIP_SEL |
4.18 Device 1 和 TRAN_CFG1 API
phase 范围 0..5。TRAN_CFG1 的 transfer mode 编码为 0、1、2、3、5、6、7,4 保留。
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_dev1_status(sfcx) |
— | uint32_t |
读取 DEV1_STA_RT |
ll_sfc_get_tran_cfg1(sfcx, phase) |
uint8_t phase |
uint32_t |
读取 TRAN_CFG1[phase] |
ll_sfc_set_tran_cfg1(sfcx, phase, value) |
uint8_t phase,uint32_t value |
void |
覆盖对应 phase 的完整 TRAN_CFG1 |
ll_sfc_set_tran_cfg1_data_endian(sfcx, phase, endian) |
uint8_t endian,0/1 |
void |
0=LSB-first,1=MSB-first |
ll_sfc_is_tran_cfg1_data_msb_first(sfcx, phase) |
— | bool |
查询 phase 数据是否 MSB-first |
ll_sfc_set_tran_cfg1_word_unit(sfcx, phase, word_unit) |
uint8_t word_unit,0..2 |
void |
1/2/4 字节数据单元;3 保留 |
ll_sfc_get_tran_cfg1_word_unit(sfcx, phase) |
— | uint32_t |
读取 word unit |
ll_sfc_set_tran_cfg1_transfer_mode(sfcx, phase, transfer_mode) |
uint8_t transfer_mode,0..3 或 5..7 |
void |
标准/dual/quad 等 SPI line mode |
ll_sfc_get_tran_cfg1_transfer_mode(sfcx, phase) |
— | uint32_t |
读取 transfer mode |
4.19 CDT 和数据端口 API
| API | 参数 | 返回 | 说明 |
|---|---|---|---|
ll_sfc_get_cdt_word(sfcx, index) |
uint8_t index,0..255 |
uint32_t |
读取一个 CDT word |
ll_sfc_set_cdt_word(sfcx, index, value) |
uint8_t index,0..255;uint32_t value |
void |
覆盖写一个 CDT word;index 不是 entry 编号 |
ll_sfc_read_data(sfcx) |
— | uint32_t |
从 DR 读取一个 CPU FIFO word |
ll_sfc_write_data(sfcx, data) |
uint32_t data |
void |
向 DR 写入一个 CPU FIFO word |
ll_sfc_read_data() 不检查 FIFONUM,空 FIFO 读可能返回无效数据;ll_sfc_write_data() 不检查剩余空间,满 FIFO 写可能导致 overflow。批量访问时应先读取 FIFONUM,并自行实现超时和错误处理。
5. 典型完整示例
以下示例用于说明 LL 调用顺序,不是可直接量产的 Flash 驱动。时钟、pinmux、Flash 上电/复位、命令、地址宽度、dummy、采样、CE timing、XIP mode bits 和超时时间必须按实际器件与板卡修改。
5.1 Direct 模式 CPU FIFO 读取 JEDEC ID
示例使用标准 RDID(0x9F) 读取 3 字节 ID。TRAN_LEN=3,CPU FIFO 读取一个 32-bit word,再按 LSB-first 提取低 3 字节。这里假设 SFC 时钟、pinmux 和 Flash 已就绪,示例中的采样与 CE 参数只是占位值。
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "im_error.h"
#include "im_time.h"
#include "ll_sfc.h"
#define APP_SFC_TIMEOUT_MS (100U)
#define APP_SFC_ERROR_MASK (SFC_SR_OVER_MASK | SFC_SR_UNDER_MASK)
static void app_sfc_abort(void)
{
ll_sfc_trigger_stop(SFC);
ll_sfc_trigger_fifo_flush(SFC);
ll_sfc_clear_all_status_bits(SFC);
}
static void app_sfc_common_config(void)
{
ll_sfc_set_poll_time(SFC, 100U);
ll_sfc_set_status_endian(SFC, 0U);
ll_sfc_set_sample_delay(SFC, 0U);
ll_sfc_set_command_type(SFC, 0U);
ll_sfc_set_status_type(SFC, 0U);
ll_sfc_set_ce_hold_time(SFC, 3U);
ll_sfc_set_ce_setup_time(SFC, 3U);
ll_sfc_set_ce_deselect_interval(SFC, 3U);
ll_sfc_set_sample_edge(SFC, 0U);
ll_sfc_set_clock_polarity(SFC, 0U);
ll_sfc_set_ce_inactive_level(SFC, 1U);
ll_sfc_set_hold_inactive_level(SFC, 1U);
ll_sfc_set_wp_inactive_level(SFC, 1U);
}
static error_code_t app_sfc_read_jedec_id(uint8_t id[3])
{
uint32_t word = 0U;
uint32_t start_ms;
bool got_word = false;
if (id == NULL) {
return IM_ERR_INVALID_PARAM;
}
if (!ll_sfc_is_instance(SFC) ||
ll_sfc_is_working(SFC) ||
ll_sfc_is_xip_enabled(SFC)) {
return IM_ERR_BUSY;
}
app_sfc_common_config();
ll_sfc_disable_cdt(SFC);
ll_sfc_disable_dma_descriptor(SFC);
ll_sfc_set_operation_mode(SFC, 0U);
ll_sfc_set_transfer_direction(SFC, 0U);
ll_sfc_set_phase_count(SFC, 1U);
ll_sfc_set_fifo_threshold(SFC, 1U);
ll_sfc_set_tran_cfg0(SFC, 0U, 0U);
ll_sfc_set_tran_cfg1(SFC, 0U, 0U);
ll_sfc_set_tran_cfg0_address_width(SFC, 0U, 0U);
ll_sfc_disable_tran_cfg0_poll(SFC, 0U);
ll_sfc_enable_tran_cfg0_command(SFC, 0U);
ll_sfc_set_tran_cfg0_phase_format(SFC, 0U, 0U);
ll_sfc_set_tran_cfg0_dummy_cycles(SFC, 0U, 0U);
ll_sfc_enable_tran_cfg0_data(SFC, 0U);
ll_sfc_set_tran_cfg0_command(SFC, 0U, 0x9FU);
ll_sfc_set_tran_cfg1_data_endian(SFC, 0U, 0U);
ll_sfc_set_tran_cfg1_word_unit(SFC, 0U, 0U);
ll_sfc_set_tran_cfg1_transfer_mode(SFC, 0U, 0U);
ll_sfc_set_transfer_length(SFC, 3U);
ll_sfc_clear_all_status_bits(SFC);
ll_sfc_trigger_fifo_flush(SFC);
ll_sfc_trigger_start(SFC);
start_ms = sys_time_get_ms();
for (;;) {
uint32_t pending = ll_sfc_get_pending_status(SFC);
if ((pending & APP_SFC_ERROR_MASK) != 0U) {
app_sfc_abort();
return IM_ERR_HARDWARE;
}
if (!got_word && (ll_sfc_get_fifo_word_count(SFC) != 0U)) {
word = ll_sfc_read_data(SFC);
got_word = true;
ll_sfc_clear_rece_req_status(SFC);
}
if ((pending & SFC_SR_END_MASK) != 0U) {
if (!got_word && (ll_sfc_get_fifo_word_count(SFC) != 0U)) {
word = ll_sfc_read_data(SFC);
got_word = true;
}
if (!got_word) {
app_sfc_abort();
return IM_ERR_HARDWARE;
}
ll_sfc_clear_end_status(SFC);
id[0] = (uint8_t)(word & 0xFFU);
id[1] = (uint8_t)((word >> 8U) & 0xFFU);
id[2] = (uint8_t)((word >> 16U) & 0xFFU);
return IM_OK;
}
if ((uint32_t)(sys_time_get_ms() - start_ms) >= APP_SFC_TIMEOUT_MS) {
app_sfc_abort();
return IM_ERR_TIMEOUT;
}
}
}
说明:
- 示例按轮询方式工作,没有配置或开放 SFC 中断。若使用 IRQ,必须同时处理请求清除和 END 清除,避免轮询路径与中断路径重复读 FIFO。
transfer_length=3表示 Flash 数据总长度;CPU FIFO 只保证一个 32-bit word 可读。若实际 word unit、端序或 FIFO 打包方式不同,应按协议调整提取顺序。ll_sfc_get_pending_status()的读取值可能早于 FIFO 状态变化,因此 END 分支再次检查FIFONUM。- 示例没有轮询 Flash 内部 busy;
RDID通常可直接执行,擦写命令仍需要 RDSR 或 CDT polling phase。 - 采样延时 0 和 CE timing 3 只是示例值,必须在目标板卡上验证。
5.2 CDT 构建擦除命令链
下面的链由 WREN(0x06)、SECTOR_ERASE(0x20) 和 RDSR(0x05) 硬件轮询组成。轮询等待 WIP(状态 bit 0)清零;命令、地址和状态位必须按实际 NOR Flash 数据手册确认。示例沿用 5.1 的 app_sfc_common_config() 和 app_sfc_abort()。
#include <stdbool.h>
#include "im_error.h"
#include "im_time.h"
#include "ll_sfc.h"
#define APP_SFC_ERASE_TIMEOUT_MS (3000U)
#define APP_SFC_ERROR_MASK (SFC_SR_OVER_MASK | SFC_SR_UNDER_MASK)
static void app_sfc_cdt_entry(uint8_t entry,
bool link_next,
uint8_t address_kind,
uint8_t address_width,
uint16_t command,
bool data_enabled,
bool poll_enabled,
uint32_t status_expect,
uint32_t status_mask)
{
uint8_t word_index = (uint8_t)(entry * SFC_CDT_WORDS_PER_ENTRY);
uint32_t word0;
uint32_t word1;
word0 = (link_next ? SFC_CDT_WORD0_LINK_MASK : 0U) |
(0U << SFC_CDT_WORD0_ENDIAN_SHIFT) |
(0U << SFC_CDT_WORD0_WORD_UNIT_SHIFT) |
(0U << SFC_CDT_WORD0_TRAN_MODE_SHIFT) |
((uint32_t)address_kind << SFC_CDT_WORD0_ADDR_KIND_SHIFT);
word1 = (0U << SFC_CDT_WORD1_CLK_MODE_SHIFT) |
((uint32_t)address_width << SFC_CDT_WORD1_ADDR_WIDTH_SHIFT) |
((poll_enabled ? 1U : 0U) << SFC_CDT_WORD1_POLL_EN_SHIFT) |
(1U << SFC_CDT_WORD1_CMD_EN_SHIFT) |
(0U << SFC_CDT_WORD1_PHASE_FORMAT_SHIFT) |
(0U << SFC_CDT_WORD1_DMY_BITS_SHIFT) |
((data_enabled ? 1U : 0U) << SFC_CDT_WORD1_DATA_EN_SHIFT) |
((uint32_t)command & 0xFFFFU);
ll_sfc_set_cdt_word(SFC, word_index, word0);
ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 1U), word1);
ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 2U), status_expect);
ll_sfc_set_cdt_word(SFC, (uint8_t)(word_index + 3U), status_mask);
}
static error_code_t app_sfc_wait_cdt_end(uint32_t timeout_ms)
{
uint32_t start_ms = sys_time_get_ms();
for (;;) {
uint32_t pending = ll_sfc_get_pending_status(SFC);
if ((pending & APP_SFC_ERROR_MASK) != 0U) {
return IM_ERR_HARDWARE;
}
if ((pending & SFC_SR_END_MASK) != 0U) {
ll_sfc_clear_end_status(SFC);
return IM_OK;
}
if ((uint32_t)(sys_time_get_ms() - start_ms) >= timeout_ms) {
return IM_ERR_TIMEOUT;
}
}
}
static error_code_t app_sfc_erase_sector(uint32_t flash_address)
{
error_code_t ret;
if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
return IM_ERR_BUSY;
}
app_sfc_common_config();
/* entry 0: WREN,无地址、无数据,继续下一项 */
app_sfc_cdt_entry(0U, true, 0U, 0U, 0x06U, false, false, 0U, 0U);
/* entry 1: SECTOR_ERASE,使用 ROW_ADDR 的 24-bit 地址,继续下一项 */
app_sfc_cdt_entry(1U, true, 1U, 3U, 0x20U, false, false, 0U, 0U);
/* entry 2: RDSR polling,等待 WIP(bit0)=0,命令链在此结束 */
app_sfc_cdt_entry(2U, false, 0U, 0U, 0x05U, false, true, 0U, 0x01U);
ll_sfc_set_column_address(SFC, 0U);
ll_sfc_set_row_address(SFC, flash_address);
ll_sfc_set_status_address0(SFC, 0U);
ll_sfc_set_status_address1(SFC, 0U);
ll_sfc_set_transfer_length(SFC, 0U);
ll_sfc_disable_dma_descriptor(SFC);
ll_sfc_set_operation_mode(SFC, 0U);
ll_sfc_disable_cmd_idx_cdt_data(SFC);
ll_sfc_set_cmd_idx_transfer_direction(SFC, 1U);
ll_sfc_set_cmd_idx_index(SFC, 0U);
ll_sfc_enable_cdt(SFC);
ll_sfc_clear_all_status_bits(SFC);
ll_sfc_trigger_fifo_flush(SFC);
ll_sfc_trigger_start(SFC);
ret = app_sfc_wait_cdt_end(APP_SFC_ERASE_TIMEOUT_MS);
if (ret != IM_OK) {
app_sfc_abort();
}
return ret;
}
说明:
ll_sfc_set_cdt_word()的 index 是 word 编号,不是 entry 编号;entry 2 从 index 8 开始。ROW_ADDR保存 24-bit 擦除地址;不同 Flash 的地址 phase 数量和ADDR_KIND可能不同。POLL_EN=1的 RDSR entry 使用status_expect=0、status_mask=0x01等待 WIP 清零;其他器件可能使用 busy=1 或不同状态位。- 最后一项
LINK=0。索引 63 不能再链接下一项,整条链和数据 phase 唯一性由调用方保证。 - 控制器 END 表示命令链执行完成;只有链中包含正确的 RDSR polling,才表示 Flash 内部擦除已经完成。
- 示例会破坏目标扇区数据,擦除地址、保护位、供电和时序必须由 Flash 驱动校验。
5.3 XIP 和软复位边界
以下代码只展示控制器侧寄存器顺序,不包含 Flash 连续读预置、XIP 地址映射、cache 属性或退出帧:
static error_code_t app_sfc_xip_enter(uint8_t mode_bits)
{
uint32_t glb1;
if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
return IM_ERR_BUSY;
}
/* 前置条件:连续读 phase、dummy 和采样已按 Flash 配置完成 */
glb1 = ll_sfc_get_glb1(SFC);
glb1 &= ~(SFC_GLB1_MODE_BIT_MASK |
SFC_GLB1_XIP_EN_MASK |
SFC_GLB1_CHIP_SEL_MASK);
glb1 |= ((uint32_t)mode_bits << SFC_GLB1_MODE_BIT_SHIFT);
ll_sfc_set_glb1(SFC, glb1);
ll_sfc_enable_xip(SFC);
return IM_OK;
}
static void app_sfc_xip_exit(void)
{
ll_sfc_disable_xip(SFC);
ll_sfc_set_mode_bit(SFC, 0U);
}
static error_code_t app_sfc_soft_reset(uint8_t reset_cycles)
{
if (ll_sfc_is_working(SFC) || ll_sfc_is_xip_enabled(SFC)) {
return IM_ERR_BUSY;
}
ll_sfc_set_reset(SFC, reset_cycles);
return IM_OK;
}
说明:
- XIP 映射地址由 SoC/板级决定,
ll_sfc.h不提供 XIP 窗口常量。进入 XIP 前必须由系统层建立可执行/可访问映射和 cache 策略。 ll_sfc_set_glb1()是覆盖写,示例先清除MODE_BIT、XIP_EN和CHIP_SEL再写入 mode bits;实际代码应保留其它有效位。- 规格要求
CHIP_SEL=00、DQS_EN和TRAN_DIR_SEL保持默认。除明确验证过的板级方案外,不要改变这些字段。 ll_sfc_set_reset()启动后硬件自动释放,不需要轮询RESET字段。复位后必须重新初始化DEV_CONF、phase、GLB0/GLB1、中断和地址;LL 层不会恢复旧配置。- 软件复位不处理外部 Flash、SFC 总线时钟或 pinmux,也不保证 Flash 内部状态已退出。
6. 使用约束和检查清单
6.1 关键约束
- LL 层只支持
SFC实例,所有地址按SFC_IOBASE=0x13440000计算。 - LL 层不校验指针、phase、CDT、地址宽度、FIFO 阈值或保留字段;非法值会直接写入硬件。
- LL 层没有
error_code_t、超时、重试、状态机或错误恢复,所有失败判断由调用方完成。 - 字段 setter 的读改写不带锁;任务和 IRQ 并发访问同一寄存器会丢写。
- 全寄存器 setter 会覆盖保留位和其它有效字段,除非调用方已经构造完整值。
- 不应在
SR.WORKING=1或GLB1.XIP_EN=1时修改 phase、CDT、传输长度、地址或 XIP 配置。 TRIG是只写寄存器,SCR是只写 W1C 寄存器,不能通过读回确认触发或清除是否完成。SR.FIFONUM、SR.END和 FIFO 请求是瞬时硬件状态;一次读取不能代表整个传输周期。TRAN_LEN是字节数,DR和 FIFO 是 32-bit word;数据打包、端序和 word unit 必须一致。- XIP 地址映射、cache 维护、Flash 连续读命令和退出流程不属于 LL 层。
- 软复位只复位 SFC 控制器,不处理 Flash、时钟或 pinmux;复位后必须重新配置。
FSM编码属于内部调试信息,不能作为稳定产品协议。
6.2 首次集成检查
- 已确认使用
SFC实例,且不存在第二个寄存器块指针被传给 LL API。 - SFC 总线时钟、门控时钟、pinmux 和 Flash 电源/复位已经由 BSP 正确配置。
- 当前没有任务、IRQ 或 DMA 与本次 LL 配置并发访问同一控制器。
- 每次启动前都确认
SR.WORKING=0,XIP 场景下确认已按流程关闭 XIP。 -
DEV_CONF的命令宽度、状态宽度、端序、采样延时/边沿、CE timing 和无效电平已按器件手册配置。 -
PHASE_NUM、TRAN_CFG0、TRAN_CFG1、DEV_ADDR和DEV_ADDR_PLUS与实际命令序列一致。 - 所有 phase index 位于 0..5,FIFO 阈值位于 1..63,address width、dummy 和 word unit 未使用保留值。
-
TRAN_LEN以字节为单位;无数据 phase 时为 0,CPU/DMA 数据通路与长度一致。 - CPU FIFO 路径在读写前检查
FIFONUM,并为空读、满写、OVER 和 UNDER 定义错误处理。 - DMA 路径已完成物理地址、对齐、连续性和 cache clean/invalidate;descriptor 地址符合硬件格式。
- CDT 每个 entry 的 word index 按
entry_index * 4计算,最后一项LINK=0,索引 63 不再链接。 - 启动前已清除旧状态和 FIFO,传输结束后按
END、OVER、UNDER处理并清理状态。 - XIP 使能前已建立连续读 phase、mode bits、地址映射和 cache 策略;关闭 XIP 后再重配控制器。
- 软复位后执行了完整的重新初始化,没有复用复位前的 LL 配置假设。
- 已通过逻辑分析仪、Flash 状态读取或回读测试验证真实时序,而不只依赖寄存器写入成功。