SPI 接口

DWM1001 SPI 接口使用 TLV 格式的数据. 用户可以使用 SPI 主模式的外部主机设备连接到 DWM 模块 SPI 接口,该接口以从模式运行. 最大 SPI 时钟频率为 8 MHz. (在 DWM1001 SPI 方案中,主机设备通过 TLV 请求与 DWM1001 通信. 完整的 TLV 请求通信流程包括以下步骤:

  1. 主机设备发送 TLV 请求;

  2. DWM1001 准备响应;

  3. 主机设备读取 SIZE(每个响应的长度)和 NUM(传输次数);

  4. 主机设备读取数据响应;

由于 SPI 采用全双工通信,当主机设备(作为 SPI 主设备)写入 x 字节时,它会向 DWM 模块(作为从设备)发送 x 字节,并同时接收 x 字节的假数据. 与读取类似,主机设备发送 x 个字节的假数据,并接收 x 个字节的数据作为响应. 在 DWM1001 SPI 方案中,虚字节是值为 0xFF 的八位字节.

../../../_images/image41.png

SPI 工作流程

如上图所示,DWM1001 SPI 线程在四个状态之间串行传输.

每个状态都会在特定事件发生时转移到下一个相应的状态.

  • SPI: Idle: 初始化后和每次成功数据传输/响应后的状态. 在此状态下,任何接收到的数据都被假定为 TLV 请求. 因此,在接收到任何数据时,固件中的 SPI 线程会将接收到的数据传递给通用 API. 通用 API 会解析 TLV 请求,并准备返回数据.

    • 等待事件:接收 TLV 请求.

    • 对事件采取行动 - 如果类型 == 0xFF:

  • SPI: 等待回调:DWM1001 SPI 等待通用 API 解析 TLV 请求并准备响应. 在此状态下,来自主机端的任何数据都将被忽略,并以 0x00 返回.

    • 等待事件:通用 API 调用 call_back()函数.

    • 事件的操作:

      • 将数据就绪 GPIO 引脚切换为高电平 - 详见 SPI 方案: 使用数据就绪 GPIO 引脚进行 TLV 通信.

      • 转到 “SPI:等待 READ SIZE/NUM”

  • SPI: 等待读取 SIZE/NUM:DWM1001 SPI 已准备好 SIZE 字节作为响应,将有 NUM 次传输. SIZE 和 NUM 共为 2 字节非零数据,即 SIZE/NUM. 它们表示要响应的数据或错误信息的总数(即 SIZE*NUM 字节). DWM1001 SPI 作为从设备,正在等待主机设备读取 SIZE/NUM 字节.

    • 等待事件:主机设备读取两个字节.

    • 事件的操作:

      • 回应 SIZE/NUM 字节.

      • 转到 ” SPI: 等待 READ DATA/ERR”.

  • SPI: 等待读取 DATA/ERR:DWM1001 SPI 已为 NUM 传输中的每次传输准备了 SIZE 字节的数据或错误信息,作为对 TLV 请求的响应,并正在等待主机设备读取.

    • 等待事件:主机设备进行 NUM 传输. 每次传输应为 SIZE 字节. 否则,数据可能会丢失.

    • 事件的操作:

      • 回应 DATA/ERR.

      • 将数据就绪 GPIO 引脚切换为低电平 - 详见 SPI 方案: 使用数据就绪 GPIO 引脚进行 TLV 通信. 转到 ” SPI: Idle “.

在 DWM1001 中,从 “SPI:在 DWM1001 中,从 “SPI: Idle”开始,遍历上述所有四种状态,然后返回 “SPI: Idle”表示完整的 TLV 请求通信流程. 在通信流程结束时,用户应该已经收到响应数据或错误信息.

下面的小节将举例说明几种不同的用法.


使用轮询进行 SPI TLV 通信

下图显示了主机设备向DWM模块写入/从DWM模块读取信息的正常通信流程:

  1. 主机设备以 TLV 格式发送请求.

  2. 主机设备读取 SIZE/NUM 字节,表示要完成的传输次数,以及每次传输中准备读取的字节数.

  1. 主机设备以 TLV 格式读取 SIZE 字节的数据响应.

../../../_images/image51.png

使用轮询进行 SPI TLV 通信

下图举例说明了主机设备写入 dwm_gpio_cfg_output TLV 请求,将引脚 13 设置为高电平(字节数组中的[0x28, 0x02, 0x0D, 0x01]),并从 DWM 模块读取响应.
传输和响应的通信流程包括
  1. 主机设备写入 dwm_gpio_cfg_output 命令,以 TLV 格式设置引脚 13 level HIGH,共 4 个字节:类型 = 0x28,长度 = 0x02,值 = 0x0D 0x01.


使用数据就绪 GPIO 引脚进行 SPI TLV 通信

用户可以设置 DWM1001 的中断 Data-Ready GPIO 引脚 (GPIO P0.26),以指示数据何时就绪,而无需主控器多次轮询检查响应状态. 设置数据就绪功能后,当没有数据要读取时,数据就绪 GPIO 引脚将被设置为低电平;当响应 SIZE/NUM 和数据就绪时,数据就绪 GPIO 引脚将在 “SPI.等待读取 SIZE/NUM” 和 “SPI.等待读取 SIZE/NUM” 状态下被设置为高电平: 等待读取 SIZE/NUM “和 ”SPI:等待读取数据/ERR “状态时,数据就绪 GPIO 引脚将被设置为高电平. 因此,用户可以将数据就绪 GPIO 引脚用作中断或状态引脚.

要为 SPI 方案设置 Data-Ready GPIO 引脚,用户需要使用 dwm_int_cfg 通过 SPI 方案进行 TLV 请求: SPI TLV 通信使用轮询 中引入的普通 TLV 通信.

该方案的通信流程如下图所示.

  1. 设置SPI中断.

  2. 主机设备以 TLV 格式写入请求.

  3. 主机设备等待 DWM1001 上的数据就绪 GPIO 引脚变为高电平.

  4. 主机设备读取 SIZE/NUM 字节.

  5. 主机设备在每次传输中读取 NUM 次 TLV 格式的 SIZE 字节数据响应.

    ../../../_images/image71.png

    使用数据就绪 GPIO 引脚进行 SPI TLV 通信

从步骤中可以看出,该方案使用 DWM1001 上的数据就绪 GPIO 引脚在响应数据就绪时指示主机设备. 这样,主机设备在读取 SIZE 字节时就不会那么忙了.


SPI 部分传输

从 DWM 模块读取数据时,如果主机设备没有在一次传输中读取所有字节的数据,读取操作仍将被视为完成. 其余的响应将被放弃. 例如,在 ” SPI:等待读取数据/ERR “状态下,DWM 模块已准备好 SIZE 字节的响应数据,并希望主机设备读取所有 SIZE 字节的响应数据. 但是,如果主机设备只读取了部分数据,DWM 模块将放弃其余数据并转入下一状态: “SPI: IDLE “.


SPI 错误恢复机制

DWM1001 SPI 有一个特殊的类型值 0xFF,称为 type_nop. 带有 type_nop 的 TLV 数据报文表示无操作. 在 ” SPI: IDLE “状态下,当 DWM1001 SPI 接收到消息并发现类型字节为 0xFF 时,它将不会执行任何操作,包括向通用 API 线程发送 TLV 数据消息.

type_nop 是为错误恢复而设计的. 如果主机设备不确定 DWM1001 SPI 处于何种状态,可以利用 SPI 响应和非部分传输机制,通过发送三个 0xFF 假字节,将 DWM1001 SPI 重置为 “SPI.IDLE ”状态: 它可以利用 SPI 响应和非部分传输机制,将 DWM1001 SPI 复位到 “SPI:IDLE ”状态,方法是发送三个 0xFF 虚字节,每个虚字节进行一次传输. 三次传输后,DWM1001 SPI 的响应数据将全部变为值为 0xFF 的哑字节,表明 DWM1001 SPI 处于 “SPI.IDLE ”状态.


低功耗模式唤醒机制

正如 PANS 库所提供的,DWM 模块可以在低功耗模式下工作. 在低功耗模式下,当主机设备不与 API 通信时,模块会让与 API 相关的线程进入 “休眠 ”状态. 在这种情况下,主机设备需要通过外部接口唤醒模块,然后才能开始真正的通信.

对于 SPI 接口,将芯片选择引脚置低电平唤醒模块,即不需要额外的传输.

在 API 传输完成后,主机设备需要让模块回到 “睡眠 ”状态,以节省电源,如 dwm_sleep 和 shell 命令 quit 所介绍.


SPI 唤醒

如果 DWM 休眠(在低功耗模式下),则必须在 SPI 开始接受命令之前执行唤醒程序. 必须在 SPI 的 CS 引脚上产生至少 35 微秒宽的脉冲,才能从休眠状态唤醒(仅在低功耗模式下).