目录
概述
FPGA的SPI主机代码
STM32从机
SPI模式配置
SPI参数设置
SPI的DMA传输配置
STM32从机SPI接收代码
概述
不说一些SPI原理之类的废话,浪费空间。我使用的硬件环境为STM32F407VET6和DE0-nano,长什么样子如下图。
使用cubemx配置工程,FPGA使用Quartus软件,时序仿真图如下
FPGA的SPI主机代码
txd_signal信号为上升沿触发,led0,1,2为调试灯,可去掉。
一个SCK周期为16个clk周期,我测试时使用50MHz晶振,故SPI时钟为3.125MHz。
需要改动通信速率可以通过改动cnt1相关值改变。
时钟相位参考STM32从机配置
//时钟极性高,采样沿上升沿
module SPI_Master(
clk, // 系统时钟
rst, // 复位,低有效
txd_flag, // 高电平表示发送完成处于忙碌状态,低电平表示处于空闲状态
sck, // 时钟输出
cs, // 片选输出
mosi, // 主设备输出信号
miso, // 主设备输入信号
txd_signal, // 发送触发信号,给一个信号发送8bit
rxd_out, // 接收的数据
txd_in, // 发送的数据
led0,
led1,
led2
);
//调试引脚
output led0,led1,led2;
assign led0=txd_signal;
assign led1=txd_flag;
assign led2=cs;
// 输入信号
input clk, rst, miso, txd_signal;
// 输出信号
output reg mosi, txd_flag;
output wire sck, cs;
output reg [7:0] rxd_out;
input [7:0] txd_in;
// 内部寄存器和变量
reg [2:0] txd_start;
reg [2:0] txd_start_outside;
reg [3:0] cnt;
reg [3:0] cnt1;
reg csr;
assign sck = cnt1[3];
assign cs = csr;
// 任务:根据计数器 cnt1 的值更新时钟状态
task clk_states;
if (cnt1 == 4'b0000) begin
cnt1 <= 4'b1111;
cnt <= cnt + 1'b1;
end else
cnt1 <= cnt1 - 1'b1;
endtask
// always 块:根据时钟和复位信号控制发送过程和计数器的更新
always @(posedge clk or negedge rst) begin
if (!rst) begin
// 复位所有状态和寄存器
txd_flag <= 1'b0;
txd_start <= 3'd1;
cnt <= 4'd0;
cnt1 <= 4'b1111;
csr <= 1'b1;
end else begin
if (txd_start == txd_start_outside) begin
csr <= 1'b0;
txd_flag <= 1'b1; // txd_flag 信号与 cs 信号相反,电路当处于通信时为高电平,表示忙碌状态
case (cnt) // 为低电平表示空闲状态
0: clk_states();//进八次cnt加1,SCK经过一个周期,SCK高电平持续时间为4和CLK
1: clk_states();
2: clk_states();
3: clk_states();
4: clk_states();
5: clk_states();
6: clk_states();
7: begin
if (cnt1 == 4'b0000) begin
cnt1 <= 4'b1111;
cnt <= 4'd8;
end else
cnt1 <= cnt1 - 1'b1;
end
8: begin
// 多加一个状态延长 cs 的低电平时间,给从机足够的时间接收数据
txd_start <= txd_start + 1'b1;
cnt <= 4'd0;
end
endcase
end else begin
csr <= 1'b1;
txd_flag <= 1'b0;
end
end
end
// always 块:外部触发发送脉冲,上升沿触发发送一次
always @(posedge txd_signal or negedge rst) begin
if (!rst)
txd_start_outside <= 1'b0;
else begin
if (!txd_flag)
txd_start_outside <= txd_start_outside + 1'b1;
else;
end
end
// always 块:sck 上升沿采样数据
reg [7:0] rxd_outr;
reg [2:0] rec_cnt;
always @(posedge sck or negedge rst) begin
if (!rst) begin
rxd_out <= 8'h00;
rec_cnt <= 3'd0;
end else begin
case (rec_cnt)
0: begin
rxd_outr[7] <= miso;
rec_cnt <= 3'd1;
end
1: begin
rxd_outr[6] <= miso;
rec_cnt <= 3'd2;
end
2: begin
rxd_outr[5] <= miso;
rec_cnt <= 3'd3;
end
3: begin
rxd_outr[4] <= miso;
rec_cnt <= 3'd4;
end
4: begin
rxd_outr[3] <= miso;
rec_cnt <= 3'd5;
end
5: begin
rxd_outr[2] <= miso;
rec_cnt <= 3'd6;
end
6: begin
rxd_outr[1] <= miso;
rec_cnt <= 3'd7;
end
7: begin
rxd_outr[0] <= miso;
rxd_out <= {rxd_outr[7:1], miso};
rec_cnt <= 3'd0;
end
default:;
endcase
end
end
// always 块:sck 下降沿时发送数据
reg [2:0] send_cnt;
always @(negedge sck or negedge rst) begin
if (!rst)
send_cnt <= 3'd0;
else begin
case (send_cnt)
0: begin
mosi <= txd_in[7];
send_cnt <= 3'd1;
end
1: begin
mosi <= txd_in[6];
send_cnt <= 3'd2;
end
2: begin
mosi <= txd_in[5];
send_cnt <= 3'd3;
end
3: begin
mosi <= txd_in[4];
send_cnt <= 3'd4;
end
4: begin
mosi <= txd_in[3];
send_cnt <= 3'd5;
end
5: begin
mosi <= txd_in[2];
send_cnt <= 3'd6;
end
6: begin
mosi <= txd_in[1];
send_cnt <= 3'd7;
end
7: begin
mosi <= txd_in[0];
send_cnt <= 3'd0;
end
default:;
endcase
end
end
endmodule
STM32从机
SPI模式配置
SPI参数设置
文章来源:https://www.toymoban.com/news/detail-783767.html
SPI的DMA传输配置
文章来源地址https://www.toymoban.com/news/detail-783767.html
STM32从机SPI接收代码
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
uint8_t data=0;
HAL_SPI_Receive_DMA(&hspi1,&data,1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//FPGA RESET
HAL_GPIO_WritePin(RST_GPIO_Port,RST_Pin,GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(RST_GPIO_Port,RST_Pin,GPIO_PIN_SET);
//等待发送空闲
while(HAL_GPIO_ReadPin(txd_flag_GPIO_Port,txd_flag_Pin)==GPIO_PIN_SET){
};
//延迟便于观察串口输出
HAL_Delay(2000);
//上升沿触发FPGA主机发送
HAL_GPIO_WritePin(txd_signal_GPIO_Port,txd_signal_Pin,GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(txd_signal_GPIO_Port,txd_signal_Pin,GPIO_PIN_RESET);
//串口打印data查看数据
printf("data is %d \r\n",data);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
到了这里,关于FPGA主SPI与STM32从机通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!