天天看点

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。