1. SPI的基本知识
SPI(Serial Peripheral Interface)是一种同步串行通信协议,通常用于在嵌入式系统中连接微控制器和外部设备。它允许微控制器与多个外设同时进行全双工通信,实现高速数据传输。SPI协议通常用于连接存储器芯片、传感器、显示屏、通信模块和其他外设。
SPI协议的基本工作原理如下:微控制器通过主设备选择(SS)引脚选择要与之通信的外设,并通过时钟信号(SCLK)同步数据传输。它还使用主输出主输入(MOSI)和主输入主输出(MISO)引脚进行双向数据传输。在传输过程中,主设备将数据位传输到外设的输入引脚,同时从外设的输出引脚读取数据位。
SPI协议具有以下特点:
-
简单灵活:SPI协议使用少量的引脚和简单的硬件电路,易于实现和调试。
-
高速通信:由于采用同步传输方式,SPI协议可以实现高速数据传输,适用于实时应用。
-
可靠性好:SPI协议具有错误检测和纠正机制,可以提供可靠的数据传输。
-
多设备支持:SPI协议使用主从结构,一个主设备可以连接多个从设备,使得系统具备良好的扩展性。
SPI协议的配置通常由以下参数确定:
-
时钟极性(CPOL):确定时钟信号的空闲状态和活动状态。
-
时钟相位(CPHA):确定数据采样的时机。
-
数据位顺序:确定数据位的传输顺序,可以是最高位先传输(MSB-first)或最低位先传输(LSB-first)。
在实际应用中,SPI协议根据不同的硬件平台和设备需求进行配置。通过正确配置参数,微控制器可以与外设进行可靠的通信和数据交换。
2. ADC读写实验
2.1 ADC128S022产品信息
ADC128S022器件是一种低功耗、8通道CMOS 12位模数转换器,其转换吞吐量为50 ksps到200 ksps。该转换器是基于一个逐次逼近寄存器结构与一个内部跟踪和保持电路。它可以配置为在输入IN0到IN7接受多达8个输入信号。串行数据的输出是直接二进制,并兼容多种标准,如SPI, QSPI, MICROWIRE和许多常见的DSP串行接口。 ADC128S022可使用独立的模拟和数字电源。模拟电源(VA)的电压范围是2.7 V到5.25 V,数字电源(VD)的电压范围是2.7 V到VA。使用3v或5v电源的正常功耗分别为1.2 mW和7.5 mW。当使用3v电源时,功耗为0.06µW;当使用5v电源时,功耗为0.25µW确保在-40°C至+105°C的扩展工业温度范围内运行。
2.2 ADC128S022的时序图
CS:片选信号,低电平表示选中。
SCLK:输入时钟。
DIN:串行输入,SCLK的上升沿进行采集。
DOUT:转换输出,SCLK的下降沿进行数据输出。
2.3 实验代码
module SPI(
input clk,
input rst_n,
input start,
input [2:0]channel,
//========ADC128S022===========//
output reg SCLK,
output reg DIN,
output reg CS_N,
input DOUT,
output reg done,
output reg [11:0]data
);
reg en;
reg [2:0]r_channel;
reg [4:0]cnt;
reg cnt_flag;
reg [5:0]SCLK_CNT;
reg [11:0]r_data;
//==============写入通道===================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
r_channel <= 'd0;
else if(start)
r_channel <= channel;
else
r_channel <= r_channel;
end
//============转换使能信号==================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
en <= 1'b0;
else if(start)
en <= 1'b1;
else if(done)
en <= 1'b0;
else
en <= en;
end
//==================cnt=====================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 'd0;
else if(en)begin
if(cnt == 'd10)
cnt <= 'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 'd0;
end
//===================cnt_flag===============//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_flag <= 1'b0;
else if(cnt == 'd10)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
end
//===============SCLK_CNT===================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
SCLK_CNT <= 'd0;
else if(en)begin
if(cnt_flag && (SCLK_CNT == 'd33))
SCLK_CNT <= 'd0;
else if(cnt_flag)
SCLK_CNT <= SCLK_CNT + 1'b1;
else
SCLK_CNT <= SCLK_CNT;
end
else
SCLK_CNT <= 'd0;
end
//===========================================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
SCLK <= 1'b1;
CS_N <= 1'b1;
DIN <= 1'b1;
end
else if(en)begin
if(cnt_flag)begin
case(SCLK_CNT)
6'd0:begin CS_N <= 1'b0;end
6'd1:begin SCLK <= 1'b0;DIN <= 1'b0;end
6'd2:begin SCLK <= 1'b1;end
6'd3:begin SCLK <= 1'b0;end
6'd4:begin SCLK <= 1'b1;end
//写入通道
6'd5:begin SCLK <= 1'b0;DIN <= r_channel[2];end
6'd6:begin SCLK <= 1'b1;end
6'd7:begin SCLK <= 1'b0;DIN <= r_channel[1];end
6'd8:begin SCLK <= 1'b1;end
6'd9:begin SCLK <= 1'b0;DIN <= r_channel[0];end
//写入数据,在上升沿使数据逐次左移 6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32:
begin SCLK <= 1'b1;r_data <= {r_data[10:0],DOUT};end
6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd31:
begin SCLK <= 1'b0;end
//传输结束,结束选中
6'd33:begin CS_N <= 1'b1;end
default:begin CS_N <= 1'b1;end
endcase
end
else ;
end
else begin
SCLK <= 1'b1;
CS_N <= 1'b1;
DIN <= 1'b1;
end
end
//================done=================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
done <= 1'b0;
else if(cnt_flag && (SCLK_CNT == 'd33))
done <= 1'b1;
else
done <= 1'b0;
end
//================data=================//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data <= 'd0;
else if(cnt_flag && (SCLK_CNT == 'd33))
data <= r_data;
else
data <= data;
end
endmodule
注:这部分代码参考自b站FPGA小学生。
2.4 测试文件
`timescale 1ns/1ns
module SPI_tb;
reg clk;
reg rst_n;
reg start;
reg [2:0]channel;
wire SCLK;
wire DIN;
wire CS_N;
reg DOUT;
wire done;
wire [11:0]data;
reg i = 0;
initial clk = 1'b1;
always#10 clk = ~clk;
SPI SPI_inst(
.clk(clk),
.rst_n(rst_n),
.start(start),
.channel(channel),
//========ADC128S022===========//
.SCLK(SCLK),
.DIN(DIN),
.CS_N(CS_N),
.DOUT(DOUT),
.done(done),
.data(data)
);
initial begin
rst_n = 1'b0;
channel = 'd0;
start = 1'b0;
DOUT = 1'b0;
#100;
rst_n = 1'b1;
#100;
channel = 3;
for(i=0;i<3;i=i+1)begin
start = 1;
#20;
start = 0;
gene_DOUT(12'b0101_1100_1010); //依次将存储器中存储的波形读出,按照ADC的转换结果输出方式送到DOUT信号线上
@(posedge done); //等待转换完成信号
#200;
end
#20000;
$stop;
end
//将并行数据按照ADC的数据输出格式,送到DOUT信号线上,供控制模块采集读取
task gene_DOUT;
input [15:0]vdata;
reg [4:0]cnt;
begin
cnt = 0;
wait(!CS_N);
while(cnt<16)begin
@(negedge SCLK) DOUT = vdata[15-cnt];
cnt = cnt + 1'b1;
end
end
endtask
endmodule
3. FLASH读写实验
3.1 FLASH的基本介绍
FLASH闪存是一种非易失性存储器,常用于存储数据并替代传统的磁盘驱动器。它具有许多优点,例如速度快、耐用性高和体积小。其具有内部时钟,可确保数据在正确的时间段写入和读取,同时提供更高的数据传输速度和准确性,还有助于协调闪存与其他设备之间的数据传输。对其主要操作包括擦除、写入和读取。因为闪存内部的存储单元被分为多个块,每个块都可以独立操作,使得闪存在处理大量数据时表现出色。
图 FLASH的连接方式
3.2 FLASH的基本指令
注:其他有关SPI和FLASH的基础内容请移步至前章的STM32通信总结。
3.3 实验代码
3.3.1 SPI驱动代码
值得注意的是这里需要区分delay_cnt、bit_cnt和clk_cnt,具体如下图所示。
delay_cnt和clk_cnt均为1位,delay_cnt更多是作为延时开始的标志,而bit_clk会在clk_cnt高电平时加一,进而切换输出的位号。根据FLASH定义的时序,上电后会需要一段时间的延时等待,接着是8位的命令、24位的地址以及后续的数据,这也说明了在32位后才真正开始进行数据的传输。文章来源:https://www.toymoban.com/news/detail-854034.html
module spi_drive(
input clk_100m ,
input sys_rst_n ,
//user interface
input spi_start ,//spi开启使能。
input [7:0 ] spi_cmd ,//FLAH操作指令
input [23:0] spi_addr ,//FLASH地址
input [7:0 ] spi_data ,//FLASH写入的数据
input [3:0 ] cmd_cnt ,
output idel_flag_r ,//空闲状态标志的上升沿
output reg w_data_req ,//FLASH写数据请求
output reg [7:0] r_data ,//FLASH读出的数据
output reg erro_flag ,//读出的数据错误标志
//spi interface
output reg spi_cs ,//SPI从机的片选信号,低电平有效。
output reg spi_clk ,//主从机之间的数据同步时钟。
output reg spi_mosi ,//数据引脚,主机输出,从机输入。
input spi_miso //数据引脚,主机输入,从机输出。
);
//状态机
parameter IDLE =4'd0;//空闲状态
parameter WEL =4'd1;//写使能状态
parameter S_ERA =4'd2;//扇区擦除状态
parameter C_ERA =4'd3;//全局擦除
parameter READ =4'd4;//读状态
parameter WRITE =4'd5;//写状态
parameter R_STA_REG =4'd6;
//指令集
parameter WEL_CMD =8'h06;
parameter S_ERA_CMD =8'h20;
parameter C_ERA_CMD =8'hc7;
parameter READ_CMD =8'h03;
parameter WRITE_CMD =8'h02;
parameter R_STA_REG_CMD=8'h05;
//wire define
wire idel_flag;
//reg define
reg[3:0] current_state ;
reg[3:0] next_state ;
reg[7:0 ] data_buffer ;
reg[7:0 ] cmd_buffer ;
reg[7:0 ] sta_reg ;
reg[23:0] addr_buffer ;
reg[31:0] bit_cnt ;
reg clk_cnt ;
reg dely_cnt ;
reg[31:0] dely_state_cnt ;
reg[7:0 ] rd_data_buffer ;
reg spi_clk0 ;
reg stdone ;
reg[7:0 ] data_check ;
reg idel_flag0 ;
reg idel_flag1 ;
//*****************************************************
//** main code
//*****************************************************
assign idel_flag=(current_state==IDLE)?1:0;//空闲状态标志
assign idel_flag_r=idel_flag0&&(~idel_flag1);//空闲状态标志的上升沿
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)begin
idel_flag0<=1'b1;
idel_flag1<=1'b1;
end
else begin
idel_flag0<=idel_flag;
idel_flag1<=idel_flag0;
end
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
w_data_req<=1'b0;
else if((bit_cnt+2)%8==0&&bit_cnt>=30&&clk_cnt==0&¤t_state==WRITE)
w_data_req<=1'b1;
else
w_data_req<=1'b0;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据移位寄存
if(!sys_rst_n)
rd_data_buffer<=8'd0;
else if(bit_cnt>=32&&bit_cnt<=2080&&clk_cnt==0&¤t_state==READ)
rd_data_buffer<={rd_data_buffer[6:0],spi_miso};
else
rd_data_buffer<=rd_data_buffer;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//检查读出的数据是否正确
if(!sys_rst_n)
data_check<=8'd0;
else if(bit_cnt%8==0&&bit_cnt>=40&&clk_cnt==1&¤t_state==READ)
data_check<=data_check+1'd1;
else
data_check<=data_check;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据
if(!sys_rst_n)
r_data<=8'd0;
else if(bit_cnt%8==0&&bit_cnt>38&&clk_cnt==1&¤t_state==READ)
r_data<=rd_data_buffer;
else
r_data<=r_data;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据错误标志
if(!sys_rst_n)
erro_flag<=1'd0;
else if(bit_cnt>32&&bit_cnt<=2080&¤t_state==READ&&cmd_cnt==6)begin
if(data_check!=r_data)
erro_flag<=1'd1;
else
erro_flag<=erro_flag;
end
else
erro_flag<=erro_flag;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
data_buffer<=8'd0;
else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)
data_buffer<=spi_data;
else if(clk_cnt==1&¤t_state==WRITE&&bit_cnt>=32)
data_buffer<={data_buffer[6:0],data_buffer[7]};
else
data_buffer<=data_buffer;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
cmd_buffer<=8'd0;
else if(spi_cs==0&&dely_cnt==0)
cmd_buffer<=spi_cmd;
else if(clk_cnt==1&&(current_state==WEL||current_state==S_ERA||current_state==C_ERA
||current_state==READ||current_state==WRITE||current_state==R_STA_REG)&&bit_cnt<8)
cmd_buffer<={cmd_buffer[6:0],1'b1};
else
cmd_buffer<=cmd_buffer;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
addr_buffer<=8'd0;
else if(spi_cs==0&&dely_cnt==0)
addr_buffer<=spi_addr;
else if(clk_cnt==1&&(current_state==READ||current_state==WRITE)&&bit_cnt>=8&&bit_cnt<32)
addr_buffer<={addr_buffer[22:0],addr_buffer[23]};
else
addr_buffer<=addr_buffer;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
clk_cnt<=1'd0;
else if(dely_cnt==1)
clk_cnt<=clk_cnt+1'd1;
else
clk_cnt<=1'd0;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
dely_cnt<=1'd0;
else if(spi_cs==0)begin
if(dely_cnt<1)
dely_cnt<=dely_cnt+1'd1;
else
dely_cnt<=dely_cnt;
end
else
dely_cnt<=1'd0;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
dely_state_cnt<=1'd0;
else if(spi_cs)begin
if(dely_state_cnt<400000000)
dely_state_cnt<=dely_state_cnt+1'd1;
else
dely_state_cnt<=dely_state_cnt;
end
else
dely_state_cnt<=1'd0;
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
bit_cnt<=11'd0;
else if(dely_cnt==1)begin
if(clk_cnt==1'b1)
bit_cnt<=bit_cnt+1'd1;
else
bit_cnt<=bit_cnt;
end
else
bit_cnt<=11'd0;
end
//三段式状态机
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
current_state<=IDLE;
else
current_state<=next_state;
end
always @(*)begin
case(current_state)
IDLE: begin
if(spi_start&&spi_cmd==WEL_CMD)
next_state=WEL;
else if(spi_start&&spi_cmd==C_ERA_CMD)
next_state=C_ERA;
else if(spi_start&&spi_cmd==S_ERA_CMD)
next_state=S_ERA;
else if(spi_start&&spi_cmd==READ_CMD)
next_state=READ;
else if(spi_start&&spi_cmd==WRITE_CMD)
next_state=WRITE;
else if(spi_start&&spi_cmd==R_STA_REG_CMD)
next_state=R_STA_REG;
else
next_state=IDLE;
end
WEL: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=WEL;
end
S_ERA: begin
if(stdone)
next_state=IDLE;
else
next_state=S_ERA;
end
C_ERA: begin
if(stdone)
next_state=IDLE;
else
next_state=C_ERA;
end
READ: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=READ;
end
WRITE: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=WRITE;
end
R_STA_REG: begin
if(stdone)
next_state=IDLE;
else
next_state=R_STA_REG;
end
default: next_state=IDLE;
endcase
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n) begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_clk0<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b0;
end
else begin
case(current_state)
IDLE: begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
WEL: begin
stdone<=1'b0;
spi_cs<=1'b0;
if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8&&clk_cnt==0)begin
stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
else if(bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
end
end
C_ERA: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8&&clk_cnt==0)begin
stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
else if(bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
end
end
S_ERA: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_cs<=1'b0;
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt==32&&clk_cnt==0) begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
end
READ: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt>=32&&bit_cnt<2080)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=1'b0;
end
else if(bit_cnt==2080&&clk_cnt==0) begin
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
else if(bit_cnt==2080&&clk_cnt==1) begin
spi_cs<=1'b1;
end
end
WRITE: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt>=32&&bit_cnt<2080)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=data_buffer[7];
end
else if(bit_cnt==2080&&clk_cnt==0) begin
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
else if(bit_cnt==2080&&clk_cnt==1) begin
spi_cs<=1'b1;
end
end
R_STA_REG:begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=1'b0;
end
else if(~spi_miso&&bit_cnt%8==0)begin
spi_clk<=1'b0;
spi_cs<=1'b1;
stdone<=1'b1;
end
else if(~spi_cs&&dely_cnt==1)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
end
end
default: begin
stdone<=1'b0;
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_clk0<=1'b0;
spi_mosi<=1'b0;
end
endcase
end
end
endmodule
3.3.2 FLASH读写代码
module flash_rw(
input sys_clk ,
input sys_rst_n ,
input idel_flag_r ,//抓取空闲状态上升沿
input w_data_req ,//写请求
output reg[3:0 ] cmd_cnt ,//计数器
output reg spi_start ,//spi开启使能
output reg[7:0 ] spi_cmd ,//spi命令号
output reg[7:0 ] spi_data
);
//指令集
parameter WEL_CMD =16'h06;//允许写
parameter S_ERA_CMD =16'h20;//扇擦除
parameter C_ERA_CMD =16'hc7;//块擦除
parameter READ_CMD =16'h03;//读数据
parameter WRITE_CMD =16'h02;//写数据
parameter R_STA_REG_CMD=8'h05 ;//读状态寄存器
//reg define
reg[3:0] flash_start;
//*****************************************************
//** main code
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n )begin //延时
if(!sys_rst_n)
flash_start<=0;
else if(flash_start<=10)
flash_start<=flash_start+1;
else
flash_start<=flash_start;
end
always @(posedge sys_clk or negedge sys_rst_n )begin
if(!sys_rst_n)
cmd_cnt<=0;
else if(flash_start==9) //生成第一次flash_start
spi_start<=1'b1;
else if(idel_flag_r&&cmd_cnt<10)begin //状态抓取idel_flag_r状态
cmd_cnt<=cmd_cnt+1; //切换下一次指令
spi_start<=1'b1;
end
else begin
cmd_cnt<=cmd_cnt;
spi_start<=1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n )begin //数据逐次加一
if(!sys_rst_n)
spi_data<=8'd0;
else if(w_data_req)
spi_data<=spi_data+1'b1;
else
spi_data<=spi_data;
end
always @(*)begin //赋值命令
case(cmd_cnt)
0:spi_cmd=WEL_CMD;
1:spi_cmd=C_ERA_CMD;
2:spi_cmd=R_STA_REG_CMD;
3:spi_cmd=WEL_CMD;
4:spi_cmd=WRITE_CMD;
5:spi_cmd=R_STA_REG_CMD;
6:spi_cmd=READ_CMD;
7:spi_cmd=WEL_CMD;
8:spi_cmd=S_ERA_CMD;
9:spi_cmd=R_STA_REG_CMD;
10:spi_cmd=READ_CMD;
default:;
endcase
end
endmodule
免责声明:本文所引用的各种资料均用于自己学习使用,这里感谢黑金和正点原子官方的资料以及各位优秀的创作者。文章来源地址https://www.toymoban.com/news/detail-854034.html
到了这里,关于学习笔记之FPGA的SPI通讯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!