weiqi7777

串口发送数据到IIC,LCD从IIC读取数据显示

0
阅读(6835)

结合上次的串口和LCD,这次在中间加了一个IIC。

流程图,如下:

clip_image002

看着好像挺复杂的样子。其实,在上次,已经实现了右下角的部分和串口的部分。只需要实现IIC部分,在和已经实现好的模块连接就行了。

首先说下功能:

串口将接收到是数据发送到IIC_FIFO中,然后IIC_FIFO控制器控制IIC控制器,将IIC_FIFO中暂存的数据给写到EEPROM中。当外部有按键按下时,按键检测模块会检测到这个输入,然后控制IIC控制器进行EEPROM的数据读取,读取到的数据发送给FIFO中。后面就和上次的一样,LCD从FIFO中取出数据,然后进行显示。

IIC控制器的程序,之前写过博客介绍过。

这里就列出顶层信号


module AT24C02_module(
			input					clk,
			input					rst_n,
			
			input					start,              
			input		[2:0]			device_address,    //the device AT24C02 address
			input		[7:0]			rom_address,       //the rom address
			input		[7:0] 			rom_write_data,    //write 8-bits data to the AT24C02
			input					write_or_read,		 //write mode or read mode   0 write  1 read
			
			input    [7:0]				write_data_number,       // write data number
			input    [7:0]                          read_data_number,  //read  data number
			
			
			output	reg[7:0]                        row_read_data,     //read 8-bits data from AT24C02 in rom_address
			
			output	reg				read_finish,       //read data finish,mean external can read data
			output   reg				write_finish,     //write one byte data finsih.
			output   reg				write_idle,       //write data state, 1 mean external can write new data
			
			inout							i2c_sda,            //I2C    data   , bidirectional port
			output	reg				i2c_scl,             //I2C    clk   250K
			
			output   reg				operate_busy,     // IIC is or bot busy ,1 mean busy ,0 mean not busy
			
			output					enable_read			// now state is read or write.  1 mean read, 0 mean write
    );	


可以实现IIC的多数据或单个数据的读写。

下面就是IIC_FIFO控制器。

采用状态机设计。

总共有三个状态。第一个状态是空闲状态,第二个状态是等待fifo接收串口数据状态,第三个状态就是控制写IIC状态。

所以,代码也是比较简单的。


module FIFO_IIC_control(
		input			clk,
		input			rst_n,
	

		input			rxd_finish,         // uart receive finish signal . 1 mean read a uart data
		
		input			iic_write_finish,		//IIC write 1 byte data signal. 1 mean write finish
		
		
		input	[5:0]           iFifo_count,			//IIC_FIFO save data number
		
		output	reg	        fifo_rd_en,		//fifo read enable, 1 mean read data from fifo
		output	reg             iic_start,		//IIC statt signal, 1 mean start
			
		output	reg  [5:0]      oFifo_count  // output to IIC  show how many data will be write
    );
	 
	 
	 localparam  idle_state			   	=  'd0;
	 localparam  fifo_rxd_state			=	'd1;
	 localparam  iic_write_state			=  'd2; 
	 
	 
	 localparam  time_value = 27'b100_0000_0000_0000_0000_0000_0000;
	 //localparam  time_value = 27'd2000;
	 reg [26:0]   time_counter;
	 
	 
	 reg [1:0] state;
	 always@(posedge clk or negedge rst_n) begin
		if(!rst_n)	
			begin
				oFifo_count <= 'd16;
				fifo_rd_en  <= 'd0;
				iic_start <='d0;
				time_counter <= 'd0;
				state <= idle_state;
			end
		else
			case(state)
			//idle state   nothing to do 
			//detect uart reveice siganl ,state go to fifo_rxd_state
			idle_state:	begin
			   time_counter <= 'd0;
				if(rxd_finish == 1)
					state <= fifo_rxd_state;
			end
			//fifo_rxd_state   wait uart receive data finsih
			//if exceed time_value ,mean uart receive data is finish, so state go to iic_write_state
			fifo_rxd_state: begin
				if( time_counter >= time_value )
					begin
						state <= iic_write_state;
						oFifo_count <= iFifo_count;
						iic_start <= 1'b1;
						fifo_rd_en <= 1'b1;
					end
				else if(rxd_finish == 1)
					time_counter <= 'd0;
				else
					time_counter <= time_counter + 1'b1;
			end
			//iic_write_state   , control IIC write data
			iic_write_state: begin
				iic_start <= 1'b0;
				fifo_rd_en <= iic_write_finish;
				if(iFifo_count == 0)
					state <= idle_state;
			end
			endcase
	 end
