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

子產品一共五個引腳:Vcc、GND、SCL、SDA和ADDR。
BH1750晶片手冊
大緻浏覽下BH1750晶片手冊,可以獲得以下資訊:
1、
晶片采用标準的IIC總線接口。
2、
這張圖說明晶片由兩種工作模式:One Time 和 Continuous 模式,在One Time模式下讀取完一次光強值會直接自動回到Power Down狀态,也就是再想讀光強的話需要重新寫一次寄存器,而Continuous模式則可以連續讀。
3、
下面标黃的句子告訴我們推薦使用H-Resolution Mode,在上面的表中我們可以查到這個模式對應的Opencode是0001_0000,其實就對應寫進寄存器的資料。
4、
這裡說明了寄存器的位址是什麼,它對應于子產品ADDR引腳的電平高低,如果是>=0.7VCC的高電平,則寄存器位址為1011100,反之則為0100011。
5、
晶片手冊中也給了我們幾個example,這個例子是ADDR低電平,Continuously H-resolution mode的,符合我們的要求,可見它的寫寄存器位址和資料都是上文提到的,是以我們可以直接以這種方式驅動BH1750去擷取環境光強。還需要注意發送完指令後需要等待180ms的時間才可以進行read result的操作,然後每隔120ms可以重新讀取一次。
IIC總線時序
1、首先是IIC總線通信的開始條件和結束條件:
即在串行時鐘線SCL為高時拉低SDA,表明開始一次IIC通信。在串行時鐘線SCL為高時拉高SDA,表明結束一次IIC通信。
2、然後,就要注意SCL為高時,SDA資料線上的資料必須穩定。不管是誰控制SDA資料線都必須遵循這個原則。
隻有在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
仿真驗證
前兩個箭頭之間是IIC發送指令,後面就是循環IIC讀取資料了,最後上闆也是沒有問題的。不過要注意直接得到的16bit光強值還需要除以1.2才是真實光強值,我們處理起來可以調用IP核先乘10再除以12。