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 [