天天看點

FPGA——驅動BH1750

GY302子產品

最近做項目用到了這個子產品,記錄一下。GY302子產品可以檢測目前的環境光強值,用到的是BH1750晶片。

FPGA——驅動BH1750

子產品一共五個引腳:Vcc、GND、SCL、SDA和ADDR。

BH1750晶片手冊

大緻浏覽下BH1750晶片手冊,可以獲得以下資訊:

1、

FPGA——驅動BH1750

晶片采用标準的IIC總線接口。

2、

FPGA——驅動BH1750

這張圖說明晶片由兩種工作模式:One Time 和 Continuous 模式,在One Time模式下讀取完一次光強值會直接自動回到Power Down狀态,也就是再想讀光強的話需要重新寫一次寄存器,而Continuous模式則可以連續讀。

3、

FPGA——驅動BH1750

下面标黃的句子告訴我們推薦使用H-Resolution Mode,在上面的表中我們可以查到這個模式對應的Opencode是0001_0000,其實就對應寫進寄存器的資料。

4、

FPGA——驅動BH1750

這裡說明了寄存器的位址是什麼,它對應于子產品ADDR引腳的電平高低,如果是>=0.7VCC的高電平,則寄存器位址為1011100,反之則為0100011。

5、

FPGA——驅動BH1750

晶片手冊中也給了我們幾個example,這個例子是ADDR低電平,Continuously H-resolution mode的,符合我們的要求,可見它的寫寄存器位址和資料都是上文提到的,是以我們可以直接以這種方式驅動BH1750去擷取環境光強。還需要注意發送完指令後需要等待180ms的時間才可以進行read result的操作,然後每隔120ms可以重新讀取一次。

IIC總線時序

1、首先是IIC總線通信的開始條件和結束條件:

FPGA——驅動BH1750

即在串行時鐘線SCL為高時拉低SDA,表明開始一次IIC通信。在串行時鐘線SCL為高時拉高SDA,表明結束一次IIC通信。

2、然後,就要注意SCL為高時,SDA資料線上的資料必須穩定。不管是誰控制SDA資料線都必須遵循這個原則。

FPGA——驅動BH1750

隻有在SCL時鐘線為低電平時,才可以改變SDA總線上的值。

3、ACK應答位信号,為低電平時有效。如果FPGA給BH1750寫資料,那麼需要校驗一個低電平的校驗位,才可以進行下一次有效的寫。同樣,如果FPGA從BH1750讀資料,在讀完一個位元組後,需要發送一個低電平的ACK應答位給BH1750。不過要注意,在最後一個位元組讀完後,FPGA需要傳回一個非有效ACK位,即拉高總線的值,表示不再接受資料。

Verilog驅動

在清楚要用IIC總線發送和接收的值以及IIC總線的時序之後,我們就可以編寫verilog驅動。代碼大緻思路就是通過幾個大的狀态機:初始化、發送指令、等待、讀取資料,進行整體狀态控制。再在發送指令和讀取資料狀态使用内部狀态機去構造IIC時序,最後将光強值打包輸出,具體代碼如下:

