STM32與FPGA通過SPI通訊
阿新 • • 發佈:2019-01-01
MCU通過該SPI介面和FPGA通訊,MCU可以讀寫4個暫存器:以及連續讀寫N(1-255)位元組資料,當然可以增加更多的暫存器。
MCU訪問FPGA方式:
寫暫存器時:在SPI_DI上傳送 CMD + PARA 資料流
讀暫存器時:在SPI_DI上傳送 CMD + DUMMY資料流,在第2位元組SPI_DO上會所需的資料流
module STM32_FPGA_SPI(
input main_clk,
output arm_clk,
// input clk,
// input [7:0] data_in,
input spi_cs,
input spi_clk,
input spi_di, //MOSI
output spi_do, //MISO
output led
);
reg [24:0] led_cnt = 0;
always @(posedge clk)
led_cnt <= led_cnt + 1'b1;
assign led = led_cnt[24]; //閃燈,無關緊要
pll_8_50M pll_8_50M_inst (
.inclk0 ( main_clk ),//25M
.c0 ( arm_clk ), //8M
.c1 ( clk ) //50M
);
//定義讀寫暫存器cmd
parameter ARM_FPGA_REG1_ADDR = 8'h10;
parameter ARM_FPGA_REG2_ADDR = 8'h20;
parameter ARM_FPGA_REG3_ADDR = 8'h30;
parameter ARM_FPGA_REG4_ADDR = 8'h40;
parameter ARM_FPGA_BUF_READ_ADDR = 8'h70; //連續讀命令
reg [7:0] ARM_FPGA_REG1 = 0;
reg [7:0] ARM_FPGA_REG2 = 0;
reg [7:0] ARM_FPGA_REG3 = 0;
reg [7:0] ARM_FPGA_REG4 = 0;
reg [7:0] ARM_FPGA_BUF_NUM = 0;
reg spi_clk_tmp1 = 0;
reg spi_clk_tmp2 = 0;
reg spi_clk_tmp3 = 0;
always @(posedge clk) //3級同步,比較可靠,2級也可以
begin
// spi_clk_tmp1 <= spi_clk;
// spi_clk_tmp2 <= spi_clk_tmp1;
spi_clk_tmp2 <= spi_clk;
spi_clk_tmp3 <= spi_clk_tmp2;
end
wire spi_clk_pos = (~spi_clk_tmp3) & spi_clk_tmp2;//上升沿
wire spi_clk_neg = (~spi_clk_tmp2) & spi_clk_tmp3;//下降沿
reg [2:0] state = 0;
reg [7:0] cmd_reg = 0;
reg [7:0] reg_data = 0;
reg [7:0] out_data = 0;
reg [3:0] cnt = 0;
reg [8:0] read_cnt = 0;
reg spi_di_tmp1 = 0;
reg spi_di_tmp2 = 0;
always @(posedge clk)
begin
// spi_di_tmp1 <= spi_di;
// spi_di_tmp2 <= spi_di_tmp1;
spi_di_tmp2 <= spi_di;
end
always @(posedge clk)
begin
if(~spi_cs)
case(state)
0 : if(spi_clk_pos)
begin
cmd_reg <= {cmd_reg[6:0],spi_di_tmp2}; //也可以將spi_di寄存
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 1;
end
end
1 : begin
case(cmd_reg)
ARM_FPGA_REG1_ADDR : if(spi_clk_pos) //寫FPGA暫存器是上升沿 begin ARM_FPGA_REG1 <= {ARM_FPGA_REG1[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG2_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG2 <= {ARM_FPGA_REG2[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG3_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG3 <= {ARM_FPGA_REG3[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG4_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG4 <= {ARM_FPGA_REG4[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_BUF_READ_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_BUF_NUM <= {ARM_FPGA_BUF_NUM[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 3;
end
end
/*以下為暫存器讀*/
ARM_FPGA_REG1_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG1;
state <= 2;
end
ARM_FPGA_REG2_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG2;
state <= 2;
end
ARM_FPGA_REG3_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG3;
state <= 2;
end
ARM_FPGA_REG4_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG4;
state <= 2;
end
default : state<= 0; endcase
end
2 : begin //移位傳送
if(spi_clk_neg)
begin
out_data <= {out_data[6:0],1'b0};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
end
3 : begin //連續讀裝載第一個初值
if(spi_clk_neg)
begin
out_data <= read_cnt;//data_in;
if(read_cnt == ARM_FPGA_BUF_NUM)
begin
read_cnt <= 0;
state <= 0;
end
else
state <= 4;
end
end
4 : begin //移位傳送
if(spi_clk_neg)
begin
out_data <= {out_data[6:0],1'b0};
cnt <= cnt + 1;
if(cnt == 6) //6就可以了
begin
cnt <= 0;
read_cnt <= read_cnt + 1;
state <= 3;
end
end
end
default : state <= 0;
endcase
end
assign spi_do = out_data[7]; //SPI傳送
endmodule
MCU驅動:
基本的SPI模式設定
CPOL_Low
CPHA_1Edge
SPI_DataSize_8b
SPI_FirstBit_MSB
/*******************************************************************************
最基本的SPI傳送與接收函式,其他函式都以其為基礎
通過SPI MOSI傳送一位元組,同時返回從MISO讀的一位元組,寫與讀共用8個clk
*******************************************************************************/
u8 SPI_FPGA_SendByte(u8 byte)
{
/* Loop while DR register in not emplty */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI1, byte);
/* Wait to receive a byte */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI1);
}
u8 SPI_FPGA_ReadByte(void)
{
return (SPI_FPGA_SendByte(Dummy_Byte));
}
void SPI_FPGA_Write_Reg(u8 cmd, u8 para)
{
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(cmd);
SPI_FPGA_SendByte(para);
SPI_FPGA_CS_HIGH();
}
u8 SPI_FPGA_Read_Reg(u8 cmd)
{
u8 reg_val = 0xFF;
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(cmd | 0x01);
reg_val = SPI_FPGA_ReadByte(); //dummy
SPI_FPGA_CS_HIGH();
return(reg_val);
}
/*讀取 N 位元組*/
void SPI_FPGA_Buf_Read(u8* pBuffer, u8 num)
{
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(0x70);//讀取一行 0x70命令
SPI_FPGA_SendByte(num); //dummy
while(num--)
{
*pBuffer = SPI_FPGA_ReadByte(); //dummy
pBuffer++;
}
SPI_FPGA_CS_HIGH();
}
實際測試發現3級寄存時,在4.5M的SPI時鐘下工作穩定,理論上可以達到7、8M,需要檢驗,但是9M時已經不行了。3級寄存改為2級寄存後,測試發現9M的spi_clk沒有問題,這時理論可以達到12M。
MCU訪問FPGA方式:
寫暫存器時:在SPI_DI上傳送 CMD + PARA 資料流
讀暫存器時:在SPI_DI上傳送 CMD + DUMMY資料流,在第2位元組SPI_DO上會所需的資料流
module STM32_FPGA_SPI(
input main_clk,
output arm_clk,
// input clk,
// input [7:0] data_in,
input spi_cs,
input spi_clk,
input spi_di, //MOSI
output spi_do, //MISO
output led
);
reg [24:0] led_cnt = 0;
always @(posedge clk)
led_cnt <= led_cnt + 1'b1;
assign led = led_cnt[24]; //閃燈,無關緊要
pll_8_50M pll_8_50M_inst (
.inclk0 ( main_clk ),//25M
.c0 ( arm_clk ), //8M
.c1 ( clk ) //50M
);
//定義讀寫暫存器cmd
parameter ARM_FPGA_REG1_ADDR = 8'h10;
parameter ARM_FPGA_REG2_ADDR = 8'h20;
parameter ARM_FPGA_REG3_ADDR = 8'h30;
parameter ARM_FPGA_REG4_ADDR = 8'h40;
parameter ARM_FPGA_BUF_READ_ADDR = 8'h70; //連續讀命令
reg [7:0] ARM_FPGA_REG1 = 0;
reg [7:0] ARM_FPGA_REG2 = 0;
reg [7:0] ARM_FPGA_REG3 = 0;
reg [7:0] ARM_FPGA_REG4 = 0;
reg [7:0] ARM_FPGA_BUF_NUM = 0;
reg spi_clk_tmp1 = 0;
reg spi_clk_tmp2 = 0;
reg spi_clk_tmp3 = 0;
always @(posedge clk) //3級同步,比較可靠,2級也可以
begin
// spi_clk_tmp1 <= spi_clk;
// spi_clk_tmp2 <= spi_clk_tmp1;
spi_clk_tmp2 <= spi_clk;
spi_clk_tmp3 <= spi_clk_tmp2;
end
wire spi_clk_pos = (~spi_clk_tmp3) & spi_clk_tmp2;//上升沿
wire spi_clk_neg = (~spi_clk_tmp2) & spi_clk_tmp3;//下降沿
reg [2:0] state = 0;
reg [7:0] cmd_reg = 0;
reg [7:0] reg_data = 0;
reg [7:0] out_data = 0;
reg [3:0] cnt = 0;
reg [8:0] read_cnt = 0;
reg spi_di_tmp1 = 0;
reg spi_di_tmp2 = 0;
always @(posedge clk)
begin
// spi_di_tmp1 <= spi_di;
// spi_di_tmp2 <= spi_di_tmp1;
spi_di_tmp2 <= spi_di;
end
always @(posedge clk)
begin
if(~spi_cs)
case(state)
0 : if(spi_clk_pos)
begin
cmd_reg <= {cmd_reg[6:0],spi_di_tmp2}; //也可以將spi_di寄存
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 1;
end
end
1 : begin
case(cmd_reg)
ARM_FPGA_REG1_ADDR : if(spi_clk_pos) //寫FPGA暫存器是上升沿 begin ARM_FPGA_REG1 <= {ARM_FPGA_REG1[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG2_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG2 <= {ARM_FPGA_REG2[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG3_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG3 <= {ARM_FPGA_REG3[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_REG4_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_REG4 <= {ARM_FPGA_REG4[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
ARM_FPGA_BUF_READ_ADDR : if(spi_clk_pos)
begin
ARM_FPGA_BUF_NUM <= {ARM_FPGA_BUF_NUM[6:0],spi_di_tmp2};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 3;
end
end
/*以下為暫存器讀*/
ARM_FPGA_REG1_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG1;
state <= 2;
end
ARM_FPGA_REG2_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG2;
state <= 2;
end
ARM_FPGA_REG3_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG3;
state <= 2;
end
ARM_FPGA_REG4_ADDR + 1'b1 : if(spi_clk_neg) //讀FPGA暫存器是下降沿
begin
out_data <= ARM_FPGA_REG4;
state <= 2;
end
default : state<= 0; endcase
end
2 : begin //移位傳送
if(spi_clk_neg)
begin
out_data <= {out_data[6:0],1'b0};
cnt <= cnt + 1;
if(cnt == 7)
begin
cnt <= 0;
state <= 0;
end
end
end
3 : begin //連續讀裝載第一個初值
if(spi_clk_neg)
begin
out_data <= read_cnt;//data_in;
if(read_cnt == ARM_FPGA_BUF_NUM)
begin
read_cnt <= 0;
state <= 0;
end
else
state <= 4;
end
end
4 : begin //移位傳送
if(spi_clk_neg)
begin
out_data <= {out_data[6:0],1'b0};
cnt <= cnt + 1;
if(cnt == 6) //6就可以了
begin
cnt <= 0;
read_cnt <= read_cnt + 1;
state <= 3;
end
end
end
default : state <= 0;
endcase
end
assign spi_do = out_data[7]; //SPI傳送
endmodule
MCU驅動:
基本的SPI模式設定
CPOL_Low
CPHA_1Edge
SPI_DataSize_8b
SPI_FirstBit_MSB
/*******************************************************************************
最基本的SPI傳送與接收函式,其他函式都以其為基礎
通過SPI MOSI傳送一位元組,同時返回從MISO讀的一位元組,寫與讀共用8個clk
*******************************************************************************/
u8 SPI_FPGA_SendByte(u8 byte)
{
/* Loop while DR register in not emplty */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI1, byte);
/* Wait to receive a byte */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI1);
}
u8 SPI_FPGA_ReadByte(void)
{
return (SPI_FPGA_SendByte(Dummy_Byte));
}
void SPI_FPGA_Write_Reg(u8 cmd, u8 para)
{
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(cmd);
SPI_FPGA_SendByte(para);
SPI_FPGA_CS_HIGH();
}
u8 SPI_FPGA_Read_Reg(u8 cmd)
{
u8 reg_val = 0xFF;
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(cmd | 0x01);
reg_val = SPI_FPGA_ReadByte(); //dummy
SPI_FPGA_CS_HIGH();
return(reg_val);
}
/*讀取 N 位元組*/
void SPI_FPGA_Buf_Read(u8* pBuffer, u8 num)
{
SPI_FPGA_CS_LOW();
SPI_FPGA_SendByte(0x70);//讀取一行 0x70命令
SPI_FPGA_SendByte(num); //dummy
while(num--)
{
*pBuffer = SPI_FPGA_ReadByte(); //dummy
pBuffer++;
}
SPI_FPGA_CS_HIGH();
}
實際測試發現3級寄存時,在4.5M的SPI時鐘下工作穩定,理論上可以達到7、8M,需要檢驗,但是9M時已經不行了。3級寄存改為2級寄存後,測試發現9M的spi_clk沒有問題,這時理論可以達到12M。