经过上一篇文章https://bbs.eeworld.com.cn/thread-1319828-1-1.html,我们已经通过仿真把这个搞出来了,但是实际上在电路上是啥样还没看过,不动手的话总归还是纸上谈兵,仿真只是为了让搞实际电路更快一点,切不可本末倒置。
首先我们看一下逻辑派G1的图纸,以下是JLC的逻辑派G1的图纸,用网页可以直接打开。但在这里方便大家查看,我截个图,如图1所示。
图1:FPGA引出的IO
有了图1,咱们就可以开整了!
File->New
FPGA Design Project ->OK
1.Name->取个名
2.Create in -> 文件地址
3.OK
接下来是选型:
截止目前 GW2A下面就一个型号,选择完以后直接Next工程建立完成。
module SPI (
input clk,
input rst,
input transFlag,
output reg cs,
output reg mosi,
output reg sclk
);
// 定义状态
// localparam 只在模块内有效,`define 是全局有效
localparam IDLE = 1'b1;
localparam Trans = 1'b0;
reg [1:0] Current_S;
reg [1:0] Next_S;
reg [4:0] Trans_Cnt;
reg [1:0] sclk_status;
reg [7:0] Data;
reg [4:0] clk_div_cnt; // 时钟分频计数器
reg spi_clk; // 分频后的时钟信号
// 25 倍时钟分频逻辑
always @(posedge clk or negedge rst) begin
if (!rst) begin
clk_div_cnt <= 5'b0;
spi_clk <= 1'b0;
end else begin
if (clk_div_cnt == 5'd24) begin // 计数到 24 时重置计数器
clk_div_cnt <= 5'b0;
spi_clk <= ~spi_clk; // 翻转分频后的时钟信号
end else begin
clk_div_cnt <= clk_div_cnt + 1'b1;
end
end
end
initial begin
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
Data <= 8'h55;
mosi <= 1'b0;
Current_S <= IDLE;
cs <= 1'b1;
Next_S <= IDLE;
end
// 使用分频后的时钟信号进行状态转移
always @(posedge spi_clk or negedge rst) begin
if (!rst) begin
// 跳转到 IDLE 状态
Current_S <= IDLE;
end
// 跳转到下个状态
else begin
Current_S <= Next_S;
end
end
// 使用分频后的时钟信号进行状态机逻辑
always @(posedge spi_clk or negedge rst) begin
if (!rst) begin
// 复位逻辑
cs <= 1'b1;
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
Data <= 8'h55;
Next_S <= IDLE;
end else begin
case(Current_S)
Trans: begin
cs <= 1'b0;
// 每次上升沿发送一位数据 sclk 状态+1
case(sclk_status)
2'b00: begin
sclk <= 1'b1;
sclk_status <= sclk_status + 1'b1;
end
2'b01: begin
mosi <= Data[7];
Data <= Data << 1;
sclk_status <= sclk_status + 1'b1;
end
2'b10: begin
sclk <= 1'b0;
sclk_status <= 2'b00;
Trans_Cnt <= Trans_Cnt + 1'b1;
end
endcase
// 如果来了 8 个时钟 下个周期跳转到 idle
if(Trans_Cnt == 5'd8) begin
Next_S <= IDLE;
Trans_Cnt <= 5'b0;
end
end
IDLE: begin
cs <= 1'b1;
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
// when clk posedge jump to next status
if (transFlag) begin
Next_S <= Trans;
Data <= 8'h55;
end else begin
Next_S <= IDLE;
end
end
default: Next_S <= IDLE;
endcase
end
end
endmodule
4.1确定引脚任选一个做为复位引脚:
按钮SW2--D11_IOL22A
按钮SW3--F10_IOL11A
晶振 XTAL-- T7_IOR29A
4.2确定IO type
不同的IO TYPE特性如下(通义千问生成):
因为一会要测试的用3.3V的UART,所以这里我们选LVCMOS33
观察测试结果下降沿发是没有问题了但是CS和SCLK之间没有间隔看起来怪怪的,在加点东西进去调一下。
module SPI (
input clk,
input rst,
input transFlag,
output reg cs,
output reg mosi,
output reg sclk
);
// 定义状态
// localparam 只在模块内有效,`define 是全局有效
localparam IDLE = 1'b1;
localparam Trans = 1'b0;
reg [1:0] Current_S;
reg [1:0] Next_S;
reg [4:0] Trans_Cnt;
reg [1:0] sclk_status;
reg [7:0] Data;
reg [4:0] clk_div_cnt; // 时钟分频计数器
reg spi_clk; // 分频后的时钟信号
reg [4:0] delay_cnt; // 延时计数器
reg delay_start; // 延时开始标志
// 25 倍时钟分频逻辑
always @(posedge clk or negedge rst) begin
if (!rst) begin
clk_div_cnt <= 5'b0;
spi_clk <= 1'b0;
end else begin
if (clk_div_cnt == 5'd24) begin // 计数到 24 时重置计数器
clk_div_cnt <= 5'b0;
spi_clk <= ~spi_clk; // 翻转分频后的时钟信号
end else begin
clk_div_cnt <= clk_div_cnt + 1'b1;
end
end
end
initial begin
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
Data <= 8'h55;
mosi <= 1'b0;
Current_S <= IDLE;
cs <= 1'b1;
Next_S <= IDLE;
delay_cnt <= 5'b0;
delay_start <= 1'b0;
end
// 使用分频后的时钟信号进行状态转移
always @(posedge clk or negedge rst) begin
if (!rst) begin
// 跳转到 IDLE 状态
Current_S <= IDLE;
end
// 跳转到下个状态
else begin
Current_S <= Next_S;
end
end
// 使用分频后的时钟信号进行状态机逻辑
always @(posedge spi_clk or negedge rst) begin
if (!rst) begin
// 复位逻辑
cs <= 1'b1;
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
Data <= 8'h55;
Next_S <= IDLE;
end else begin
case(Current_S)
Trans: begin
// 如果来了 8 个时钟 下个周期跳转到 idle
if(Trans_Cnt > 5'd7) begin
Next_S <= IDLE;
Trans_Cnt <= 5'b0;
end
else begin
// 每次上升沿发送一位数据 sclk 状态+1
case(sclk_status)
2'b00: begin
sclk <= 1'b1;
sclk_status <= sclk_status + 1'b1;
end
2'b01: begin
mosi <= Data[7];
Data <= Data << 1;
sclk_status <= sclk_status + 1'b1;
end
2'b10: begin
sclk <= 1'b0;
sclk_status <= 2'b00;
Trans_Cnt <= Trans_Cnt + 1'b1;
end
endcase
end
end
IDLE: begin
cs <= 1'b1;
sclk <= 1'b0;
Trans_Cnt <= 5'b0;
sclk_status <= 2'b00;
// when clk posedge jump to next status
if (transFlag) begin
delay_start<=1'b1;
end
if(delay_start) begin
if (delay_cnt<5'd2) begin
delay_cnt <= delay_cnt + 1'b1;
end
else begin
cs <= 1'b0;
Next_S <= Trans;
Data <= 8'h55;
delay_cnt <= 5'b0;
delay_start <= 1'b0;
end
end
else begin
Next_S <= IDLE;
end
end
default: Next_S <= IDLE;
endcase
end
end
endmodule
主要修改部分:
// Change transmission standard bit to delay flag bit
// 修改传输标志位为延时标记位
if (transFlag) begin
delay_start<=1'b1;
end
if(delay_start) begin
// Setting delay counter
// 设置延迟计数器
if (delay_cnt<5'd2) begin
delay_cnt <= delay_cnt + 1'b1;
end
else begin
//jump to Send
// 跳转到发送
cs <= 1'b0;
Next_S <= Trans;
Data <= 8'h55;
delay_cnt <= 5'b0;
delay_start <= 1'b0;
end
end
else begin
Next_S <= IDLE;
end
每个时钟都跳转到Next_Status
always @(posedge clk or negedge rst) begin
if (!rst) begin
// 跳转到 IDLE 状态
Current_S <= IDLE;
end
// 跳转到下个状态
else begin
Current_S <= Next_S;
end
end
https://mp.weixin.qq.com/s/jY0BIhtX8UtWXFMW-xcK2g