module	bh1750_driver
#(
		parameter				TIME_1S			=	480_000_000	,
		parameter				TIME_180MS		=	864_0000	,
		parameter				TIME_120MS		=	576_0000	,
		parameter				CLOCK_DIV		=	480			,
		parameter				SEND_FIRST_BYTE	=	8'b01000110	,
		parameter				SEND_SECOND_BYTE=	8'b00010000	,
		parameter				SEND_THIRD_BYTE	=	8'b01000111	
)
(
		input					clk								,
		input					reset							,
		output	wire			scl								,
		inout	wire			sda								,
		output	reg		[15:0]	lux_data						,
		output	wire			lux_data_vld					
);					
			
		localparam				INIT			=	5'b00001	;
        localparam				SEND_COMMAND	=	5'b00010	;
        localparam				WAIT_RESULT_1	=	5'b00100	;
        localparam				READ_RESULT		=	5'b01000	;
        localparam				WAIT_RESULT_2	=	5'b10000	;
		localparam				CLOCK_DIV_HALF	=	CLOCK_DIV/2 ;
        localparam				CLOCK_DIV_QUAT	=	CLOCK_DIV/4	;
		
		localparam				SEND_INIT		=	3'd0		;
		localparam				LOAD_SECOND_BYTE=	3'd2		;
		localparam				SEND_START		=	3'd3		;
		localparam				SEND_BYTE		=	3'd4		;
		localparam				RECIVE_ACK		=	3'd5		;
		localparam				LOAD_FIRST_BYTE	=	3'd1		;
		localparam				CHECK_ACK		=	3'd6		;
		localparam				SEND_END		=	3'd7		;
		
		localparam				READ_INIT		=	4'd0		;
		localparam				LOAD_SEND_BYTE	=	4'd1		;
		localparam				READ_START		=	4'd2		;
		localparam				READ_SEND_BYTE	=	4'd3		;
		localparam				READ_ACK		=	4'd4		;
		localparam				CHECK_READ_ACK	=	4'd5		;
		localparam				READ_HIGH_BYTE	=	4'd6		;
		localparam				SEND_FIRST_ACK	=	4'd7		;
		localparam				SEND_ACK_READ	=	4'd8		;
		localparam				READ_LOW_BYTE	=	4'd9		;
		localparam				SEND_SECOND_ACK	=	4'd10		;
		localparam				SEND_ACK_END	=	4'd11		;
        localparam				READ_END		=	4'd12		;

		reg				[ 4:0]	state_c_global					;
		reg				[ 4:0]	state_n_global					;
		wire					init_end						;
		wire					send_command_end				;
		wire					wait_result_1_end				;
		wire					read_result_end					;
		wire					wait_result_2_end				;
		reg				[25:0]	cnt								;
		
		wire					scl_en							;
		wire					scl_cnt_end						;
		wire					scl_low_middle					;
		wire					scl_high_middle					;
		reg				[ 8:0]	scl_cnt							;
		reg						scl_reg							;
		wire					scl_neg							;
		
		reg				[ 2:0]	state_send						;
		reg				[ 7:0]	send_byte						;
		wire			[ 7:0]	send_third_byte					;
		reg						sda_send_reg					;
		reg						sda_fpga_ctrl					;
		reg				[ 3:0]	send_bit_cnt					;
		reg				[ 3:0]	send_jump_state					;
		reg						ack_received					;
		reg				[ 3:0]	state_read						;
		reg				[ 2:0]	read_bit_cnt					;
		
		
		always @(posedge clk or negedge reset)begin
			if(reset==0)
				state_c_global	<=	INIT	;
			else
				state_c_global	<=	state_n_global	;
		end
		
		always @(*)begin
			case(state_c_global)
				INIT:begin
					if(init_end)
						state_n_global	=	SEND_COMMAND	;
					else
						state_n_global	=	state_c_global	;
				end
				SEND_COMMAND:begin
					if(send_command_end)
						state_n_global	=	WAIT_RESULT_1	;
					else
						state_n_global	=	state_c_global	;
				end
				WAIT_RESULT_1:begin
					if(wait_result_1_end)
						state_n_global	=	READ_RESULT		;
					else
						state_n_global	=	state_c_global	;
				end
				READ_RESULT:begin
					if(read_result_end)
						state_n_global	=	WAIT_RESULT_2	;
					else
						state_n_global	=	state_c_global	;
				end
				WAIT_RESULT_2:begin
					if(wait_result_2_end)
						state_n_global	=	READ_RESULT		;
					else
						state_n_global	=	state_c_global	;
				end
				default:
					state_n_global		=	INIT			;
			endcase
		end
		
		assign	init_end			=	state_c_global==INIT			&&	cnt==0			;
		assign	send_command_end	=	state_send==SEND_END			&&	scl_high_middle	;
		assign	wait_result_1_end	=	state_c_global==WAIT_RESULT_1	&&	cnt==0			;
		assign	read_result_end		=	state_read==READ_END			&&	scl_high_middle	;
		assign	wait_result_2_end	=	state_c_global==WAIT_RESULT_2	&&	cnt==0			;
		
		always @(posedge clk or negedge reset)begin
			if(reset==0)
				cnt	<=	TIME_1S	-1	;
			else if(send_command_end)
				cnt	<=	TIME_180MS	-1	;
			else if(read_result_end)
				cnt	<=	TIME_120MS	-1	;
			else if(cnt!=0)
				cnt	<=	cnt-1		;
		end
		
		assign	scl_en				=	state_c_global==SEND_COMMAND	||	state_c_global==READ_RESULT	;
		assign	scl_cnt_end			=	scl_en	&&	scl_cnt==CLOCK_DIV-1	;
		assign	scl_high_middle		=	scl_en	&&	scl_cnt==CLOCK_DIV_QUAT -1	;
		assign	scl_low_middle		=	scl_en	&&	scl_cnt==CLOCK_DIV_HALF + CLOCK_DIV_QUAT	-1	;
		
		always @(posedge clk or negedge reset)begin
			if(reset==0)
				scl_cnt	<=	0	;
			else if(scl_en)begin
				if(scl_cnt_end)
					scl_cnt	<=	0	;
				else	
					scl_cnt	<=	scl_cnt	+1	;
			end
			else 
				scl_cnt	<=	0	;
		end
				
		assign	scl		=	scl_en	?	((scl_cnt	<	CLOCK_DIV_HALF	)?	1'b1	:	1'b0	)	:	1'b1	;
		
		always @(posedge clk or negedge reset)begin
			if(reset==0)
				scl_reg	<=	1	;
			else 
				scl_reg	<=	scl	;
		end
		
		assign	scl_neg	=	scl==0	&&	scl_reg==1	;
		assign	send_third_byte		=	SEND_THIRD_BYTE			;
		
		always @(posedge clk or negedge reset)begin
			if(reset==0)begin
				sda_fpga_ctrl	<=	1	;
				sda_send_reg	<=	1	;
				send_bit_cnt	<=	0	;
				send_jump_state	<=	0	;	
				ack_received	<=	0	;
				read_bit_cnt	<=	0	;
				state_read		<=	READ_INIT	;
				state_send		<=	SEND_INIT	;
				lux_data		<=	16'd0		;
			end
			else if(state_c_global==SEND_COMMAND)begin
				case(state_send)
					SEND_INIT:begin
						sda_fpga_ctrl	<=	1	;
						sda_send_reg	<=	1	;
						send_bit_cnt	<=	0	;
						send_jump_state	<=	0	;
						ack_received	<=	0	;
						state_send		<=	LOAD_FIRST_BYTE	;
					end
					LOAD_FIRST_BYTE:begin
						send_byte		<=	SEND_FIRST_BYTE	;
						state_send		<=	SEND_START		;
						send_jump_state	<=	LOAD_SECOND_BYTE;
					end
					LOAD_SECOND_BYTE:begin	
						send_byte		<=	SEND_SECOND_BYTE;
						state_send		<=	SEND_BYTE		;
						send_jump_state	<=	SEND_END		;
					end
					SEND_START:begin
						if(scl_high_middle)begin
							sda_send_reg	<=	0			;
							state_send		<=	SEND_BYTE	;
						end
						else
							state_send		<=	state_send	;
					end
					SEND_BYTE:begin
						if(scl_low_middle)begin
							if(send_bit_cnt==8)begin
								send_bit_cnt	<=	0		;
								state_send		<=	RECIVE_ACK	;
							end
							else begin
								sda_send_reg	<=	send_byte[7-send_bit_cnt]		;	
								send_bit_cnt	<=	send_bit_cnt	+1				;
							end
						end
						else 
							state_send	<=	state_send	;
					end
					RECIVE_ACK:begin
						sda_fpga_ctrl	<=	0	;
						if(scl_high_middle)begin
							ack_received	<=	sda			;
							state_send		<=	CHECK_ACK	;
						end
						else 
							state_send		<=	state_send	;
					end
					CHECK_ACK:begin
						if(ack_received==1'b0)begin
							if(scl_neg)begin
								state_send	<=	send_jump_state	;
								sda_fpga_ctrl	<=	1		;
								sda_send_reg	<=	0		;
							end
							else 
								state_send	<=	state_send	;
						end
						else 
							state_send		<=	SEND_INIT	;
					end
					SEND_END:begin
						sda_fpga_ctrl	<=	1	;
						if(scl_high_middle)begin
							sda_send_reg	<=	1	;
							state_send		<=	SEND_INIT	;
						end
						else 
							state_send		<=	state_send	;
					end
					default:begin
						state_send		<=	SEND_INIT	;
						ack_received	<=	0			;
						sda_fpga_ctrl	<=	1			;
						sda_send_reg	<=	1			;
						send_bit_cnt	<=	0			;
						send_jump_state	<=	0			;
					end
				endcase
			end
			else if(state_c_global==READ_RESULT)begin
				case(state_read)
					READ_INIT:begin
						sda_fpga_ctrl	<=	1	;
						sda_send_reg	<=	1	;
						ack_received	<=	0	;
						read_bit_cnt	<=	0	;
						send_bit_cnt	<=	0	;
						state_read		<=	READ_START	;
					end
					READ_START:begin
						if(scl_high_middle)begin
							sda_send_reg	<=	0	;
							state_read		<=	READ_SEND_BYTE	;
						end
						else
							state_read		<=	state_read		;
					end
					READ_SEND_BYTE:begin
						if(scl_low_middle)begin
							if(send_bit_cnt==8)begin
								send_bit_cnt	<=	0	;
								state_read		<=	READ_ACK	;
							end
							else begin
								sda_send_reg	<=	send_third_byte[7-send_bit_cnt]	;
								send_bit_cnt	<=	send_bit_cnt	+1	;
							end
						end
						else 
							state_read	<=	state_read	;
					end
					READ_ACK:begin
						sda_fpga_ctrl	<=		0	;
						if(scl_high_middle)begin
							ack_received	<=	sda	;
							state_read		<=	CHECK_READ_ACK	;
						end
						else
							state_read		<=	state_read		;
					end
					CHECK_READ_ACK:begin
						if(ack_received==1'b0)
							state_read		<=	READ_HIGH_BYTE	;
						else
							state_read		<=	READ_INIT		;
					end
					READ_HIGH_BYTE:begin
						if(scl_high_middle)begin
							if(read_bit_cnt==7)begin
								read_bit_cnt	<=		0		;
								lux_data[8]		<=		sda		;
								state_read		<=		SEND_FIRST_ACK	;
							end
							else begin
								lux_data[15-read_bit_cnt]	<=	sda		;
								read_bit_cnt	<=		read_bit_cnt	+1	;
							end
						end
						else
							state_read		<=	state_read		;
					end
					SEND_FIRST_ACK:begin
						if(scl_neg)begin
							sda_fpga_ctrl	<=	1	;
							sda_send_reg	<=	0	;
							state_read		<=	SEND_ACK_READ	;
						end
						else
							state_read		<=	state_read		;
					end
					SEND_ACK_READ:begin
						if(scl_neg)begin
							sda_fpga_ctrl	<=	0	;
							sda_send_reg	<=	1	;
							state_read		<=	READ_LOW_BYTE	;
						end
						else
							state_read		<=	state_read		;
					end
					READ_LOW_BYTE:begin
						if(scl_high_middle)begin
							if(read_bit_cnt==7)begin
								read_bit_cnt	<=		0		;
								lux_data[0]		<=		sda		;
								state_read		<=		SEND_SECOND_ACK	;
							end
							else begin
								lux_data[7-read_bit_cnt]	<=	sda		;
								read_bit_cnt	<=		read_bit_cnt	+1	;
							end
						end
						else
							state_read	<=	state_read		;
					end
					SEND_SECOND_ACK:begin
						if(scl_neg)begin
							sda_fpga_ctrl	<=	1	;
							sda_send_reg	<=	1	;
							state_read		<=	SEND_ACK_END	;
						end
						else
							state_read		<=	state_read	;
					end
					SEND_ACK_END:begin
						if(scl_neg)begin
							state_read	<=	READ_END		;
							sda_send_reg<=	0				;
						end
						else
							state_read	<=	state_read		;
					end
					READ_END:begin
						if(scl_high_middle)begin
							sda_send_reg	<=	1			;
							state_read		<=	READ_INIT	;
						end
						else
							state_read		<=	state_read	;
					end
					default:begin
						sda_fpga_ctrl	<=	1	;
						sda_send_reg	<=	1	;	
						ack_received	<=	0	;	
						read_bit_cnt	<=	0	;
						send_bit_cnt	<=	0	;		
						state_read		<=	READ_INIT	;	
					end
				endcase
			end
			else begin
				sda_fpga_ctrl	<=	1	;
			    sda_send_reg	<=	1	;
				send_bit_cnt	<=	0	;			
				send_jump_state	<=	0	;			
				ack_received	<=	0	;		
				read_bit_cnt	<=	0	;				
				state_read		<=	READ_INIT	;		
				state_send		<=	SEND_INIT	;	
			end
		end
		
		assign	lux_data_vld	=	read_result_end	;
		assign	sda		=		sda_fpga_ctrl	?	sda_send_reg	:	1'bz		;
		
endmodule


           

仿真驗證

FPGA——驅動BH1750

前兩個箭頭之間是IIC發送指令,後面就是循環IIC讀取資料了,最後上闆也是沒有問題的。不過要注意直接得到的16bit光強值還需要除以1.2才是真實光強值,我們處理起來可以調用IP核先乘10再除以12。