endmodule


这里说明一下oFifo_count <= 'd16; 这条语句。

oFifo_count这个是输出给IIC控制器的,指示这次操作读或写是要操作多少个数据。这里复位值是16.因为我们知道EEPROM器件数据是掉电不丢失的。所以上次写的数据在掉电后是不丢失的。所以在给开发板重新上电后,我们可以直接读取数据,而不用串口重新写数据之后才能读。所以这里将这个数据个数给复位为16.意思就在上电复位后,当按下按键后,就读取IIC的16个数据进行显示。

然后将IIC部分的代码给封装成一个模块


module fifo_iic(
		input 			clk,
		input			rst_n,
		
		input			iuart_send_flag,  //uart receive finish signal
		input	 [7:0]	        iuart_data,			//uart receive 8-bits data
		
		input			key,					//external buttom
		
		
		output			iic_read_finish,	//IIC read 1-byte data finish
		output [7:0]	        iic_read_data,		//IIC read 1-byte data
		
		inout			i2c_sda,            //I2C    data   , bidirectional port
		output			i2c_scl ,           //I2C    clk   250K
		
		
		output [7:0]            fifo_data_o,			
		output	                fifo_rd_en
		
    );
	 
	 
	 wire iic_write_idle;
	 wire iic_write_finish;
	 wire [5:0]	data_count;
	 wire [5:0] oFifo_count;
	 reg write_or_read;
	 wire key_down;
	 
	 
