SPI ======== 概述 -------- SPI ( Serial Peripheral Interface, 串行外围设备接口)是一种高速,全双工,同步的通信总线 常规只占用四根线,可节约芯片管脚和 PCB 的布局省空间 常见的集成这种协议的芯片有 EEPROM , FLASH , AD 转换器等 优点: - 支持全双工 - 支持高速 - 协议支持字长不限于 8bit ,可根据应用特点灵活选择消息字长 - 硬件连接简单 缺点: - 相比 IIC 多两根线 - 没有寻址机制,只能靠片选选择不同设备 - 没有从设备接受 ACK ,主设备对于发送成功与否不得而知 - SPI 传输距离较短 .. image:: ../../../_static/SPI_topologies.png :align: center .. centered:: SPI 一主多从拓扑结构 SPI 总线包含以下 4 条逻辑线: - SCK : Serial Clock 串行时钟信号,由主机发给从机 - MOSI : Master Output Slave Input 主发从收信号 - MISO : Master Input Slave Output 主收从发信号 - SS/CS : Slave Select 片选信号,由主机发送 WQ7036A/AX 内置两个 SPI master 控制器 WQ7036A/AX 作为主机,向从机发送命令或数据,从机端接收主机端发送的数据 .. image:: ../../../_static/SPI_framework.png :align: center .. centered:: SPI 架构图 功能特性 ------------------- - 单收、单发和收发并行模式 - 四线全双工和三线半双工通信 - 主机从机模式配置 - 时钟极性( CPOL )和时钟相位( CPHA )可配 - 时钟分频倍数可配 - 传输数据大小可配 - SPI DMA 接口支持使用链表收/发数据 - SPI 中断接口 工作模式 ---------- SPI 支持四种传输模式,可以通过寄存器 SPICR1 中的 CPOL 和 CPHA 位设置: CPOL , 即 Clock Polarity 时钟极性,决定在总线空闲时, SCK 信号线上的电平为高或低 - 1 : 时钟低电平时有效, SCK 信号线在空闲时为高电平 - 0 : 时钟高电平时有效, SCK 信号线在空闲时为低电平 CPHA ,即 Clock Phase 时钟相位,定义 SPI 数据传输的两种基本模式: - 1 : 在时钟偶数( 2 , 4 , 6 , ... , 16 )边沿(包括上下边沿)进行数据采样 - 0 : 在时钟奇数( 1 , 3 , 5 , ... , 15 )边沿(包括上下边沿)进行数据采样 SPI 通讯要求主从设备必须配置相同的传输模式 .. wavedrom:: { signal:[ {node: "..A", phase:-0.35}, {node: "...B", phase:0.14}, [ "时钟信号", {name: "SCK (CPOL=0)", wave: "l.nn......l."}, {name: "SCK (CPOL=1)", wave: "h.pp......h."} ], {}, [ "从机选择", {name: "SSEL", wave: "10.........1"}, ], {}, [ "CPHA=0", {name: "CYCLE (CPHA=0)", wave: "xx34567893x.", data:[1, 2, 3, 4, 5, 6, 7, 8], phase:0.14}, {name: "MOSI (CPHA=0)", wave: "xx34567893x.", data:["bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8"], phase:0.14}, {name: "MISO (CPHA=0)", wave: "xx34567893x.", data:["bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8"], phase:0.14}, ], [ "CPHA=1", {name: "CYCLE (CPHA=1)", wave: "xx34567893x.", data:[1, 2, 3, 4, 5, 6, 7, 8], phase: -0.35}, {name: "MOSI (CPHA=1)", wave: "xx34567893x.", data:["bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8"], phase: -0.35}, {name: "MISO (CPHA=1)", wave: "xx34567893x.", data:["bit1", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8"], phase: -0.35}, ], {node: "..C", phase:-0.35}, {node: "...D", phase:0.14}, ], foot: { text:'SPI 工作模式与 CPOL 和 CPHA 设置' }, edge: [ 'A|C', 'B|D' ] } 注意事项一: 当 CPHA = 0 且 CPOL = 0 时, DW_apb_ssi 主设备在开始连续串行传输的每个新数据帧之前切换从选择输出( ss_0_n ) 而当 CPHA = 1 且 CPOL = 1 时, DW_apb_ssi 主设备在开始连续串行传输之前拉低从选择输出( ss_0_n )即可 .. wavedrom:: { signal: [ {name: "sclk_out/in 0" , wave: "nlnnnlnn"}, {name: "txd", wave: "30423042", data: ["LSB", "MSB", "", "LSB", "MSB"]}, {name: "rxd", wave: "30423042", data: ["LSB", "MSB", "", "LSB", "MSB"]}, {name: "ss_0_n/ss_in_n", wave: "l.pl..pl.", phase:0.5}, {name: "ssi_oe_n", wave: "0......."} ], gaps: ". . . s", foot: { text:'当 CPHA = 0 且 CPOL = 0 时 DW_apb_ssi SPI 连续传输' }, } | .. wavedrom:: { signal:[ {name: "sclk_out/in 1" , wave: "n........h"}, {name: "txd", wave: "x32222224x", data: ["MSB", "", "", "", "", "", "", "LSB"]}, {node: ".A.......B"}, {name: "rxd", wave: "x32222224x", data: ["MSB", "", "", "", "", "", "", "LSB"]}, {name: "ss_0_n/ss_in_n", wave: "hl........", phase: 0.3}, {name: "ssi_oe_n", wave: "hl........", phase: 0.2} ], edge:['A+B 4-16 bits'], gaps:". . . . . s", foot: { text:'当 CPHA = 1 且 CPOL = 1 时 DW_apb_ssi SPI 连续传输' }, } 故当用户需要使用 CPHA = 0 且 CPOL = 0 此模式时, 如果用户配置 CS ,交由驱动去控制,那 auto_cs 需设为 false , 如果用户为控制多个 SPI 从设备,那用户需自己手动拉低从选择输出( ss_0_n ), 来保证数据连续传输 注意事项二: 因为 SPI 的频率是在 DCORE 主频上分频的,故需要注意 DCORE 的主频必须大于或等于设置的 SPI 频率的四倍,且注意将运行 核频率也适配于设置的 SPI 频率(推荐运行核主频设置成大于或等于设置的 SPI 频率的四倍)。 如果 DCORE 的主频小于设置的 SPI 频率的四倍,则可能出现 SPI CLK 波形异常的问题; 如果运行核频率不适配于设置的 SPI 频率,则可能出现 SPI 大数据包传输时异常终止的问题。 资源依赖 --------- - SPI - DMA 用法流程 --------- - 初始化 :cpp:func:`wq_spi_init` - 配置并打开 :cpp:func:`wq_spi_open` - poll 方式先写后读 :cpp:func:`wq_spi_poll_transfer` - poll 方式双工同时读写 :cpp:func:`wq_spi_poll_duplex_transfer` - DMA 方式先写后读 :cpp:func:`wq_spi_dma_transfer` - DMA 方式双工同时读写 :cpp:func:`wq_spi_dma_duplex_transfer` - 关闭 :cpp:func:`wq_spi_close` - 注销 :cpp:func:`wq_spi_deinit` 注:如果需要 DMA 方式读写,需要在调用传输函数调用 :cpp:func:`wq_dma_init` 参考示例 --------- :: examples/spi_demo API 介绍 ----------- .. doxygenfile:: wq_spi.h