Sipeed 高云GW2A 三线SPI读取磁编数据
[复制链接]
今天分享一下自己在高云GW2A上编写的三线SPI。
我准备驱动的电机上的角度传感器是三线SPI通信的磁编,所以自己尝试学习编写了一个三线SPI。
三线SPI需要将数据线在输入和输出之间切换,使用三态门可以实现这个效果。
因为对语法不太熟悉,所以我使用的按位处理整个通信过程。
源码时针对使用的器件编写的,后面会继续优化成通用型的SPI模块。
通信效果如下:
源码:
module my_spi(
input clkin_i,
input reset,
input [31:0] spi_trans_cycle,
input [31:0] spi_clock_cycle,
input [31:0] spi_waite_cycle,
output spi_dat_io_e,
input spi_dat_in,
output spi_dat_out,
output spi_clk,
input [15:0] tx_dat,
output [15:0] rx_dat
);
reg [7:0] reg_spi_ctrl;
reg reg_spi_dat_io_e; /* 1 in 0 out */
reg reg_spi_clk;
reg reg_spi_dat_out;
reg [3:0] reg_spi_bit_cnt;
reg [31:0] spi_trans_cycle_cnt;
reg [31:0] spi_clock_cycle_cnt;
reg [31:0] spi_waite_cycle_cnt;
reg [15:0] reg_rx_dat;
reg spi_trans_en;
reg spi_trans_ok;
reg spi_waite_en;
reg spi_waite_ok;
reg spi_receive_en;
reg spi_receive_ok;
task tx_task;
input reg clk_in;
if( spi_clock_cycle_cnt < spi_clock_cycle ) begin
spi_clock_cycle_cnt <= spi_clock_cycle_cnt + 1'd1;
end
else begin
spi_clock_cycle_cnt <= 32'd0;
reg_spi_clk <= clk_in;
reg_spi_ctrl <= reg_spi_ctrl + 8'd1;
end
endtask
always @(posedge clkin_i) begin
if( reset ) begin
reg_spi_ctrl <= 1'd0;
reg_spi_dat_io_e <= 1'd1;
reg_spi_clk <= 1'd0;
spi_trans_cycle_cnt <= 32'd0;
spi_clock_cycle_cnt <= 32'd0;
spi_waite_cycle_cnt <= 32'd0;
spi_trans_en <= 1'd0;
spi_trans_ok <= 1'd0;
spi_waite_en <= 1'd0;
spi_waite_ok <= 1'd0;
spi_receive_en <= 1'd0;
spi_receive_ok <= 1'd0;
reg_spi_bit_cnt <= 1'd0;
end
else begin
if( spi_trans_cycle_cnt < spi_trans_cycle ) begin
spi_trans_cycle_cnt = spi_trans_cycle_cnt + 1'd1;
end
else begin
spi_trans_en <= 1'd1;
spi_trans_ok <= 1'd0;
reg_spi_ctrl <= 8'd1;
spi_trans_cycle_cnt <= 32'd0;
end
case (reg_spi_ctrl)
0: begin
reg_spi_dat_io_e <= 1'd1;
end
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32: begin
reg_spi_dat_io_e <= 1'd1;
tx_task(reg_spi_ctrl[0]);
end
33: begin
if( spi_waite_cycle_cnt < spi_waite_cycle ) begin
spi_waite_cycle_cnt <= spi_waite_cycle_cnt + 1'd1;
end
else begin
spi_waite_cycle_cnt <= 32'd0;
reg_spi_ctrl <= reg_spi_ctrl + 8'd1;
end
if( spi_waite_cycle_cnt > spi_waite_cycle[31:1] ) begin
reg_spi_dat_io_e <= 1'd0;
spi_receive_en <= 1'd1;
spi_trans_en <= 1'd0;
end
end
34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65: begin
reg_spi_dat_io_e <= 1'd0;
tx_task(~reg_spi_ctrl[0]);
end
67: begin
reg_spi_dat_io_e <= 1'd1;
reg_spi_clk <= 1'd0;
reg_spi_ctrl <= 8'd0;
spi_receive_en <= 1'd0;
end
endcase
end
end
reg [7:0] tx_dat_cnt;
reg [7:0] rx_dat_cnt;
always @(posedge reg_spi_clk) begin
if( reset ) begin
reg_spi_dat_out <= 1'd1;
tx_dat_cnt <= 8'd15;
end
else if( spi_trans_en == 1'd1 ) begin
reg_spi_dat_out <= tx_dat[ tx_dat_cnt ];
if( tx_dat_cnt > 8'd0 ) begin
tx_dat_cnt <= tx_dat_cnt - 1'd1;
end
else begin
tx_dat_cnt <= 8'd15;
end
end
else begin
tx_dat_cnt <= 8'd15;
reg_spi_dat_out <= 1'd1;
end
end
always @(negedge reg_spi_clk) begin
if( reset ) begin
rx_dat_cnt <= 8'd15;
end
else if( spi_receive_en == 1'd1 ) begin
reg_rx_dat[rx_dat_cnt] <= spi_dat_in;
if( rx_dat_cnt > 8'd0 ) begin
rx_dat_cnt <= rx_dat_cnt - 1'd1;
end
else begin
rx_dat_cnt <= 8'd15;
end
end
else begin
rx_dat_cnt <= 8'd15;
end
end
assign spi_dat_io_e = reg_spi_dat_io_e;
assign spi_clk = reg_spi_clk;
assign spi_dat_out = reg_spi_dat_out;
assign rx_dat = reg_rx_dat;
endmodule
|