首页IT科技状态机的状态编码有哪几种(用状态机实现通用多字节SPI接口模块)

状态机的状态编码有哪几种(用状态机实现通用多字节SPI接口模块)

时间2025-05-05 11:24:58分类IT科技浏览3250
导读:这次设计一个通用的多字节SPI接口模块,特点如下: 可以设置为1-128字节的SPI通信模块...

这次设计一个通用的多字节SPI接口模块           ,特点如下:

可以设置为1-128字节的SPI通信模块 可以修改CPOL          、CPHA来进行不同的通信模式 可以设置输出的时钟

状态转移图和思路与多字节串口发送模块一样               ,这里就不给出了     ,具体可看该随笔           。

一                、模块代码

1     、需要的模块

通用8位SPI接口模块 `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: Lclone // // Create Date: 2023/01/23 00:56:52 // Design Name: SPI_Interface // Module Name: SPI_Interface // Project Name: // Target Devices: // Tool Versions: // Description: // SPI接口模块 // 可修改分频参数来生成目标频率           ,最低分频系数为2; // 可以置位CPOL     、CPHA可以来设置通信模式; // 本模块只有1位                ,但是可以简单修改位宽来设置多位片选信号 // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module SPI_Interface # ( parameter Value_divide = 2)//分频系数(最低为2) ( //-----------------内部接口------------------ input Clk, //时钟 input Rst_n, //复位信号 input CPOL, //时钟极性 input CPHA, //时钟相位 input CS_input, //片选信号 input Send_en, //发送使能 input [7:0] Data_send, //待发送数据 output reg Read_en, //接收数据读使能 output reg [7:0] Data_recive, //接收到的数据 //------------------外部接口------------------ output reg Spi_clk, //输出时钟端 output reg Spi_mosi, //主输出从接收端 input Spi_miso, //主接收从输出端 output Cs_output //片选信号输出 ); reg act_flag; //活动状态寄存器 reg [9:0] cnt_divide; //分频计数器 reg [7:0] Data_send_reg; //带发送数据寄存器 reg [4:0] cnt_pulse; //脉冲计数器 always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) act_flag <= 0; else if(Send_en == 1) act_flag <= 1; else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1 & cnt_pulse == 16) act_flag <= 0; else act_flag <= act_flag; end always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) Read_en <= 0; else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1 & cnt_pulse == 16) Read_en <= 1; else Read_en <= 0; end always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) Data_send_reg <= 0; else if(Send_en == 1) Data_send_reg <= Data_send; else cnt_divide <= 0; end always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) cnt_divide <= 0; else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1) cnt_divide <= 0; else if(act_flag == 1) cnt_divide <= cnt_divide + 1b1; else cnt_divide <= 0; end always @(posedge Clk or negedge Rst_n) begin//生成目标时钟两倍频率的的cnt_pulse if(Rst_n == 0) cnt_pulse <= 0; else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1 & cnt_pulse == 16) cnt_pulse <= 0; else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1) cnt_pulse <= cnt_pulse + 1b1; else if(act_flag == 1) cnt_pulse <= cnt_pulse; else cnt_pulse <= 0; end always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) begin if(CPOL == 1) begin Spi_clk <= 1; Spi_mosi <= 1; Data_recive <= 0; end else begin Spi_clk <= 0; Spi_mosi <= 1; Data_recive <= 0; end end else if(cnt_divide == Value_divide/2 - 1 & act_flag == 1) begin if(CPHA == 0) case(cnt_pulse) 0:begin Spi_clk <= Spi_clk; Spi_mosi <= Data_send_reg[7]; Data_recive <= Data_recive; end 1:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[7] <= Spi_miso; end 2:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[6]; Data_recive <= Data_recive; end 3:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[6] <= Spi_miso; end 4:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[5]; Data_recive <= Data_recive; end 5:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[5] <= Spi_miso; end 6:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[4]; Data_recive <= Data_recive; end 7:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[4] <= Spi_miso; end 8:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[3]; Data_recive <= Data_recive; end 9:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[3] <= Spi_miso; end 10:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[2]; Data_recive <= Data_recive; end 11:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[2] <= Spi_miso; end 12:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[1]; Data_recive <= Data_recive; end 13:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[1] <= Spi_miso; end 14:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[0]; Data_recive <= Data_recive; end 15:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[0] <= Spi_miso; end 16:begin Spi_clk <= ~Spi_clk; Spi_mosi <= 1; Data_recive <= Data_recive; end default:; endcase else case(cnt_pulse) 0:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[7]; Data_recive <= Data_recive; end 1:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[7] <= Spi_miso; end 2:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[6]; Data_recive <= Data_recive; end 3:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[6] <= Spi_miso; end 4:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[5]; Data_recive <= Data_recive; end 5:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[5] <= Spi_miso; end 6:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[4]; Data_recive <= Data_recive; end 7:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[4] <= Spi_miso; end 8:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[3]; Data_recive <= Data_recive; end 9:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[3] <= Spi_miso; end 10:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[2]; Data_recive <= Data_recive; end 11:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[2] <= Spi_miso; end 12:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[1]; Data_recive <= Data_recive; end 13:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[1] <= Spi_miso; end 14:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Data_send_reg[0]; Data_recive <= Data_recive; end 15:begin Spi_clk <= ~Spi_clk; Spi_mosi <= Spi_mosi; Data_recive[0] <= Spi_miso; end 16:begin Spi_clk <= Spi_clk; Spi_mosi <= 1; Data_recive <= Data_recive; end default:; endcase end end assign Cs_output = CS_input; endmodule

2                、设计的模块

`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: GDUT // Engineer: Lclone // // Create Date: 2023/01/23 22:12:11 // Design Name: SPI_Bytes // Module Name: SPI_Bytes // Project Name: // Target Devices: // Tool Versions: // Description: // - 可以设置位1-128字节的SPI通信模块 // - 可以修改CPOL           、CPHA来进行不同的通信模式 // - 可以设置输出的时钟 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module SPI_Bytes # ( parameter Data_Width = 16, //数据位宽 parameter ROUNDS = Data_Width/8) //传输轮数(例化时不需要设置) ( //-----------------内部接口-------------------- input Clk, //时钟信号 input Rst_n, //复位信号 input [Data_Width-1:0] Send_Bytes_Data, //发送的多字节数据 input Bytes_Send_en, //多字节发送使能 output reg [Data_Width-1:0] Recive_Bytes_Data, //接收的多字节数据 output reg Bytes_Read_en, //多字节读使能 input Cs_input, //片选信号输入 //-----------------外部接口-------------------- output Spi_mosi, //主输出从输入 input Spi_miso, //主输入从输出 output Spi_clk, //输出时钟 output Cs_output //片选信号输出 ); reg send_en; //发送使能 wire read_en; //读使能 reg [7:0] data_send; //待发送数据 reg [Data_Width-1:0] Send_Bytes_Data_reg; //多字节数据寄存器 wire[7:0] data_recive; //接收的数据 reg [9:0] round; //发送次数(修改该位宽可改变最大发送数据位宽) reg [1:0] state; //状态寄存器 always @(posedge Clk or negedge Rst_n) begin if(Rst_n == 0) round <= 0; else if(round == ROUNDS) round <= 0; else if(read_en == 1) round <= round + 1b1; else round <= round; end always @(posedge Clk or negedge Rst_n) begin//状态机 if(Rst_n == 0) begin state <= 0; Bytes_Read_en <= 0; data_send <= 0; Send_Bytes_Data_reg <= 0; send_en <= 0; Recive_Bytes_Data <= 0; end else case(state) 0://空闲状态 begin Bytes_Read_en <= 0; if(Bytes_Send_en == 1) begin state <= 1; Send_Bytes_Data_reg <= Send_Bytes_Data; end else state <= 0; end 1://发送状态 begin send_en <= 0; if(round == ROUNDS) begin state <= 0; Bytes_Read_en <= 1; Recive_Bytes_Data[7:0] <= data_recive;//由于发送和接收的时序略有不同     ,这里给接收做个补偿               。 end else begin state <= 2; send_en <= 1; data_send <= Send_Bytes_Data_reg[Data_Width-1:Data_Width-8];//发送高位 Recive_Bytes_Data[7:0] <= data_recive;//把接收到的数据放在低位 end end 2://数据移位 begin send_en <= 0; if(read_en == 1) begin Send_Bytes_Data_reg <= Send_Bytes_Data_reg << 8;//高位刷新 Recive_Bytes_Data <= Recive_Bytes_Data << 8;//把低位的数据移到高位 state <= 1; end else state <= 2; end default:; endcase end SPI_Interface # ( .Value_divide (4)) //分频系数 SPI_SPI_Interface_inst ( //-----------------内部接口------------------ .Clk (Clk), //时钟信号 .Rst_n (Rst_n), //复位信号 .CPOL (1), .CPHA (0), .CS_input (1), //片选输入 .Send_en (send_en), //发送使能 .Data_send (data_send), //待发送数据 .Read_en (read_en), //读使能 .Data_recive (data_recive), //接收的数据 //------------------外部接口------------------ .Spi_clk (Spi_clk), //输出时钟 .Spi_mosi (Spi_mosi), //主输出从输入 .Spi_miso (Spi_miso), //主输入从输出 .Cs_output (Cs_output) //片选输出 ); endmodule

