从零学习FPGA(spi串口模块)

此程序可以实现发送端发出一个8位数字信号,接收端通过解码,将对应的数字显示管点亮

一.接收模块

module SPI_rx_exer(
input clk,                      //系统时钟
input spi_clk,                  //spi时钟    
input cs_n,                     //cs片选信号
input rst_n,                    //复位信号
input spi_rx,                   //输入数据
output reg valid,               //使能信号
output reg [7:0]rx_data         //输出数据
);
wire spi_clk_negedge;           //边缘信号

reg [7:0]rx_data_r;
//reg [7:0]tx_data;
reg [2:0]count;
reg [3:0]negedge_count;
reg data_ready;zhi'xing
reg data_ready_r0;
reg data_ready_r1;
reg data_ready_t;
reg spi_clk_r0;
reg spi_clk_r1;
//捕捉clk信号
always@(posedge clk)begin
spi_clk_r0<=spi_clk;                   //延迟捕捉1次
spi_clk_r1<=spi_clk_r0;                //延迟不占2次
end

//二值选择
assign spi_clk_negedge={spi_clk_r1,spi_clk_r0}==2'b10 ? 1'b1 : 1'b0;  //10捕捉下降沿,01捕捉上升沿

//计数器第二种写法
always@(posedge clk or negedge rst_n)begin   //@是敏感列表,下一个时钟敏感,晚一拍的关系

if(~rst_n)begin
 negedge_count<=1'b0;                  //初始化计数信号,此信号用于记录spi_clk_negedge的下降沿
 rx_data_r<=8'd0;                      //初始化转存寄存器
 data_ready<=1'b0;                     //初始化延迟信号
end

else if(spi_clk_negedge==1)begin
 negedge_count<=negedge_count+4'b1;    //计数
 rx_data_r[7-negedge_count]<=spi_rx;   //注意输入输出时从高位开始
 end
 
 else if(negedge_count==8)begin
 data_ready<=1'b1;                     //一个信号从一个always快流向另外一个always或寄存器当中会延迟一个时钟周期
 negedge_count<=1'b0;                  //negedge_count置零
 end
 
 end


 //输出寄存器中的数据到输出信号中
always@(posedge clk or negedge rst_n)begin
 if(~rst_n)
 valid<=1'b0;                          //初始化使能信号
 else if(data_ready==1)begin
 valid<=1'b1;                          //激活使能信号
 rx_data<=rx_data_r;                   //输出转存寄存器中的数据
 end
 else
 valid<=1'b0;                          //拉低使能信号
 end

 
 


/*
//计数器   //第一种写法(不用状态机)
always@(negedge spi_clk or negedge rst_n)
if (~rst_n)
 count <= 3'b0;
else if(cs_n==1'b0) begin
 if(count < 3'd7 )
  count <= count+1;
 else
  count <= 3'b0;
 end

 always @(negedge spi_clk or negedge rst_n)begin     //移位寄存器串转并
 if (~rst_n)begin
   rx_data_r<=8'b0;
   data_ready_t<=1'b0;
   end
 else begin
   if(cs_n==1'b0)begin

        rx_data_r<={rx_data_r[6:0],spi_rx};      
        count<=count+1;
        data_ready_t<=1'b1;

        end
    end
 end
 
//快时钟预处理,and 输出数据(使能信号)   //跨时钟域处理
 always    @(posedge clk or negedge rst_n)
 if(~rst_n)begin
  data_ready<=1'b0;
 end
 else if(count == 3'd0 && data_ready_t == 1)begin
  rx_data<=rx_data_r;  
  data_ready<=1'b1; 
  end
  else
  data_ready<=1'b0;
 
//data_ready进行边缘检测
always@(clk)begin
data_ready_r0<=data_ready;
data_ready_r1<=data_ready_r0;   //ready数据存在r0和r1中
end

always@(clk or negedge rst_n)begin
if(~rst_n)
valid<=1'b0;
else if({data_ready_r1,data_ready_r0}==2'b01)
valid<=1'b1;
else
valid<=1'b0;
end


//在voilog中begin end用来代替c语言中的花括号
// 在函数括号中必须定义output,input来定义具体变量的作用

//分频器
/*
always@(posedge clk negedge rst_n)begin
 if(rst_n == 1'b0)
  spi_clk<=1'b0;
  else if(count == 9)
   spi_clk<=~spi_clk;
  else
   spi_clk<=spi_clk;
end
*/

endmodule

二.数据发出模块

module SPI_Master(
    input valid,    //valid == 1数据有效时间,valid == 0无效时间
    input clk,    //系统时钟
    input [7:0]tx_data,    //输入信号tx_data,表示需要发送的数据
    input rst_n,    //复位,rst_n = 0时,参数回归初始值
    output reg cs,    //输出信号cs,表示片选信号
    output spi_tx,    //输出信号spi_tx,表示SPI总线上的发送数据
    output reg spi_clk    //输出信号spi_clk,表示SPI总线上的时钟信号
);
//状态
localparam IDLE = 2'b00;    //定义一个局部参数IDLE,表示空闲状态
localparam SEND = 2'b01;    //定义一个局部参数SEND,表示发送状态

