常用通信协议总结及FPGA实现(下)-FPGA常见问题社区-FPGA CPLD-ChipDebug

常用通信协议总结及FPGA实现(下)

继续更新常用的串口通信协议。本次要介绍的是SPI协议。

3.SPI: 
    SPI(serial peripheral interface)也是一种同步串行通信协议。这里为啥要说“也”呢,回想上一篇介绍的PS/2不也是同步串行通信协议吗。SPI由一个主设备 和 一个或多个从设备组成,主设备启动与从设备的同步通信,从而完成数据的交换。标准的SPI仅仅使用4个引脚,常用于主设备和外设(flash、实时时钟、dsp等器件)的通信。
下面说说这四个引脚分别用来干啥。
    1)SSEL:从设备片选使能信号。如果从设备是低电平使能,那么拉低这个引脚,从设备将会被选中,主机和这个被选中的从机进行通信。
    2)SCK:时钟信号,由主机产生。
    3)MOSI:主机给从机发送指令或者数据的通道。
    4)MISO:主机读取从机的状态或者数据的通道。
    另外还需要特别介绍两个名词:
1)CPOL:clock polarity,时钟极性。通信整个过程分为空闲时刻和通信时刻,SCK在数据发送前后的空闲状态是高电平,那么CPOL为1,否则为0。
    2)CPHA:clock phase,时钟相位。
    对CPOL和CPHA进行排列组合的话,会发现SPI会有四种工作模式。下面介绍这四种模式:
模式0:CPOL = 0, CPHA = 0.

图片[1]-常用通信协议总结及FPGA实现(下)-FPGA常见问题社区-FPGA CPLD-ChipDebug

    模式1:CPOL = 0, CPHA =1.

图片[2]-常用通信协议总结及FPGA实现(下)-FPGA常见问题社区-FPGA CPLD-ChipDebug

    模式2:  CPOL = 1, CPHA = 0.

图片[3]-常用通信协议总结及FPGA实现(下)-FPGA常见问题社区-FPGA CPLD-ChipDebug

    模式3:  CPOL = 1, CPHA=1.

图片[4]-常用通信协议总结及FPGA实现(下)-FPGA常见问题社区-FPGA CPLD-ChipDebug

总结:CPHA=1表示数据输出在第一个时钟周期的第一个沿,数据采样在第二个沿;

           CPHA=0表示数据采样在第一个沿,数据输出在第二个沿.

          CPOL=1表示第一个沿为下降沿;

           CPOL=0表示第一个沿为上升沿.

根据从设备的技术手册可以确定SPI.

下面给出一个SPI控制器模块的实例代码:

(工作在模式0,100MHz分频产生1MHz的SCK时钟)

//代码参考自《搭建你的数字积木--数字电路与逻辑设计》,根据个人编码风格稍有改动
module spi_master
(
    input       mosi,
    input       clk,
    input       rst,
    input       busy,
    input       spi_send,
    input [7:0] spi_data_out,
    output reg  sck,
    output reg  miso,
    output reg  cs,
    output reg  spi_send_done
);


reg [3:0] count;


parameter IDLE = 0;
parameter CS_L = 1;
parameter DATA = 2;
parameter FINISH = 3;


reg [4:0] cur_st;
reg [4:0] nxt_st;
reg [7:0] reg_data;
reg       sck_reg;
reg [8:0] delay_count;


always @(posedge clk or negedge rst) begin
  if(~rst)
    delay_count <= 0;
  else if(delay_count == 49)
    delay_count <= 0;
  else
    delay_count <= delay_count + 1;
end
//产生1MHz的时钟
always @(posedge clk or negedge rst) begin
  if(~rst)
    sck_reg <= 0;
  else if(delay_count == 49)
    sck_reg <= ~sck_reg;      
end


always @(*) begin
  if(cs) 
    sck = 1;
  else if(cur_st == FINISH)
    sck = 1;
  else if(!cs)
    sck = sck_reg;
  else
    sck = 1;
end


always @(posedge sck_reg or negedge rst) begin
    if(~rst)
        cur_st <= IDLE;
    else 
        cur_st <= nxt_st;
end


always @(*) begin
    nxt_st <= cur_st;
    case(cur_st)
        IDLE:if(spi_send) nxt_st = CS_L;
        CS_L:nxt_st = DATA;
        DATA:if(count == 7) nxt_st = FINISH;
        FINISH:if(busy) nxt_st = IDLE;
        default:nxt_st = IDLE;
    endcase
end


always @(*) begin
    if(~rst)
        spi_send_done = 0;
    else if(cur_st == FINISH)
        spi_send_done = 1;
    else 
        spi_send_done = 0;    
end




always @(posedge sck_reg or negedge rst) begin
    if(~rst)
        cs <= 1;
    else if(cur_st == CS_L) 
        cs <= 0;
    else if(cur_st == DATA)
        cs <= 0;
    else
        cs <= 1;
end


always @(posedge sck_reg or negedge rst) begin
    if(~rst)
        count <= 0;
    else if(cur_st == DATA)
        count <= count + 1;
    else if(cur_st == IDLE | cur_st == FINISH)
        count <= 0;
end


always @(negedge sck_reg or negedge rst) begin
    if(~rst)
        miso <= 0;
    else if(cur_st == DATA)begin
        reg_data[7:1] <= reg_data[6:0];
        miso <= reg_data[7];
    end
    else if(spi_send)
        reg_data <= spi_data_out;
end




endmodule

请登录后发表评论

    没有回复内容