BH1750环境光强度传感器FPGA驱动
1. BH1750介绍
BH1750是一种用于两线式串行总线接口的数字型光强度传感器集成电路。这种集成电路可以根据收集的光线强度数据来调整液晶或者键盘背景灯的亮度。利用它的高分辨率可以探测较大范围的光强度变化。(1x-65535lx)
特点:
1.支持IIC接口。
2.接近视觉灵敏度的光谱灵敏度特性(峰值灵敏度波长典型值:560nm).
3.输出对应亮度的数字值。
4.对应广泛的输入光范围(相当于1-65535lx)。
5.通过降低功率功能,实现低电流化。
引脚名称 | 介绍 |
---|---|
GND | 电源地 |
VCC | 供电电源 |
SCL | IIC时钟线 |
SDA | IIC数据线,双向IO口 |
ADDR | IIC地址线,接GND时地址为:0100011;接VCC时地址为:1011100 |
2. BH1750的IIC通信
上面所示是BH1750用IIC通信协议实现测量的步骤。如果忽略第二步的等待时间,这是一个标准的IIC读取过程,因此为了方便,可以在标准的IIC通信协议上做修改,再第二次写入器件地址前做一个延时等待判断。当延时时间为200ms时,开始执行第三步的操作。同时需要注意,BH1750的输出数据为16bit,IIC读取时要改为16位读取。
3. 整体模块
key_filter模块是按键输入的消抖模块,当按下按键是,FPGA开始用IIC通信协议向BH1750通信,经过延时等待后得到数据。将数据利用UART串口模块发送到上位机计算使用。
4. 代码
按键消抖
module key_filter(Clk,Rst_n,key_in,key_flag,key_state);
input Clk;
input Rst_n;
input key_in;
output reg key_flag;
output reg key_state;
localparam
IDEL = 4'b0001,
FILTER0 = 4'b0010,
DOWN = 4'b0100,
FILTER1 = 4'b1000;
reg [3:0]state;
reg [19:0]cnt;
reg en_cnt; //使能计数寄存器
//对外部输入的异步信号进行同步处理
reg key_in_sa,key_in_sb;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_sa <= 1'b0;
key_in_sb <= 1'b0;
end
else begin
key_in_sa <= key_in;
key_in_sb <= key_in_sa;
end
reg key_tmpa,key_tmpb;
wire pedge,nedge;
reg cnt_full;//计数满标志信号
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_tmpa <= 1'b0;
key_tmpb <= 1'b0;
end
else begin
key_tmpa <= key_in_sb;
key_tmpb <= key_tmpa;
end
//产生跳变沿信号
assign nedge = !key_tmpa & key_tmpb;
assign pedge = key_tmpa & (!key_tmpb);
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
en_cnt <= 1'b0;
state <= IDEL;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDEL :
begin
key_flag <= 1'b0;
if(nedge)begin
state <= FILTER0;
en_cnt <= 1'b1;
end
else
state <= IDEL;
end
FILTER0:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
en_cnt <= 1'b0;
state <= DOWN;
end
else if(pedge)begin
state <= IDEL;
en_cnt <= 1'b0;
end
else
state <= FILTER0;
DOWN:
begin
key_flag <= 1'b0;
if(pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
state <= DOWN;
end
FILTER1:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
end
else if(nedge)begin
en_cnt <= 1'b0;
state <= DOWN;
end
else
state <= FILTER1;
default:
begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 4_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
endmodule
IIC驱动
module i2c_dri
#(// slave address(器件地址)
parameter SLAVE_ADDR = 7'b0100011 ,
parameter CLK_FREQ = 26'd50_000_000, // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 // I2C的SCL时钟频率
)(
//global clock
input clk , // i2c_dri模块的驱动时钟(CLK_FREQ)
input rst_n , // 复位信号
//i2c interface
input i2c_exec , // I2C触发执行信号
input bit_ctrl , // 字地址位控制(16b/8b)
input i2c_rh_wl , // I2C读写控制信号
input [ 7:0] i2c_addr , // I2C器件内地址
input [ 7:0] i2c_data_w , // I2C要写的数据
output reg [ 15:0] i2c_data_r , // I2C读出的数据
output reg i2c_done , // I2C一次操作完成
output reg scl , // I2C的SCL时钟信号
inout sda , // I2C的SDA信号
//user interface
output reg dri_clk // 驱动I2C操作的驱动时钟
);
//localparam define
localparam st_idle = 8'b0000_0001; // 空闲状态
localparam st_sladdr = 8'b0000_0010; // 发送器件地址(slave address)
localparam st_addr16 = 8'b0000_0100; // 发送16位字地址
localparam st_addr8 = 8'b0000_1000; // 发送8位字地址
localparam st_data_wr = 8'b0001_0000; // 写数据(8 bit)
localparam st_addr_rd = 8'b0010_0000; // 发送器件地址读
localparam st_data_rd = 8'b0100_0000; // 读数据(8 bit)
localparam st_stop = 8'b1000_0000; // 结束I2C操作
//reg define
reg sda_dir ; // I2C数据(SDA)方向控制
reg sda_out ; // SDA输出信号
reg st_done ; // 状态结束
reg wr_flag ; // 写标志
reg [ 6:0] cnt ; // 计数
reg [ 7:0] cur_state ; // 状态机当前状态
reg [ 7:0] next_state ; // 状态机下一状态
reg [15:0] addr_t ; // 地址
reg [15:0] data_r ; // 读取的数据
reg [ 7:0] data_wr_t ; // I2C需写的数据的临时寄存
reg [ 9:0] clk_cnt ; // 分频时钟计数
reg [15:0] delay_cnt ; // 延时200ms时钟计数
reg delay_done ; // 延时结束
//wire define
wire sda_in ; // SDA输入信号
wire [8:0] clk_divide ; // 模块驱动时钟的分频系数
//SDA控制
assign sda = sda_dir ? sda_out : 1'bz; // SDA数据输出或高阻
assign sda_in = sda ; // SDA数据输入
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 3; // 模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b1;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide - 1'd1) begin
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//延时200ms等待BH1750采样转换结束
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
delay_cnt <= 16'd0;
end
else if((cur_state ==st_addr_rd) && (delay_cnt < 16'd50_000))
delay_cnt <= delay_cnt + 1'b1;
else
delay_cnt <= 16'd0;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
delay_done <= 1'b0;
end
else if(delay_cnt == 16'd49_999)
delay_done <= ~delay_done;
end
//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @( * ) begin
case(cur_state)
st_idle: begin // 空闲状态
if(i2c_exec) begin
next_state = st_sladdr;
end
else
next_state = st_idle;
end
st_sladdr: begin
if(st_done) begin
if(bit_ctrl) // 判断是16位还是8位字地址
next_state = st_addr16;
else
next_state = st_addr8 ;
end
else
next_state = st_sladdr;
end
st_addr16: begin // 写16位字地址
if(st_done) begin
next_state = st_addr8;
end
else begin
next_state = st_addr16;
end
end
st_addr8: begin // 8位字地址
if(st_done) begin
if(wr_flag==1'b0) // 读写判断
next_state = st_data_wr;
else
next_state = st_addr_rd;
end
else begin
next_state = st_addr8;
end
end
st_data_wr: begin // 写数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_wr;
end
st_addr_rd: begin // 写地址以进行读数据
if(st_done) begin
next_state = st_data_rd;
end
else begin
next_state = st_addr_rd;
end
end
st_data_rd: begin // 读取数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_rd;
end
st_stop: begin // 结束I2C操作
if(st_done)
next_state = st_idle;
else
next_state = st_stop ;
end
default: next_state= st_idle;
endcase
end
//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done <= 1'b0;
cnt <= 1'b0;
st_done <= 1'b0;
data_r <= 1'b0;
i2c_data_r <= 1'b0;
wr_flag <= 1'b0;
addr_t <= 1'b0;
data_wr_t <= 1'b0;
end
else begin
st_done <= 1'b0 ;
cnt <= cnt +1'b1 ;
case(cur_state)
st_idle: begin // 空闲状态
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done<= 1'b0;
cnt <= 7'b0;
if(i2c_exec) begin
wr_flag <= i2c_rh_wl ;
addr_t <= i2c_addr ;
data_wr_t <= i2c_data_w;
end
end
st_sladdr: begin // 写地址(器件地址和字地址)
case(cnt)
7'd1 : sda_out <= 1'b0; // 开始I2C
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; // 传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b0; // 0:写
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: st_done <= 1'b1;
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr16: begin
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[15]; // 传送字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[14];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[13];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[12];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[11];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[10];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[9];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[8];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr8: begin
case(cnt)
7'd0: begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[7]; // 字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_wr: begin // 写数据(8 bit)
case(cnt)
7'd0: begin
sda_out <= data_wr_t[7]; // I2C写8位数据
sda_dir <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_wr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_wr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= data_wr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= data_wr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= data_wr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= data_wr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= data_wr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1;
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr_rd: begin // 写地址以进行读数据
if(delay_done) begin
case(cnt)
7'd0 : begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd2 : sda_out <= 1'b0; // 重新开始
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; // 传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b1; // 1:读
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0; // 从机应答
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: st_done <= 1'b1;
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
end
st_data_rd: begin // 读取数据(16 bit)
case(cnt)
7'd0: sda_dir <= 1'b0;
7'd1: begin
data_r[15] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0;
7'd5: begin
data_r[14] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0;
7'd9: begin
data_r[13] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0;
7'd13: begin
data_r[12] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0;
7'd17: begin
data_r[11] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0;
7'd21: begin
data_r[10] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0;
7'd25: begin
data_r[9] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0;
7'd29: begin
data_r[8] <= sda_in;
scl <= 1'b1 ;
end
7'd31: begin
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd33: scl <= 1'b0;
7'd35: begin
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd37: scl <= 1'b0;
7'd39: begin
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd41: scl <= 1'b0;
7'd43: begin
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd45: scl <= 1'b0;
7'd47: begin
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd49: scl <= 1'b0;
7'd51: begin
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd53: scl <= 1'b0;
7'd55: begin
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd57: scl <= 1'b0;
7'd59: begin
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd61: scl <= 1'b0;
7'd62: begin
sda_dir <= 1'b1; // 非应答
sda_out <= 1'b1;
end
7'd63: scl <= 1'b1;
7'd64: st_done <= 1'b1;
7'd65: begin
scl <= 1'b0;
cnt <= 1'b0;
i2c_data_r <= data_r;
end
default : ;
endcase
end
st_stop: begin // 结束I2C操作
case(cnt)
7'd0: begin
sda_dir <= 1'b1; // 结束I2C
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1;
7'd15: st_done <= 1'b1;
7'd16: begin
cnt <= 1'b0;
i2c_done <= 1'b1; // 向上层模块传递I2C结束信号
end
default : ;
endcase
end
endcase
end
end
endmodule
串口模块
module uart_byte_tx(
Clk,
Rst,
data_byte,
send_en,
baud_set,
Rs232_Tx,
Tx_Done,
uart_state
);
input Clk;
input Rst;
input [15:0]data_byte;
input send_en;
input [2:0]baud_set;
output reg Rs232_Tx;
output reg Tx_Done;
output reg uart_state;
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//波特率时钟
reg [15:0]bps_DR;//分频计数最大值
reg [4:0]bps_cnt;//波特率时钟计数器
reg [15:0]r_data_byte;
localparam start_bit = 1'b0;
localparam stop_bit = 1'b1;
always@(posedge Clk or negedge Rst)
if(!Rst)
uart_state <= 1'b0;
else if(send_en )
uart_state <= 1'b1;
else if(Tx_Done)
uart_state <= 1'b0;
else
uart_state <= uart_state;
always@(posedge Clk or negedge Rst)
if(!Rst)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
always@(posedge Clk or negedge Rst)
if(!Rst)
bps_DR <= 16'd5207;
else begin
case(baud_set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'D2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default:bps_DR <= 16'd5207;
endcase
end
//counter
always@(posedge Clk or negedge Rst)
if(!Rst)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <=div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
//bps_clk
always@(posedge Clk or negedge Rst)
if(!Rst)
bps_clk <= 1'b0;
else if(div_cnt == 1'b1)
bps_clk <=1'b1;
else
bps_clk <= 1'b0;
always@(posedge Clk or negedge Rst)
if(!Rst)
bps_cnt <= 5'd0;
else if(Tx_Done)
bps_cnt <= 5'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
always@(posedge Clk or negedge Rst)
if(!Rst)
Tx_Done <= 1'b0;
else if(bps_cnt == 5'd21)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
always@(posedge Clk or negedge Rst)
if(!Rst)
Rs232_Tx <= 1'b1;
else begin
case(bps_cnt)
0:Rs232_Tx <= 1'b1;
1:Rs232_Tx <= start_bit;
2:Rs232_Tx <= r_data_byte[8];
3:Rs232_Tx <= r_data_byte[9];
4:Rs232_Tx <= r_data_byte[10];
5:Rs232_Tx <= r_data_byte[11];
6:Rs232_Tx <= r_data_byte[12];
7:Rs232_Tx <= r_data_byte[13];
8:Rs232_Tx <= r_data_byte[14];
9:Rs232_Tx <= r_data_byte[15];
10:Rs232_Tx <= stop_bit;
11:Rs232_Tx <= 1'b1;
12:Rs232_Tx <= start_bit;
13:Rs232_Tx <= r_data_byte[0];
14:Rs232_Tx <= r_data_byte[1];
15:Rs232_Tx <= r_data_byte[2];
16:Rs232_Tx <= r_data_byte[3];
17:Rs232_Tx <= r_data_byte[4];
18:Rs232_Tx <= r_data_byte[5];
19:Rs232_Tx <= r_data_byte[6];
20:Rs232_Tx <= r_data_byte[7];
21:Rs232_Tx <= stop_bit;
default:Rs232_Tx <= 1'b1;
endcase
end
endmodule
顶层模块
module BH1750_CTRL(
input clk,
input rst_n,
input key3,
output rx232_tx,
output scl,
inout sda
);
wire key_flag ;
wire key_state ;
wire start_en ; //按键启动信号
wire en ; //定时1秒启动
wire i2c_exec ; //I2C触发执行信号
wire i2c_done ; //I2C寄存器配置完成信号
wire i2c_dri_clk ; //I2C操作时钟
wire [15:0] i2c_data_r ; //I2C读出数据
parameter SLAVE_ADDR = 7'h23 ; //BH1750的器件地址7'h3c
parameter BIT_CTRL = 1'b0 ; //字节地址为8位 0:8位 1:16位
parameter CLK_FREQ = 26'd50_000_000; //时钟频率 50MHz
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率,250KHz
assign start_en = key_flag && !key_state ;
i2c_dri
#(
.SLAVE_ADDR (SLAVE_ADDR), //参数传递
.CLK_FREQ (CLK_FREQ ),
.I2C_FREQ (I2C_FREQ )
)
u_i2c_dri(
.clk (clk ),
.rst_n (rst_n ),
.i2c_exec (start_en ),
.bit_ctrl (BIT_CTRL ),
.i2c_rh_wl (1'b1), //固定为读操作
.i2c_addr (8'h10),
.i2c_data_w (0),
.i2c_data_r (i2c_data_r),
.i2c_done (i2c_done ),
.scl (scl ),
.sda (sda ),
.dri_clk (i2c_dri_clk) //I2C操作时钟
);
uart_byte_tx u_uart_byte_tx(
.Clk (clk),
.Rst (rst_n),
.data_byte (i2c_data_r),
.send_en (i2c_done),
.baud_set (3'd0),
.Rs232_Tx (rx232_tx),
.Tx_Done (),
.uart_state()
);
key_filter u_key_filter(
.Clk (i2c_dri_clk),
.Rst_n (rst_n),
.key_in (key3),
.key_flag (key_flag),
.key_state(key_state)
);
endmodule
注意:按键消抖模块的驱动时钟使用IIC驱动模块的输出时钟1MHz,如果使用50MHz的输入时钟,则按键启动信号的高电平持续周期时间太短,可能不会被IIC模块启动信号捕捉到,从而导致按键按下,但IIC通信并未开始。
5. 验证
将程序下载至开发板,将芯片模块引脚连接好,打开串口调试助手,波特率选择9600,无检验位,数据位8位,1位停止位。按下按键,得到16位环境光强度数据,用光照射芯片,再按下按键,可以看到数据变大,多次测试无误,证明验证成功。PS:得到的161位数据还需要转换成十进制数据进行计算才可以得到光强数据,单位lx,请自行查阅手册得到计算公式和参数。