OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 9539|回复: 8

[ALTERA] SPI通信实现fpga自收发,上位机显示

[复制链接]

12

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
103
金钱
103
注册时间
2021-1-30
在线时间
68 小时
发表于 2021-3-11 20:30:23 | 显示全部楼层 |阅读模式
1金钱
FPGA上怎么实现spi协议的自收发呀?我试着用新起点开发板的UART通信例程改,改了好几天了也不行,到底是哪里有问题

最佳答案

查看完整内容[请看2#楼]

直接看程序很难看出问题,你可以在线抓波形或者仿真来调试
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

1979

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
5520
金钱
5520
注册时间
2018-10-21
在线时间
1561 小时
发表于 2021-3-11 20:30:24 | 显示全部楼层
LJY344500 发表于 2021-3-14 20:32
新帖子一直在审核,我把程序发在下面,大佬能看看哪里有问题吗

直接看程序很难看出问题,你可以在线抓波形或者仿真来调试
回复

使用道具 举报

12

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
103
金钱
103
注册时间
2021-1-30
在线时间
68 小时
 楼主| 发表于 2021-3-11 20:31:09 | 显示全部楼层
困扰好几天了,要哭出来了
回复

使用道具 举报

11

主题

314

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1039
金钱
1039
注册时间
2020-7-21
在线时间
258 小时
发表于 2021-3-12 11:29:40 | 显示全部楼层
帮顶           
回复

使用道具 举报

3

主题

1979

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
5520
金钱
5520
注册时间
2018-10-21
在线时间
1561 小时
发表于 2021-3-14 09:18:16 | 显示全部楼层
这个要熟悉SPI的协议,而且实现SPI协议的自收发,FPGA是和什么模块或者设备实现SPI通信?
回复

使用道具 举报

12

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
103
金钱
103
注册时间
2021-1-30
在线时间
68 小时
 楼主| 发表于 2021-3-14 18:58:45 | 显示全部楼层
QinQZ 发表于 2021-3-14 09:18
这个要熟悉SPI的协议,而且实现SPI协议的自收发,FPGA是和什么模块或者设备实现SPI通信?

我是写了两个子模块,一个主机模块发送,一个从机模块接受,大佬你看看我另一个帖子,我把程序放进去了,看看哪里有问题
回复

使用道具 举报

12

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
103
金钱
103
注册时间
2021-1-30
在线时间
68 小时
 楼主| 发表于 2021-3-14 20:30:53 | 显示全部楼层
顶层模块:
module  spi
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号


    output  wire    txd            //输出
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值
parameter   UART_BPS    =   14'd9600        ,   //比特率
            CLK_FREQ    =   26'd50_000_000  ;   //时钟频率
//wire  define
wire         po_key  ;
wire [7:0]   mosi_w  ;
wire         sck_w  ;
wire         cs_n_w ;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//------------- flash_be_ctrl_inst -------------
master     master_inst
(

    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号

    .sck        (sck_w        ),  //片选信号
    .cs_n       (cs_n_w       ),  //串行时钟
    .mosi       (mosi_w     )   //主输出从输入数据
);

slave
#(
    .UART_BPS    (UART_BPS  ),  //串口波特率
    .CLK_FREQ    (CLK_FREQ  )   //时钟频率
)
slave_inst
(
    .cs_n_in    (cs_n_w       )   ,   //片选信号
    .sck_in     (sck_w        )   ,   //串行时钟
    .mosi_in    (mosi_w       )   ,   //主输出从输入数据
         
         .txd        (txd          )    //输出
);
endmodule


按键消抖模块:
module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号

    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

主机发送:
module  master
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   2'b01 ,   //初始状态
            WR_EN   =   4'b10 ;  //写状态

parameter   WR_EN_INST  =   8'b0000_0110;   //写使能指令


//reg   define
reg     [1:0]   cnt_byte;   //字节计数器,0-2计数
reg     [1:0]   state   ;   //状态机状态,2个状态
reg     [4:0]   cnt_clk ;   //系统时钟计数器,0-31循环计数
reg     [1:0]   cnt_sck ;   //串行时钟计数器,0-3计数
reg     [2:0]   cnt_bit ;   //比特计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节,不用修改
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间,ok
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  3'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 2'd2))
        cnt_byte    <=  3'd0;
    else    if(cnt_clk == 31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟,ok
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 1'b1)) //处于写状态并且字节计数器等于1,开始计数
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号,ok
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;


         
//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//state:两段式状态机第一段,状态跳转,ok
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 2'd2) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 2'd2))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 2'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令


endmodule


从机模块:
module  slave
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input               cs_n_in        ,   //片选信号
    input               sck_in         ,   //串行时钟
    input   wire [7:0]  mosi_in        ,   //主输出从输入数据
         
         output  reg         txd             //输出
);

reg[3:0] Data_State = 4'd0;

parameter D7_State = 4'd0;
parameter D6_State = 4'd1;
parameter D5_State = 4'd2;
parameter D4_State = 4'd3;
parameter D3_State = 4'd4;
parameter D2_State = 4'd5;
parameter D1_State = 4'd6;
parameter D0_State = 4'd7;

always@(posedge sck_in)
        if(cs_n_in == 1'b1)
            txd <= 1'b1; //空闲状态时为高电平
        else    if(cs_n_in == 1'b0)
            case(Data_State)
             D7_State:begin txd <= mosi_in[0]; Data_State<= D6_State;end
             D6_State:begin txd <= mosi_in[1]; Data_State<= D5_State;end
             D5_State:begin txd <= mosi_in[2]; Data_State<= D4_State;end
             D4_State:begin txd <= mosi_in[3]; Data_State<= D3_State;end
             D3_State:begin txd <= mosi_in[4]; Data_State<= D2_State;end
             D2_State:begin txd <= mosi_in[5]; Data_State<= D1_State;end
             D1_State:begin txd <= mosi_in[6]; Data_State<= D0_State;end
                                 D0_State:begin txd <= mosi_in[7]; Data_State<= D7_State;end
                default : txd <= 1'b1;
            endcase

         
endmodule
回复

使用道具 举报

12

主题

60

帖子

0

精华

初级会员

Rank: 2

积分
103
金钱
103
注册时间
2021-1-30
在线时间
68 小时
 楼主| 发表于 2021-3-14 20:32:19 | 显示全部楼层
QinQZ 发表于 2021-3-14 09:18
这个要熟悉SPI的协议,而且实现SPI协议的自收发,FPGA是和什么模块或者设备实现SPI通信?

新帖子一直在审核,我把程序发在下面,大佬能看看哪里有问题吗
回复

使用道具 举报

3

主题

62

帖子

0

精华

高级会员

Rank: 4

积分
955
金钱
955
注册时间
2019-7-8
在线时间
145 小时
发表于 2021-3-29 16:55:01 | 显示全部楼层
你直接仿真,仿真通过就算可以了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-10-3 18:20

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表