SPI协议之擦除flash芯片数据

SPI协议是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。一主多从spi设备图如下:

各信号线解释如下:SCK (Serial Clock):时钟信号线,用于同步通讯数据。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不同,两个设备之间通讯时,通讯速率受限于 低速设备(这里产生的sck时钟频率为12.5MHz)。MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从 这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机。MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。主机从这条信号 线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机。CS (Chip Select):片选信号线,也称为 CS_N,以下用 CS_N 表示。当有多个 SPI 从 设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO 同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同使用这 3 条总线;而每个从设备都有独立的这一 条 CS_N 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。

I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 CS_N 信号线来寻址,当主机要选择从设备时,把该从设备 的 CS_N 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中 的从设备进行 SPI 通讯。所以 SPI 通讯以 CS_N 线置低电平为开始信号,以 CS_N 线被拉 高作为结束信号。

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 时钟的奇数边沿,本模 式中,偶数边沿为下降沿。

全擦除(BE)指令写入前必须先对 Flash 芯片写入写使能 (WREN)指令,使芯片处于写使能锁存(WEL)状态。此状态下写入全擦除指令才会被 Flash 芯片响应,否则,全擦除指令无效。写使能(Write Enable)指令,简称 WREN,操作指令为 8’b0000_0110(06h)。全擦除(Bulk Erase)操作,简称 BE,操作指令为 8’b1100_0111(C7h),全擦除指令是将 Flash 芯片中的所有存储单元设 置为全 1。在指令写入前后,需要满足一定的建立与保持时间,具体如下:

主要代码如下:


module flash_ctrl
(
	input 	wire	sys_clk	,	//50MHz
	input 	wire	sys_rst_n,
	input	wire	key	,		//按键输入
	
	output	reg		cs_n,		//片选信号
	output  reg     sck ,		//串行时钟输出
	output  reg     mosi    	//主输出从输入数据
);

reg key_flag;

//计数器--cnt_1    产生4分频时钟
reg [1:0]  cnt_1          ;  //计数器位宽
wire	   add_cnt_1      ;  //计数器开始条件
wire	   end_cnt_1      ;  //计数器结束条件
parameter  MAX_1 = 'd4    ;  //计数的最大值      
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		cnt_1 <= 0;
	end
	else if(add_cnt_1)begin
		if(end_cnt_1)
			cnt_1 <= 0;
		else
			cnt_1 <= cnt_1 + 1;
	end
	else
		cnt_1 <= cnt_1;
end
assign add_cnt_1 = 1;
assign end_cnt_1 = (add_cnt_1 && (cnt_1==MAX_1-1));

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		key_flag <= 0;
	else if(key)
		key_flag <= 1;
	else
		key_flag <= key_flag;
end

//计数器--cnt_2    计数640ns
reg [5:0]  cnt_2          ;  //计数器位宽
wire	   add_cnt_2      ;  //计数器开始条件
wire	   end_cnt_2      ;  //计数器结束条件
parameter  MAX_2 = 'd32   ;  //计数的最大值

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		cnt_2 <= 0;
	end
	else if(add_cnt_2)begin
		if(end_cnt_2)
			cnt_2 <= 0;
		else
			cnt_2 <= cnt_2 + 1;
	end
	else
		cnt_2 <= cnt_2;
end
assign add_cnt_2 = key_flag;
assign end_cnt_2 = (add_cnt_2 && (cnt_2==MAX_2-1));

//计数器--cnt_3    每640ns计数一次
reg [2:0]  cnt_3          ;  //计数器位宽
wire	   add_cnt_3      ;  //计数器开始条件
wire	   end_cnt_3      ;  //计数器结束条件
parameter  MAX_3 = 'd7    ;  //计数的最大值

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		cnt_3 <= 0;
	end
	else if(add_cnt_3)begin
		if(end_cnt_3)
			cnt_3 <= 0;
		else
			cnt_3 <= cnt_3 + 1;
	end
	else
		cnt_3 <= cnt_3;
end

assign add_cnt_3 = end_cnt_2;
assign end_cnt_3 = (add_cnt_3 && (cnt_3==MAX_3-1));


//三段式状态机
parameter	IDLE	=	'd0;		//空闲状态
parameter	WR_EN	=	'd1;		//写使能状态
parameter	DELAY	=	'd2;		//延迟状态
parameter	BE		=	'd3;		//擦除状态