二     、仿真

1               、仿真激励

`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2023/01/26 16:00:48 // Design Name: // Module Name: SPI_Bytes_tb // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module SPI_Bytes_tb(); reg clk_50m; initial clk_50m <= 1; always #10 clk_50m <= ~clk_50m; reg rst_n; initial begin rst_n <= 0; #200 rst_n <= 1; end reg Bytes_Send_en; reg [31:0] Send_Bytes_Data; wire Bytes_Read_en; wire [31:0] Recive_Bytes_Data; wire Spi_clk; wire Spi_mosi; wire Spi_miso; wire Cs_output; SPI_Bytes # ( .Data_Width (32))//数据位宽为32位 SPI_Bytes_inst ( //-----------------内部接口-------------------- .Clk (clk_50m), .Rst_n (rst_n), .Send_Bytes_Data (Send_Bytes_Data), .Bytes_Send_en (Bytes_Send_en), .Recive_Bytes_Data (Recive_Bytes_Data), .Bytes_Read_en (Bytes_Read_en), .Cs_input (1b1), //-----------------外部接口-------------------- .Spi_mosi (Spi_mosi), .Spi_miso (Spi_miso), .Spi_clk (Spi_clk), .Cs_output (Cs_output) ); assign Spi_miso = Spi_mosi; initial begin Bytes_Send_en <= 0; Send_Bytes_Data <= 0; #400; Bytes_Send_en <= 1; Send_Bytes_Data <= 32h89abcdef; #20 Bytes_Send_en <= 0; #4000; Bytes_Send_en <= 1; Send_Bytes_Data <= 32h12345678; #20 Bytes_Send_en <= 0; end endmodule

2           、仿真结果

仿真结果:两次多字节发送都能正确的发送和接收数据     ,且能正确的生成Bytes_Read_en信号     。模块仿真验证可行           。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
vim unix格式(Vim 配置 C/C++使用组合快捷键格式化文件)