最近,在公司项目上用到了许多ADI的芯片,通过观察发现,部分芯片的SPI只有三根线,虽说听过,但在之前的工程中从未使用过。与4线相比,这种SPI使用SDIO取代MISO MOSI,在通信过程中该引脚被配置成OD并pull up状态,使主从双方可以通过拉低该线进行数据通信。
从时序上来看,通信时主机先发指令,通过第一帧数据的第一个位(MSB)来决定是否为读、写。以我所使用的AD9643芯片的时序图为例,可以看到数据帧由几个部分构成。
首先最高位是读写切换,1为读,0为写。W1 W0位决定了后续数据传输的长度,00为1B,11为4B。A12-A0为13bit寄存器寻址位,决定需要操作的寄存器地址,最后的D位为实际读取、写入的数据。
这个时序并不复杂,在测试阶段我是用STM32简单的写了一个,跑的呼呼的,没啥问题,在cube中轻车熟路的把SPI配置好,这里我把速度开的非常低,是因为AD9643的数字供电是1.8V的,因此需要过一个电平转换器,那玩意挺蠢的,不是很好使,开高了速度会抽风。
测试ok的,寄存器可以正常写入读取。但是当你切换到ZYNQ平台,你会发现BD中PS的配置里,压根没有让你配SPI的地方。查询资料后发现,ZYNQ这玩意不支持半双工SPI。
但是如果你稍加回忆,在我们很常见的ADALM-2000口袋仪器套件上,他所使用的MXFE芯片AD9963,使用的也是相同的3Wire SPI,不妨参考M2K的代码,看看他是怎么实现的。(M2K PLUTO的代码都非常值得学习,我们以后会经常讨论)
阅读代码可以发现,M2K将SPI就配置为普通的4wire模式。而在PL部分,通过一个module对读写进行判断,从而自动切换SDIO的方向。
// check on rising edge and change on falling edge
always @(posedge spi_clk or posedge ad9963_csn) begin
if (ad9963_csn == 1'b1) begin
spi_count <= 6'd0;
spi_rd_wr_n <= 1'd0;
end else begin
spi_count <= spi_count + 1'b1;
if (spi_count == 6'd0) begin
spi_rd_wr_n <= spi_mosi;
end
end
end
第一段代码用于寄存在这一帧中的读写标志位,在SPI计数器spi_count = 0时,也就是MSB时将spi_mosi的值赋给spi_rd_wr_n。
always @(negedge spi_clk or posedge ad9963_csn) begin
if (ad9963_csn == 1'b1) begin
spi_enable <= 1'b0;
end else begin
if ((spi_count == 6'd16) && (ad9963_csn == 1'b0)) begin
spi_enable <= spi_rd_wr_n;
end
end
end
assign spi_miso = spi_sdio;
assign spi_sdio = (spi_enable == 1'b1) ? 1'bz : spi_mosi;
第二段代码用于在MSB 16个bit后,对读写标志位做出响应。spi_enable高(读)时,spi_miso始终连接至sdio,spi_sdio赋为高阻态,此使由从设备负责拉数据线进行输出发送,从而完成一次读操作。spi_enable低(写)时,sdio连接至spi_mosi,从设备可以接收主设备发送的数据。
如此一来,不用通过另加一根线进行方向切换,而又可以简单的使ZYNQ支持半双工SPI,实在巧妙。