IIC_FPGA控制程序设计

通过上篇文章描述IIC原理可知IIC工作原理。使用FPGA控制iic总线,接口可定义如下:
IIC_FPGA控制程序设计_第1张图片
图示信号意义如下:
clk为系统时钟;
Rst_n为系统复位信号;
[5:0]Wrdata_num为写数据的字节数;
[5:0]Rddata_num为读数据的字节数;
[1:0]Wdaddr_num为器件地址字节数;
[2:0]Device_addr为器件地址;
[15:0]Word_addr为寄存器地址;
Wr为写操作使能信号;
[7:0]Wr_data为写数据;
Rd为读使能信号;
[7:0]Rd_data为读数据;
Wr_data_vaild为写数据有效信号;
Rd_data_vaild为读数据有效信号;
SCL为时钟总线信号;
SDA为数据总线信号;
Done为读写完成标志位;

设计思想:IIC控制总线只有两条输出信号线,即SCL和SDA。IIC在进行读写操作时候都是在SCL的高电平或者低电平读取数据和改变数据的。
首先解决SCL信号的产生,SCL总线采用400KHz频率,因此可以采用系统时钟计数产生SCL信号。同时设计两个标志位scl_high和scl_low,方便在SCL高低电平有效期间进行数据操作。对于scl_low和scl_high信号,只需要在计数到四分之一和四分之三的位置产生标志位即可。
IIC_FPGA控制程序设计_第2张图片
接着,按照上篇所述的读写数据时序分析,可以看出无论是读或者写均分为几个状态进行,因此可以设计一个状态机列出读/写过程中各个状态情况,可以分为一下几种状态:1、空闲状态; 2、写开始状态; 3、写控制状态; 4、写地址状态; 5、写数据状态; 6、读开始状态; 7、读控制状态; 8、读数据状态; 9、停止状态;

localparam IDLE = 9'b0_0000_0001,  //空闲状态
					WR_START = 9'b0_0000_0010,  //写开始状态
					WR_CTRL = 9'b0_0000_0100,  //写控制状态
					WR_WADDR = 9'b0_0000_1000,  //写地址状态
					WR_DATA = 9'b0_0001_0000,  //写数据状态
					RD_START = 9'b0_0010_0000,  //读开始状态
					RD_CTRL = 9'b0_0100_0000,  //读控制状态
					RD_DATA = 9'b0_1000_0000,  //读数据状态
					STOP = 9'b1_0000_0000;  //停止状态

所述的九种状态中属3、4、5、7、8状态需要发送或接受8位数据,因此可以以任务的方式展现出来,通过定义内部待接收和待发送寄存器,以及SDA输出临时寄存器,如此,在3、4、5、7、8状态即可在状态机中特定条件下赋值相应的器件地址、寄存器地址、待发送/接受的数据给内部待接收或待发送寄存器进行总线传输即可,也省去了主状态机的工作量。对于发送/接受数据均需要响应信号ack,且ack也是在SCL控制下响应,故可以对SCL高低电平进行计数,在特定计数值下进行数据操作和信号控制。

//输出串行数据任务
	reg FF; //串行数据输出、输入执行位
	reg [7:0]sda_data_out;  //待输出的串行数据
	reg sda_reg;  //sda数据输出寄存器
	reg [7:0]sda_data_in;  //待输入的串行数据
	
	task send_8bit_data;
	if(scl_high && (halfbit_cnt == 8'd16))
		FF <= 1;
	else if(halfbit_cnt < 8'd17)begin
		sda_reg <= sda_data_out[7];
		if(scl_low)
			sda_data_out <= {sda_data_out[6:0],1'b0};
		else
			sda_data_out <= sda_data_out;
	end
	else
		;
	endtask
	
	//串行数据大输入任务
	task receive_8bit_data;
	if(scl_low && (halfbit_cnt == 8'd15))
		FF = 1;
	else if(halfbit_cnt < 8'd15)begin
		if(scl_high)
			sda_data_in <= {sda_data_in[6:0],SDA};
		else begin
			sda_data_in <= sda_data_in;
		end
	end
	else
		;
	endtask

SCL信号高低电平的计数值下控制的ack信号的产生。发送/接受8位数据和一位ack信号共9bit数据,需要9个低电平改变数据,9个高电平输入/输出数据,一共需要高低电平计数值=18即可完成。

//数据接受方对发送方响应检测标志位
	reg ack;
	always@(posedge clk or negedge Rst_n)
	begin
		if(!Rst_n)
			ack <= 1'b0;
		else if((halfbit_cnt == 8'd16) && (scl_high) && (SDA == 1'b0))
			ack <= 1'b1;
		else if((halfbit_cnt == 8'd17) && (scl_low))
			ack <= 1'b0;
		else
			ack <= ack;
	end

SDA信号控制:总线采用的三态使能输出。

assign SDA = sda_en ? sda_reg : 1'bz;

简单而粗糙的理解即是:空闲状态保持三态输入;主机向从机传输数据设置SDA为输出状态,由sda_reg临时寄存器将数据给SDA进行送到总线上;从机向主机发送数据和信号时,SDA设置为三态输入,等待主机读取从机通过总线传输来的数据。

//SDA三态使能信号sda_en
	always@(*)
	begin
		case(main_state)
			IDLE:
				sda_en = 1'b0;
				
			WR_START,RD_START,STOP:
				sda_en = 1'b1;
				
			WR_CTRL,WR_WADDR,WR_DATA,RD_CTRL:
				if(halfbit_cnt < 16)
					sda_en <= 1'b1;
				else
					sda_en <= 1'b0;
					
			RD_DATA:
				if(halfbit_cnt < 16)
					sda_en = 1'b0;
				else
					sda_en = 1'b1;
					
			default:
				sda_en = 1'b0;
			
		endcase
	end

最后,可以设计读取/写入的标志信号和主机读出有效数据控制。

你可能感兴趣的:(FPGA,IIC,控制程序,FPGA)