FPGA学习记录——使用verilog完成对IIC总线的编写,采用三段式状态机

module i2c_me(
	clk,
	rst_n,
	
	Cmd,
	Go,
	Rx_DATA,
	Tx_DATA,
	Trans_Done,
	ack_o,
	i2c_sclk,
	i2c_sdat
);
	input clk;
	input rst_n;
	
	input [5:0]Cmd;
	input Go;
	output reg[7:0]Rx_DATA;
	input [7:0]Tx_DATA;
	output reg Trans_Done;
	output reg ack_o;
	output reg i2c_sclk;
	inout i2c_sdat;


//系统时钟采用50MHz
parameter SYS_CLOCK = 50_000_000;

parameter SCL_CLOCK = 400_000;

localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK/4 - 1;
reg [19:0]div_cnt;
reg en_div_cnt;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		div_cnt <= 20'd0;
	else if(en_div_cnt)begin
		if(div_cnt < SCL_CNT_M)
			div_cnt <= div_cnt + 1'b1;
		else
			div_cnt <= 0;
	end
	else
		div_cnt <= 0;
wire sclk_plus = div_cnt == SCL_CNT_M;
reg  sclk_plus_state ;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		sclk_plus_state <= 1'b0;
	else if(sclk_plus)	
			sclk_plus_state <= 1'b1;
		else 
		sclk_plus_state <= 1'b0;


localparam
		IDLE      = 8'b00000001,   
		GEN_STA   = 8'b00000010,   
		WR_DATA   = 8'b00000100,   
		RD_DATA   = 8'b00001000,   
		CHECK_ACK = 8'b00010000,   
		GEN_ACK   = 8'b00100000,   
		GEN_STO   = 8'b01000000;   
reg [7:0]c_state,n_state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
	c_state <= IDLE;
else 
	c_state <= n_state;	
end





localparam 
		WR   = 6'b000001,   
		STA  = 6'b000010,   
		RD   = 6'b000100,   
		STO  = 6'b001000,   
		ACK  = 6'b010000,   
		NACK = 6'b100000;   

