FPGA verilog语言逻辑实现IIC 数组数据配置

最近要应用一个AD,低功耗需求,不能上SOC,寄存器配置又臭又长,简单写一个IIC配置数组。
首先看一下协议:
在这里插入图片描述
典型IIC,两条线,一根时钟,一根数据。启动时SCL时钟滞后于SDA,结束时提前。
将程序分为两个模块:
1.驱动模块:具体接口协议实现
2.配置模块:定义写多少个寄存器,写什么数据
在驱动模块中:
这部分比较简单,只需要与上层做简单的交互,接收上层的信息,按照协议输入到设备。
端口:

	input 						clk_10MHz,
	input 	      				clk_i, 
	input						rst_n,
	
	input 						wr_rd_flag,	//0 wr -- 1 rd
	input 						start_en,
	
	input		[ 7:0] 			i2c_device_addr,
	input		[ 7:0] 			register,
	input 		[ 7:0]			data_byte,
	
	output reg  				scl,
	inout 						sda,
	output reg 					busy,
	output reg 					err
		

其他的就是做一个inout输入输出切换,按照时序撸就可以了。
实时抓取的数据写入及应答:
在这里插入图片描述
代码(只简单做了写操作,就不详细解释了,很简单:

assign sda = (cstate == start_bit || cstate == idle || cstate == i2c_over)? sda_o :( Rec_count >16'd16 || Rec_count ==16'd0 )? 1'bz : sda_o ;// || nstate != start_bit)? 1'bz : sda_o  ;
//assign sda = ( Rec_count >16'd16 || Rec_count ==16'd0 )? 1'bz : sda_o ;
assign sda_i = sda;
always @(*) begin
    case (cstate)
		idle:			nstate <=	(start_en)		? start_bit 	: idle;
		start_bit:		nstate <=	(State_turn)	? wr_dev_ctrl	: start_bit;
		wr_dev_ctrl:	nstate <=	(State_turn)	? wr_reg		: wr_dev_ctrl;
		wr_reg:			nstate <=	(State_turn)	? wr_data_byte	: wr_reg;
		wr_data_byte:	nstate <=	(State_turn)	? i2c_over		: wr_data_byte;
		i2c_over:		nstate <=	(State_turn)	? idle			: i2c_over;
		
	endcase
end
// busy
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		busy	<= 1'b1;
	end
	else begin
		case (nstate)
			idle:	begin
				busy	<= 1'b0;
			end
			wr_dev_ctrl,wr_reg,wr_data_byte,i2c_over,start_bit:begin
				busy	<= 1'b1;
			end
		endcase 
	end

end
// scl
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		scl	<= 1'b1;
	end
	else begin
		case (nstate)
			idle,i2c_over,start_bit:	begin
				scl	<= 1'b1;
			end
			wr_dev_ctrl,wr_reg,wr_data_byte:begin
				scl	<= ~scl;
			end
		endcase 
	end

end

//sda_o
always @(posedge clk_i or negedge rst_n)
begin	
	if(!rst_n) begin
		sda_o		<= 1'b1;
		dev_r 		<= 8'hff;
		reg_r 		<= 8'hff;
		data_byte_r <= 8'hff;
	end
	else begin
		case (nstate)
			idle:begin
				sda_o	<= 1'b1;
			end
			start_bit: begin
				sda_o		<= 1'b0;
				dev_r 		<= {i2c_device_addr[7:1] , wr_rd_flag};
				reg_r 		<= register;
				data_byte_r <= data_byte;	
			end
			i2c_over:	begin				
				sda_o		<= 1'b0;
				dev_r 		<= dev_r;
				reg_r 		<= reg_r;
				data_byte_r <= data_byte_r;
			end
			wr_dev_ctrl:begin
				reg_r 		<= reg_r;
				data_byte_r <= data_byte_r;	
				if(Rec_count >=16'd16)begin
					sda_o	<= 1'b1;
				end				
				else begin
					sda_o	<= dev_r[7];
					if(!scl)begin
						dev_r <= {dev_r[6:0],dev_r[7]};
					end
				end
			end
			wr_reg:begin
				dev_r 		<= dev_r;
				data_byte_r <= data_byte_r;	
				if(Rec_count >=16'd16)begin
					sda_o	<= 1'b1;
				end				
				else begin			
					sda_o	<= reg_r[7];
					if(!scl)begin
						reg_r <= {reg_r[6:0],reg_r[7]};
					end
				end
			end
			wr_data_byte:begin
				dev_r <= dev_r;
				reg_r <= reg_r;
				if(Rec_count >=16'd16)begin
					sda_o	<= 1'b1;
				end				
				else begin
					sda_o	<= data_byte_r[7];
					if(!scl)begin
						data_byte_r <= {data_byte_r[6:0],data_byte_r[7]};
					end	
				end
			end			
		endcase 
	end
end
//count
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		Rec_count	<= 16'd0;
		State_turn  <=  1'b0;
	end
	else begin
		case (nstate)
			idle:begin
				Rec_count	<= 16'd0;
				State_turn 	<=  1'b0;
			end
			start_bit,i2c_over:begin
				if (Rec_count == 16'd2 - 1'b1) begin
					Rec_count  	<= 16'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end				
			end
			wr_dev_ctrl:begin
				if (Rec_count == 16'd18 - 1'b1) begin
					Rec_count  	<= 16'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end				
			end
			wr_reg:begin
				if (Rec_count == 16'd18 - 1'b1) begin
					Rec_count  	<= 16'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end				
			end
			wr_data_byte:begin
				if (Rec_count == 16'd18 - 1'b1) begin
					Rec_count  	<= 16'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end				
			end	
		endcase
	end
end
//err
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		err	<= 1'b0;
	end
	else begin
		case (nstate)
			idle,i2c_over,start_bit:	begin
				err	<= err;
			end
			wr_dev_ctrl,wr_reg,wr_data_byte:begin
				if(Rec_count >=16'd15) begin
					if(sda_i) 	err	<= 1'b0;
					else 		err	<= 1'b1;
				end
				else begin
					err	<= err;
				end
			end
		endcase 
	end

end

always @(posedge clk_i or negedge rst_n) begin
    if(!rst_n)
        cstate <= idle;
    else begin
        cstate <= nstate;
    end
end

在配置模块中:
共需要写入3种数据,设备地址,寄存器地址,寄存器数据。
因为需要做数组数据输入,因设备地址在对单个设备配置时是固定的,则可以定义为常量REG类型,当挂载多个设备时只需更新这个变量。
其他数据定义为数组。
具体代码如下:
I2C_DEVICE_ADDR:定义设备地址
IIC_REG_COUNT :定义需要写入数据个数,对应IIC_REG_DATA 数组
IIC_REG_DATA :奇数字节为寄存器地址,偶数为寄存器数据
更换设备或更改寄存器时只需要改上面几个

module iic_reg_init#(
	parameter					IIC_REG_COUNT = 16'd9,
	parameter					I2C_DEVICE_ADDR = 8'b1001_1110,
	parameter					I2C_length = IIC_REG_COUNT*16 - 4'd1
	//parameter						;
		
)(
	input 						clk_10MHz,
	input 	      				clk_i, 
	input						rst_n,
	
	output reg 					wr_rd_flag,	//0 wr -- 1 rd
	output reg 					start_en,
	
	output   	[ 7:0] 			i2c_device_addr,
	output reg	[ 7:0] 			register,
	output reg  [ 7:0]			data_byte,
	

	input	 					busy,
	input	 					err,
	input 						tri_en
		
    );
									//register // data_byte
reg [I2C_length :0]	IIC_REG_DATA = { 	8'h00, 	8'h00,                
										//8'h01,	8'h01,
										8'h07,	8'h30,
										8'h08,	8'h02,
										8'h0B,	8'h00,
										8'h0C,	8'h01,
										8'h0D,	8'h02,
										8'h0E,	8'h03,												
										8'h13,	8'h06,	
										8'h1F,	8'h43								
						};

写入流程要和驱动进行交互,代码如下:

ssign i2c_device_addr = I2C_DEVICE_ADDR;					
always @(*) begin
    case (cstate)
		ele_delay:		nstate <=	(State_turn)				? idle 			: ele_delay;
		idle:			nstate <=	(!busy && iic_count != 0)	? reg_sent		: idle;
		reg_sent:		nstate <=	delay;
		delay:			nstate <=	delay_cnt;
		delay_cnt:		nstate <=	(State_turn)				? idle			: delay_cnt;	
	endcase
end	
//	iic_count
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		iic_count <= IIC_REG_COUNT - 1'b1;
		start_en		<= 1'b0;
		wr_rd_flag		<= 1'b0;
	end
	else begin
		if(tri_en) begin
			iic_count <= IIC_REG_COUNT - 1'b1;
			start_en		<= 1'b0;
			wr_rd_flag		<= 1'b0;
		end
		else begin
			case (nstate)
				reg_sent:	begin
					iic_count <= iic_count - 1'b1;
					start_en		<= 1'b1;
					wr_rd_flag		<= 1'b0;
				end
				delay: begin
					iic_count <= iic_count;
					start_en		<= 1'b1;
					wr_rd_flag		<= 1'b0;
				end
				ele_delay,idle,delay,delay_cnt:begin
					iic_count <= iic_count;
					start_en		<= 1'b0;
					wr_rd_flag		<= 1'b0;
				end
			endcase 
		end
	end

end
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		IIC_REG_DATA_r <= IIC_REG_DATA;
	end
	else begin
		case (nstate)
			reg_sent:	begin
				register 	<= IIC_REG_DATA_r[I2C_length 		:I2C_length - 4'd7];
				data_byte 	<= IIC_REG_DATA_r[I2C_length - 4'd8 :I2C_length - 4'd15];
				IIC_REG_DATA_r<= { IIC_REG_DATA_r[I2C_length - 8'd16:4'd15],IIC_REG_DATA_r[I2C_length - 4'd15:0]};
			end
			ele_delay,idle,delay,delay_cnt:begin
				IIC_REG_DATA_r <= IIC_REG_DATA_r;
			end
		endcase 
	end
end
always @(posedge clk_i or negedge rst_n)
begin
	if(!rst_n) begin
		Rec_count	<= 32'd1000;
		State_turn  <=  1'b0;
	end
	else begin
		case (nstate)
			ele_delay:begin
				if (Rec_count == 32'd1000 - 1'b1) begin
					Rec_count  	<= 32'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end	
			end
			delay_cnt:begin
				if (Rec_count == 32'd1000 - 1'b1) begin
					Rec_count  	<= 32'd0;
					State_turn  <=  1'b1;
				end
				else 							begin
					Rec_count  	<= Rec_count + 1'b1;
					State_turn  <= 1'b0;
				end	
			end
			idle,delay,reg_sent:begin
				Rec_count	<= 16'd0;
				State_turn 	<=  1'b0;
			end
		endcase
	end
end
always @(posedge clk_i or negedge rst_n) begin
    if(!rst_n)
        cstate <= idle;
    else begin
        cstate <= nstate;
    end
end		

busy信号和err信号都为测试信号,按需求应用即可。
clk_i时钟为iic scl时钟×2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值