reg bool = 1;
reg [1:0]current_state, next_state;    //定义两个寄存器,分别表示当前状态和下一个状态
reg [3:0]count = 4'd0;
//reg send_data;    //定义一个寄存器send_data,用于存储发送数据
//reg [7:0] reg_shift = 8'b0000_0001;    //定义一个寄存器reg_shift,用于移位操作
reg tx_data_r;    //定义一个寄存器tx_data_r,用于接收数据
reg [3:0]bit_cnt;    //定义一个寄存器bit_cnt,用于计数位

assign spi_tx = tx_data_r;    //将tx_data_r的值赋给spi_tx


//状态机1
always @(posedge clk or negedge rst_n)begin    //敏感列表,表示在spi_clk的上升沿或rst_n的下降沿时触发该always块
    if(rst_n == 1'b0)
        current_state <= 2'b00;    //设置为初始状态(IDLE)
    else
        current_state <= next_state;    //将当前状态更新为下一个状态
end

//2
always @(*)begin    //敏感列表,表示在任何输入变化时触发该always块
if(bool == 1)
    case(current_state)
    IDLE:
        if(valid == 1)    //如果valid信号为高电平(有效),则执行下一行代码
            next_state <= SEND;    //将下一个状态设置为SEND
        else
            next_state <= IDLE;
    SEND:
        if(bit_cnt == 4'd8)    //如果valid信号为低电平(无效),则执行下一行代码
            next_state <= IDLE;    //将下一个状态设置为IDLE
        else
            next_state <= SEND;

    endcase
else
    next_state <= IDLE;    
end

//3
always @(posedge spi_clk or negedge rst_n)begin
    if(rst_n == 0)begin
        tx_data_r <= 1'b1;    //将发送数据寄存器tx_data_r设置为1
        bit_cnt <= 3'd0;    //将位计数器bit_cnt重置为0
    end
    else if (current_state == SEND && bit_cnt < 4'd8)begin
        tx_data_r <= tx_data[7 - bit_cnt];    //将发送数据寄存器tx_data_r设置为tx_data数组中对应的值
        bit_cnt <= bit_cnt + 1'd1;    //位计数器bit_cnt加1
    end
    else if (current_state == SEND && bit_cnt == 4'd8)begin
        bool <= 0;    //将下一个状态设置为IDLE
    end
    else
        bit_cnt <= bit_cnt;

end


//计数器 产生spi_clk
always @(posedge clk)begin
    if(current_state == SEND)begin
        if(count < 4'd9)
            count <= count + 1'b1;
        else
            count <= 4'd0;
    end
    else if(current_state != SEND && count != 0)
        count <= count + 1'b1;
end

//分频器,2/4/6分频变成原0.5/0.25频率---偶分频
always @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)
        spi_clk <= 1'b0;
    else if(current_state == SEND && bit_cnt < 4'd8 && count == 4'd4)
        spi_clk <= 1;
    else if(current_state == SEND && bit_cnt < 4'd8 && count == 4'd9)
        spi_clk <= 0;
    else if(current_state != SEND && bit_cnt == 4'd8 && count == 4'd9)
        spi_clk <= 0;
    else
        spi_clk <= spi_clk;
end


always @(posedge clk or negedge rst_n)begin    //
    if(rst_n == 1'b0)
        cs <= 1;
    else if(current_state == SEND)
        cs <= 0;
    else if(current_state != SEND && spi_clk != 0)
        cs <= 0;
    else
        cs <= 1;
end

endmodule

三.接收程序调用模块

module top(

//接收信号模块输入
//接受的数据线要在版上连线
input clk,              
input spi_clk,    //时钟线 L1     
input cs_n,       //片选线 N1      
input rst_n,            
input spi_rx,     //接受数据线 P1     

//数码管输出
output [5:0]sel,          
output [7:0]seg_led   

);

wire [7:0]rx_data;
wire valid;

//调用rx数据接受模块
SPI_rx_exer spi_rx_inst(

//输入信号
.spi_rx     (spi_rx), 
.spi_clk    (spi_clk),
.cs_n       (cs_n),

//版内控制信号
.rst_n      (rst_n), 
.clk        (clk),   
   
//输出信号               
.valid      (valid),      
.rx_data    (rx_data)

);

//调用数码管译码模块
decode_exer decoder_inst(

.decode_number   (rx_data),
.sel             (sel),          
.seg_led         (seg_led)

);
 
endmodule

三.烧录立化程序

module top(

//接收信号模块输入
//接受的数据线要在版上连线
input clk,              
input spi_clk,    //时钟线 L1     
input cs_n,       //片选线 N1      
input rst_n,            
input spi_rx,     //接受数据线 P1     

//数码管输出
output [5:0]sel,          
output [7:0]seg_led   

);

wire [7:0]rx_data;
wire valid;

//调用rx数据接受模块
SPI_rx_exer spi_rx_inst(

//输入信号
.spi_rx     (spi_rx), 
.spi_clk    (spi_clk),
.cs_n       (cs_n),

//版内控制信号
.rst_n      (rst_n), 
.clk        (clk),   
   
//输出信号               
.valid      (valid),      
.rx_data    (rx_data)

);

//调用数码管译码模块
decode_exer decoder_inst(

.decode_number   (rx_data),
.sel             (sel),          
.seg_led         (seg_led)

);
 
endmodule

四.烧录

在quartus软件上编译程序,配置对应管脚实现功能

fpga spi串口实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值