reg [4:0]cnt;
always@(*)
	begin
		case(c_state)
					IDLE     : 
					begin 
							if(Go)begin
								if(Cmd & STA)
									n_state <= GEN_STA;
								else if(Cmd & WR)
									n_state <= WR_DATA;
								else if(Cmd & RD)
									n_state <= RD_DATA;
							end
							else 
								n_state <= c_state;
					end
					
					GEN_STA  :     
					begin 
						if(cnt==5'd3&&sclk_plus)begin
							if(Cmd & WR)
								n_state <= WR_DATA;
							else if(Cmd & RD)
								n_state <= RD_DATA;
							end
						else
							n_state <= c_state;
					end
					
				
					WR_DATA  :     
					begin 
							if(cnt==5'd31&&sclk_plus)
								n_state <= CHECK_ACK;
							else
								n_state <= c_state;
					end
					
					RD_DATA  :     
					begin 
							if(cnt==5'd31&&sclk_plus)
								n_state <= GEN_ACK ;
							else
								n_state <= c_state;
					end
					
					CHECK_ACK:     
					begin 
						if(cnt==5'd3&&sclk_plus)begin
							if(Cmd & STO)
								n_state <= GEN_STO;
							else 
								n_state <= IDLE;
							end
						else
							n_state <= c_state;
					end
					
					GEN_ACK  :    
					begin
						if(cnt==5'd3&&sclk_plus)begin
							if(Cmd & STO)
								n_state <= GEN_STO;
							else 
								n_state <= IDLE;
						end
							else
								n_state <= c_state;
							
					end
					
					GEN_STO  :     
					begin   
							if(cnt==5'd3&&sclk_plus)
								n_state <= IDLE;
							else
								n_state <= c_state;
					end
				
					default:
							n_state <= IDLE;
		endcase
	end




always@(posedge clk or negedge rst_n)
	if(!rst_n)
	cnt <= 0;
	else if(sclk_plus == 1)begin
	if(cnt==3&&c_state==GEN_STA)
	cnt <=0 ;
	else if(cnt==31&&c_state==WR_DATA)
		cnt <=0 ;
	else if(cnt==31&&c_state==RD_DATA)
		cnt <=0 ;
	else if(cnt==3&&c_state==CHECK_ACK)
		cnt <=0 ;
	else if(cnt==3&&c_state==GEN_ACK )
	cnt <=0 ;	 
	else if(cnt==3&&c_state==GEN_STO )
	cnt <=0 ;
	else
	cnt <= cnt +1'b1;
	end

 always@(posedge clk or negedge rst_n)
 if(!rst_n)
 en_div_cnt <= 1'b0;
else if((c_state == GEN_STO) && (cnt >3'd3))
 en_div_cnt <= 1'b0;
 else if(Go == 1'b1)
  en_div_cnt  <= 1'b1;
  else
  en_div_cnt  <=en_div_cnt;

	reg i2c_sdat_o;
	reg i2c_sdat_oe;
	assign i2c_sdat = !i2c_sdat_o && i2c_sdat_oe ? 1'b0:1'bz;
	

	
always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		Rx_DATA         <= 0   ;
		i2c_sdat_oe     <= 1'd0;
		en_div_cnt      <= 1'b0;
		i2c_sdat_o      <= 1'd1;
		Trans_Done      <= 1'b0;
		ack_o           <= 0   ;

	end
    else begin
		case(c_state)
			IDLE      :   
			begin
			Trans_Done <= 1'b0;i2c_sdat_oe<= 1'd1;
//				if(Go)begin
//					en_div_cnt <= 1'b1;
//					Done_IDLE <= 1'b1;
//					end
//				else begin
//				en_div_cnt <= 1'b0;
//					Done_IDLE <= 1'b0;
//				end
			/* 	case(cnt)				
					3:begin i2c_sclk <= 0;Trans_Done <= 1'b1;end
					default:begin Trans_Done <= 1'b0;i2c_sdat_oe <= 1'd1;end
				endcase */
			end
			GEN_STA   :   
			begin 
				if(sclk_plus)begin
								case(cnt)
									0:begin i2c_sdat_o <= 1; i2c_sdat_oe <= 1'd1;end
									1:begin i2c_sclk<= 1;end
									2:begin i2c_sdat_o <= 0; i2c_sclk <= 1;end
									3:begin i2c_sclk <= 0;end
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
							
							
							end
			end
			WR_DATA   :   
			begin 
				if(sclk_plus)begin
				
								case(cnt)
									0,4,8,12,16,20,24,28:begin i2c_sdat_o <= Tx_DATA[7-cnt[4:2]]; i2c_sdat_oe <= 1'd1;end	//set data;
									1,5,9,13,17,21,25,29:begin i2c_sclk <= 1;end	//sclk posedge
									2,6,10,14,18,22,26,30:begin i2c_sclk <= 1;end	//sclk keep high
									3,7,11,15,19,23,27,31:begin i2c_sclk <= 0;end	//sclk negedge
									
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
								
							end
			end
			RD_DATA   :   
			begin
				if(sclk_plus)begin
								
								case(cnt)
									0,4,8,12,16,20,24,28:begin i2c_sdat_oe <= 1'd0; i2c_sclk <= 0;end	//set data;
									1,5,9,13,17,21,25,29:begin i2c_sclk <= 1;end	//sclk posedge
									2,6,10,14,18,22,26,30:begin i2c_sclk <= 1; Rx_DATA <= {Rx_DATA[6:0],i2c_sdat};end	//sclk keep high
									3,7,11,15,19,23,27:begin i2c_sclk <= 0;end	//sclk negedge	
									31:begin i2c_sclk <= 0;end 									
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
							end
			end
			CHECK_ACK :   
			begin
				if(sclk_plus)begin
								
								case(cnt)
									0:begin i2c_sdat_oe <= 1'd0; i2c_sclk <= 0;end
									1:begin i2c_sclk <= 1;end
									2:begin ack_o <= i2c_sdat; i2c_sclk <= 1;end
									3:begin i2c_sclk <= 0;
									if(Cmd & STO)
												Trans_Done <= 1'b0;
											else
												Trans_Done <= 1'b1;
												end
						
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
								end
								
							
			end
			GEN_ACK   :   
			begin
				if(sclk_plus)begin
								
								case(cnt)
									0:begin 
											i2c_sdat_oe <= 1'd1;
											i2c_sclk <= 0;
											if(Cmd & ACK)
												i2c_sdat_o <= 1'b0;
											else if(Cmd & NACK)
												i2c_sdat_o <= 1'b1;
										end
									1:begin i2c_sclk <= 1;end
									2:begin i2c_sclk <= 1;end
									3:begin i2c_sclk <= 0;
										
											if(Cmd & STO)
											Trans_Done <= 1'b0;
											else 
											Trans_Done <= 1'b1;
									end
							
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
							
							end
			end
			GEN_STO   :  
			begin 
				if(sclk_plus)begin
								case(cnt)
									0:begin i2c_sdat_o <= 0; i2c_sdat_oe <= 1'd1;end
									1:begin i2c_sclk <= 1;end
									2:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
									3:begin i2c_sclk <= 1;Trans_Done <= 1'b1;end
			
									default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
								endcase
						
							end
			end
			
		
		endcase
	end	

endmodule

 

 

说明以后补;

出现了几个bug,读写数据使用的状态时钟使用的n_state导致写下一个状态信号会直接和状态一起跳变。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个使用Verilog语言编写FPGA三段式状态机按键控制翻转灯的代码示例: ```verilog module state_machine(input clk, input btn, output reg led); // 定义状态枚举类型 typedef enum logic [1:0] { STATE_OFF, STATE_ON, STATE_TOGGLE } state_t; // 定义状态寄存器和当前状态变量 reg [1:0] state_reg, state_next; // 定义状态转移逻辑 always @(posedge clk) begin state_reg <= state_next; case (state_reg) STATE_OFF: if (btn) state_next = STATE_ON; STATE_ON: if (btn) state_next = STATE_TOGGLE; else state_next = STATE_OFF; STATE_TOGGLE: if (btn) state_next = STATE_OFF; else state_next = STATE_ON; default: state_next = STATE_OFF; endcase end // 定义输出逻辑 always @(state_reg) begin case (state_reg) STATE_OFF: led = 1'b0; STATE_ON: led = 1'b1; STATE_TOGGLE: led = !led; default: led = 1'b0; endcase end endmodule ``` 在这个状态机中,我们使用了一个时钟信号clk和一个按键信号btn作为输入,以及一个LED灯信号led作为输出。状态机包括三个状态:熄灭状态、点亮状态和翻转状态。当按键被按下时,状态机会根据当前状态进行状态转移,从而改变LED灯的状态。 在状态转移逻辑中,我们使用了一个状态寄存器state_reg和一个当前状态变量state_next来储存状态信息。在每个时钟上升沿时,我们根据当前状态和按键信号来更新下一个状态。在输出逻辑中,我们根据当前状态来改变LED灯的状态。 最后,我们需要使用FPGA开发工具将这个Verilog代码综合、实现和下载到开发板上,以实现按键控制LED灯的翻转。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值