AT24C02_module u1 (
    .clk(clk), 
    .rst_n(rst_n), 
    .start(iic_start | key_down), 
    .device_address(3'b000), 
    .rom_address('d0), 
    .rom_write_data(fifo_data_o), 
    .write_or_read(write_or_read), 
    .write_data_number({2'b0,oFifo_count}), 
    .read_data_number({2'b0,oFifo_count}), 
    .row_read_data(iic_read_data), 
    .read_finish(iic_read_finish), 
    .write_finish(iic_write_finish), 
    .write_idle(iic_write_idle), 
    .i2c_sda(i2c_sda), 
    .i2c_scl(i2c_scl), 
	 .operate_busy(),
    .enable_read()
    );
	 
 FIFO_IIC_control u2 (
	 .clk(clk),
	 .rst_n(rst_n),
    .rxd_finish(iuart_send_flag), 
    .iic_write_finish(iic_write_finish), 
    .iFifo_count(data_count), 
    .fifo_rd_en(fifo_rd_en), 
    .iic_start(iic_start), 
    .oFifo_count(oFifo_count)
    );
	 
	 

fifo_module u3 (
    .clk(clk), 
    .rst_n(rst_n), 
    .idata(iuart_data), 
    .ird_en(fifo_rd_en), 
    .iwr_en(iuart_send_flag), 
    .odata(fifo_data_o), 
    .empty(),
	 .data_count(data_count) // output [5 : 0] data_count
    );
	 
	 key_button u4 (
    .clk(clk), 
    .rst_n(rst_n), 
    .key(~key), 
    .key_down(key_down)
    );
	 
	 always@(posedge clk or negedge rst_n) begin
			if(!rst_n)
				write_or_read <= 1'b0;  //default  IIC is write operate
			else
				if(key_down == 1)
					write_or_read <= 1'b1;
				else if(iic_write_idle == 1 && write_or_read == 1'b1)
					write_or_read <= 1'b0;
	 end
endmodule


在然后,将串口模块,IIC模块,LCD模块给连接起来。也就是进行封装。


module uart_iic_lcd(
	input				clk,
	input				rst_n,
	
	//uart interface
	input				uart_rxd,     //input  serial rxd data
	output			        uart_txd ,    //output   serial txd data
	  
	input		                key,
	
	inout							i2c_sda,            //I2C    data   , bidirectional port
	output					i2c_scl,             //I2C    clk   250K
	
	 //    LCD Interface
        output [3:0]                    LCD_DATA,    //
        output                          LCD_RW,		//
        output                          LCD_EN,		//
        output                          LCD_RS ,     //	
	 
	output       [7:0]              oled 	 
    );
	 
	 wire [7:0] iic_read_data;
	 wire iic_read_finish;
	 
	 wire iuart_send_flag;
	 wire [7:0] iuart_data;
	 
	 wire fifo_rd_en;
	 wire [7:0] fifo_data_o;
	 
	 wire clk_50M;


	 	 
    uart_top u0 (
    .clk(clk_50M), 
    .rst_n(rst_n), 
    .uart_rxd(uart_rxd), 
    .tx_start(fifo_rd_en ), 
    .tx_data(fifo_data_o[7:0]), 
    .tx_finish(), 
    .rx_finish(iuart_send_flag), 
    .uart_txd(uart_txd), 
    .receive_data(iuart_data[7:0])
    );
	 
	 assign oled = iuart_data[7:0];
	 
	 
	 fifo_iic u1 (
    .clk(clk_50M), 
    .rst_n(rst_n), 
    .iuart_send_flag(iuart_send_flag), 
    .iuart_data(iuart_data), 
    .key(key), 
    .iic_read_finish(iic_read_finish), 
    .iic_read_data(iic_read_data[7:0]), 
    .i2c_sda(i2c_sda), 
    .i2c_scl(i2c_scl),
    .fifo_data_o(),
	 .fifo_rd_en()
    );
	 
	  LCD_top u2 (
    .clk(clk_50M), 
    .rst_n(rst_n), 
    .iuart_data(iic_read_data[7:0]), 
    .iuart_send_flag(iic_read_finish), 
    .LCD_DATA(LCD_DATA[3:0]), 
    .LCD_RW(LCD_RW), 
    .LCD_EN(LCD_EN), 
    .LCD_RS(LCD_RS),
	 .fifo_data_o(fifo_data_o[7:0]),
	 .fifo_rd_en(fifo_rd_en)
    );
	 
	 dcm_100_50 u3 (
    .clk(clk), 
    .rst_n(rst_n), 
    .clk_out(clk_50M)
    );
endmodule


这里多一个dcm_100_50模块,因为virtex5的板子的晶振是100M的。而设计的代码是基于50M时钟的,所以需要一个dcm将时钟进行二分频。为什么不自己写一个二分频代码了。因为用dcm分频出来的时钟的质量比直接用代码写生成出来的时钟质量好。

下面就开始测试

首先发送数据。

clip_image006

使用逻辑分析仪抓紧的IIC写数据波形

clip_image008

最前面两个发的是器件地址和写数据的地址。后面是写的数据。

然后读取数据,

clip_image010

逻辑分析仪抓取的IIC读取波形

clip_image012

前两个是器件地址和读取数据的地址。后面一个是重新发送器件地址,但是发送的数据最后一位要为1,表示是读数据。后面就是读取的数据。

设计,还有些小bug。

EEPROM在多字节写入的时候,一次操作最多只能写16个数据,超过16个数据后,写的数据会覆盖之前写的数据,但是在读的时候,是没有限制的。所以,当串口发送的数据超过16个后,之前写入的数据会被覆盖掉,而读取的时候,是读取发送那么多数据的个数的。所以,就会读到无效数据,显示就会出现乱码。

例如,我发送18个数据,那么最后两个数据会覆盖最先写的两个数据。读的时候,会读取18个数据,这样就读到了无效的后两个数据。

这里,在程序中,就需要加入,当写入的数据超过16个的时候,进行写第二次。

我发送http://blog.chinaaet.com/weiqi7777

clip_image014

用逻辑分析仪抓波形

clip_image016

然后读取数据

clip_image018

抓取的波形

clip_image020

发送的数据,总共有34个。所以最终写入到IIC的第一个数据是第33个,也就是7.因为被后面写的数据覆盖了。所以读取到的数据的第一个就是7。至于后面读到的乱码,那是因为地址超过16了,而地址超过16的数据,我们都没有写,所以不知道这些数据是什么。就有可能是乱码了。