您好,欢迎来到华拓科技网。
搜索
您的当前位置:首页fpga模块(5)SPI从机通信

fpga模块(5)SPI从机通信

来源:华拓科技网
SPI通信基础知识

SPI即串行外设接口(Serial Peripheral Interface)

SPI需要至少4根线,单向传输时3根线,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选)

四种通讯模式

SPI通讯协议一共有四种通讯模式,模式0、模式1、模式2以及模式3,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中CPOL参数规定了空闲状态(CS_N为高电平,设备未被选中)时SCK时钟信号的电平状态,CPHA规定了数据采样是在SCK时钟的奇数边沿还是偶数边沿。

SPI通讯协议的4种模式如下,通讯模式时序图,具体见图 46‑3。

模式0:CPOL= 0,CPHA=0。空闲状态时SCK串行时钟为低电平;数据采样在SCK时钟的奇数边沿,本模式中,奇数边沿为上升沿;数据更新在SCK时钟的偶数边沿,本模式中,偶数边沿为下降沿。

模式1:CPOL= 0,CPHA=1。空闲状态时SCK串行时钟为低电平;数据采样在SCK时钟的偶数边沿,本模式中,偶数边沿为下降沿;数据更新在SCK时钟的奇数边沿,本模式中,偶数边沿为上升沿。

模式2:CPOL= 1,CPHA=0。空闲状态时SCK串行时钟为高电平;数据采样在SCK时钟的奇数边沿,本模式中,奇数边沿为下降沿;数据更新在SCK时钟的偶数边沿,本模式中,偶数边沿为上升沿。

模式3:CPOL= 1,CPHA=1。空闲状态时SCK串行时钟为高电平;数据采样在SCK时钟的偶数边沿,本模式中,偶数边沿为上升沿;数据更新在SCK时钟的奇数边沿,本模式中,偶数边沿为下降沿。

我使用了CPOL=1,CPHA=0的模式,即sck闲高,下降沿采样,上升沿数据更新

spi_slave
module spi_slave(
   clk      ,  //50MHz时钟
   rst_n    ,  //复位
   data_in  ,  //要发送的数据
   data_out ,  //接收到的数据
   spi_sck  ,  //主机时钟
   spi_miso ,  //主收从发(从机)
   spi_mosi ,  //主发从收(从机)
   spi_cs   ,  //主机片选,低有效(从机)
   tx_en    ,  //发送使能
   tx_done  ,  //发送完成标志位
   rx_done     //接收完成标志位
);
//改DATA_W的参数即可实现任意字节的发送和接收,现在是两字节发送和接收
parameter DATA_W  =  16;
 
parameter SYNC_W  =  2;
 
//计数器参数
parameter CNT_W   =  4;
parameter CNT_N   =  DATA_W;
 
input                   clk;
input                   rst_n;
input    [DATA_W-1:0]   data_in;
input                   spi_sck;
input                   spi_mosi;
input                   spi_cs;
input                   tx_en;
 
output   [DATA_W-1:0]   data_out;
output                  spi_miso;
output                  tx_done;
output                  rx_done;
 
reg      [DATA_W-1:0]   data_out;
reg                     spi_miso;
reg                     tx_done;
reg                     rx_done;
 
//中间变量
reg      [SYNC_W-1:0]   spi_sync;
wire                    nedge;
wire                    pedge;
reg                     spi_mosi_reg;
reg                     spi_mosi_reg1;
//计数器变量
reg      [CNT_W-1:0]    cnt_rxbit;
wire                    add_cnt_rxbit;
wire                    end_cnt_rxbit;
 
reg      [CNT_W-1:0]    cnt_txbit;
wire                    add_cnt_txbit;
wire                    end_cnt_txbit;
reg                     tx_flag;
 
//边沿检测
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      //SCK时钟空闲状态位高电平,工作模式3
      spi_sync <= 2'b11;
   else
      spi_sync <= {spi_sync[0],spi_sck};
end
assign nedge = spi_sync[1:0] == 2'b10;
assign pedge = spi_sync[1:0] == 2'b01;
 
//上升沿接收,工作模式三
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_rxbit <= 0;
   else if(add_cnt_rxbit)begin
      if(end_cnt_rxbit)
         cnt_rxbit <= 0;
      else
         cnt_rxbit <= cnt_rxbit + 1'b1;
   end
end
assign add_cnt_rxbit = pedge;
assign end_cnt_rxbit = add_cnt_rxbit && cnt_rxbit == CNT_N - 1;
 
//下降沿发送,工作模式三
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_txbit <= 0;
   else if(add_cnt_txbit)begin
      if(end_cnt_txbit)
         cnt_txbit <= 0;
      else
         cnt_txbit <= cnt_txbit + 1'b1;
   end
end
assign add_cnt_txbit = nedge && tx_flag;
assign end_cnt_txbit = add_cnt_txbit && cnt_txbit == CNT_N - 1;
 
//因为异步信号同步化的原因,为了与延后的下降沿对齐,多打2拍
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      spi_mosi_reg <= 0;
   else
      spi_mosi_reg <= spi_mosi;
end
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      spi_mosi_reg1 <= 0;
   else
      spi_mosi_reg1 <= spi_mosi_reg;
end
 
//下降沿 接收主机发来的数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)  
      data_out <= 0;
   else if(!spi_cs)  // cs低电平使能
      data_out[CNT_N - 1 - cnt_rxbit ] <= spi_mosi_reg1;
end
 
//上升沿发送数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      spi_miso <= 0;
   else if(!spi_cs && tx_flag)
      spi_miso <= data_in[CNT_N - 1 - cnt_txbit];
   else
      spi_miso <= 0;
end
 
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      rx_done <= 0;
   else if(end_cnt_rxbit)
      rx_done <= 1;
   else
      rx_done <= 0;
end
 
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      tx_done <= 0;
   else if(end_cnt_txbit)
      tx_done <= 1;
   else
      tx_done <= 0;
end
 
 
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      tx_flag <= 0;
   else if(tx_en)
      tx_flag <= 1;
   else if(end_cnt_txbit)
      tx_flag <= 0;
end
 
endmodule
stm32 cubemx 配置

此处配置为全双工主机模式

注意:

1.spi的速率可根据实际情况修改,但需要注意的是,最好确保和fpga的spi时钟相差好几倍,保证传输的稳定性。

2.spi的通信模式注意和fpga要匹配

3.

串口配置 

 

代码简易示例(16bit)

定义变量

uint16_t aa[1];  //定义变量接收fpga发送的数据

while中 

	  			if (HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY)
			{
				HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
                //HAL_SPI_Receive(&hspi1,aa,2,1000);
				HAL_SPI_Receive(&hspi1,(uint8_t *)aa,1,10);
				//SendReceive16BitData(data);
				HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
				
				//HAL_UART_Transmit_DMA(&huart1,&a ,1);      //利用DMA发送
			}

			printf("%d\n",aa[0]);

借助vofa+,便可以直观看到fpga通过spi发送给stm32的数据,不过,由于printf比较慢,因此频率比较高的时候,可能会看不出波形。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo6.cn 版权所有 赣ICP备2024042791号-9

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务