reg [2:0]	STATE_NOW;				//现态
reg	[2:0]	STATE_NEXT;				//次态

//(1)状态切换
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		STATE_NOW  <= 'd0;
		//STATE_NEXT <= 'd0;
	end
	else
		STATE_NOW <= STATE_NEXT;
end
//(2)状态转移
always@(*)begin 
	case(STATE_NOW)
		'd0:		
			begin
				if(key)					//按键有效
					STATE_NEXT<='d1;	//进入写使能态
				else
					STATE_NEXT<=STATE_NOW;
			end
		'd1:
			begin 
				if(cnt_3==3)			//计数2*640ns
					STATE_NEXT<='d2;	//进入延迟状态
				else
					STATE_NEXT<=STATE_NOW;
			end
		'd2:
			begin 
				if(cnt_3==5)			//计数2*640ns
					STATE_NEXT<='d3;	//进入擦除状态
				else
					STATE_NEXT<=STATE_NOW;
			end
		'd3:
			begin
				if(cnt_3==0)			//计数2*640ns
					STATE_NEXT<='d0;	//进入空闲状态
				else
					STATE_NEXT<=STATE_NOW;
			end
		default:
					STATE_NEXT<='d0;	//回到空闲态
	endcase
end
//(3)输出控制
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		//cs_n <= 'd1;					//空闲时片选信号拉高
		sck	 <= 'd0;					//空闲时时钟输出低电平
		//mosi <= 'd0;
	end
	else 
		case(STATE_NOW)
			'd0://if(end_cnt_1)			//四分频时钟输入一次
				begin
					//cs_n <= 'd1;					//空闲时片选信号拉高
					sck	 <= 'd0;					//空闲时时钟输出低电平
					//mosi <= 'd0; 
				end
			'd1:if(cnt_3=='d1)
				begin 
					//cs_n <= 'd0;					
					if(cnt_1=='d0||cnt_1=='d2)
						sck	 <= ~sck;					//产生4分频时钟
				
					/* if(cnt_2<'d3)
						mosi <= 'd0;  			//输入擦除指令 8'b0000_0110
					else if(cnt_2<'d7)
						mosi <= 'd0;
					else if(cnt_2<'d11)
						mosi <= 'd0;
					else if(cnt_2<'d15)
						mosi <= 'd0;
					else if(cnt_2<'d19)
						mosi <= 'd0;
					else if(cnt_2<'d23)
						mosi <= 'd1;
					else if(cnt_2<'d27)
						mosi <= 'd1;
					else if(cnt_2<'d31)
						mosi <= 'd0;
					else 
						mosi <= 'd0; */
				end
			'd2://if(cnt_3==1)
				begin
					sck	 <= 'd0;					//空闲时时钟输出低电平
					//mosi <= 'd0; 
				end
			'd3:if(cnt_3==5||cnt_3==6)
				begin
					if(cnt_3==5&&(cnt_1==0||cnt_1==2))
						sck	 <= ~sck;					//产生4分频时钟
					else if(cnt_3==6&&(cnt_1==0||cnt_1==2))	
						sck=0;
					/* if(cnt_2<'d3)
						mosi <= 'd1;  		//输入写指令 8'b1100_0111
					else if(cnt_2<'d7)
						mosi <= 'd1;
					else if(cnt_2<'d11)
						mosi <= 'd0;
					else if(cnt_2<'d15)
						mosi <= 'd0;
					else if(cnt_2<'d19)
						mosi <= 'd0;
					else if(cnt_2<'d23)
						mosi <= 'd1;
					else if(cnt_2<'d27)
						mosi <= 'd1;
					else if(cnt_2<'d31)
						mosi <= 'd1;
					else 
						mosi <= 'd0; */
				end
		endcase
end
//mosi数据输出
always@(*)begin
	if(!sys_rst_n)begin
		mosi <= 'd0;
	end
	else 
		case(STATE_NOW)
			'd0://if(end_cnt_1)			//四分频时钟输入一次
				begin
					mosi <= 'd0; 
				end
			'd1:if(cnt_3=='d1)
				begin 
					if(cnt_2<'d3)
						mosi <= 'd0;  			//输入擦除指令 8'b0000_0110
					else if(cnt_2<'d7)
						mosi <= 'd0;
					else if(cnt_2<'d11)
						mosi <= 'd0;
					else if(cnt_2<'d15)
						mosi <= 'd0;
					else if(cnt_2<'d19)
						mosi <= 'd0;
					else if(cnt_2<'d23)
						mosi <= 'd1;
					else if(cnt_2<'d27)
						mosi <= 'd1;
					else if(cnt_2<'d31)
						mosi <= 'd0;
					else 
						mosi <= 'd0; 
				end
			'd2://if(cnt_3==1)
				begin
					//sck	 <= 'd0;					//空闲时时钟输出低电平
					mosi <= 'd0; 
				end
			'd3:if(cnt_3==5||cnt_3==6)
				begin
				if(cnt_3==5)
				begin	
					if(cnt_2<'d3)
						mosi <= 'd1;  		//输入写指令 8'b1100_0111
					else if(cnt_2<'d9)
						mosi <= 'd1;
					else if(cnt_2<'d11)
						mosi <= 'd0;
					else if(cnt_2<'d15)
						mosi <= 'd0;
					else if(cnt_2<'d21)
						mosi <= 'd0;
					else if(cnt_2<'d23)
						mosi <= 'd1;
					else if(cnt_2<'d32)
						mosi <= 'd1;
					else 
						mosi <= 'd0; 
				end
				
				else if(cnt_3==6)
					begin
					if(cnt_2>'d0)
						mosi <= 'd0; 
					else 
						mosi <= mosi;
					end
				end
		endcase
end
//片选信号输出
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		cs_n <= 'd1;					//空闲时片选信号拉高
	else if(key)
		cs_n <= 'd0;
	else if(cnt_3==3)
		cs_n <= 'd1;
	else if(cnt_3==4)
		cs_n <= 'd0;
	else if(cnt_3==0&&STATE_NOW=='d0)
		cs_n <= 'd1;
end


endmodule

仿真代码如下:


`timescale 1ns/1ns
module flash_ctrl_tb();

reg  sys_clk	;	//50MHz
reg  sys_rst_n;
reg  key;

wire cs_n;			//片选信号
wire sck ;			//串行时钟输出
wire mosi ;		  	//主输出从输入数据

initial begin
	sys_clk = 0;
	sys_rst_n = 0;
	key = 0;
	
	#34;
	sys_rst_n = 1;
	#20;
	key = 1;
	#20;
	key = 0;
	//#278;
	//key = 1;
end
always #10 sys_clk = ~sys_clk;

spi_flash flash_ctrl_inst
(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.key(key),
	
	.cs_n(cs_n),
	.sck(sck),
	.mosi(mosi)
);

endmodule

按键消抖:

module key_fliter//按键消抖
(
	input wire sys_clk,
	input wire sys_rst_n,
	input wire key,
	
	output reg key_flag
);
//计数器--cnt_1
reg key_1;
reg [31:0] cnt_1          ;  //计数器位宽
wire	   add_cnt_1      ;  //计数器开始条件
wire	   end_cnt_1      ;  //计数器结束条件
parameter  MAX = 'd100_0000    ;  //计数的最大值

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)begin
		cnt_1 <= 0;
	end
	else if(add_cnt_1)begin
		if(end_cnt_1)
			cnt_1 <= 0;
		else
			cnt_1 <= cnt_1 + 1;
	end
	else
		cnt_1 <= cnt_1;
end

assign add_cnt_1 = key_1;
assign end_cnt_1 = (add_cnt_1 && (cnt_1==MAX-1));

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		key_1 <= 0;
	else if(key==0)
		key_1 <= 1;
end

always@(posedge sys_clk or negedge sys_rst_n)begin
	if(!sys_rst_n)
		key_flag <= 0;
	else if(key_1==1 && key==0 && end_cnt_1)
		key_flag <= 1;
	else 
		key_flag <= 0;
end

endmodule

顶层模块:


module spi_flash
(
	input wire sys_clk,
	input wire sys_rst_n,
	input wire key,
	
	output wire cs_n,
	output wire sck,
	output wire mosi
);

key_fliter key_fliter_inst
(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.key(key),
	
	.key_flag(key_flag)
);

flash_ctrl	flash_ctrl_inst
(
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.key(key_flag),
	
	.cs_n(cs_n),
	.sck(sck),
	.mosi(mosi)
);

endmodule

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值