BH1750环境光强度传感器FPGA驱动

BH1750环境光强度传感器FPGA驱动

1. BH1750介绍
       BH1750是一种用于两线式串行总线接口的数字型光强度传感器集成电路。这种集成电路可以根据收集的光线强度数据来调整液晶或者键盘背景灯的亮度。利用它的高分辨率可以探测较大范围的光强度变化。(1x-65535lx)
特点:
1.支持IIC接口。
2.接近视觉灵敏度的光谱灵敏度特性(峰值灵敏度波长典型值:560nm).
3.输出对应亮度的数字值。
4.对应广泛的输入光范围(相当于1-65535lx)。
5.通过降低功率功能,实现低电流化。

BH1750环境光强度传感器FPGA驱动_第1张图片

引脚名称 介绍
GND 电源地
VCC 供电电源
SCL IIC时钟线
SDA IIC数据线,双向IO口
ADDR IIC地址线,接GND时地址为:0100011;接VCC时地址为:1011100

2. BH1750的IIC通信
      
BH1750环境光强度传感器FPGA驱动_第2张图片
上面所示是BH1750用IIC通信协议实现测量的步骤。如果忽略第二步的等待时间,这是一个标准的IIC读取过程,因此为了方便,可以在标准的IIC通信协议上做修改,再第二次写入器件地址前做一个延时等待判断。当延时时间为200ms时,开始执行第三步的操作。同时需要注意,BH1750的输出数据为16bit,IIC读取时要改为16位读取。

3. 整体模块
BH1750环境光强度传感器FPGA驱动_第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
						      scl  <= 1'b0;
							  sda_dir <= 1'b1;
							  sda_out <= 1'b0;  //主机应答
						  end
				    7'd33: scl  <= 1'b1;
				    7'd35: scl  <= 1'b0;
				    7'd40: sda_dir <= 1'b0;
					7'd41: begin
                        data_r[7] <= sda_in;
                        scl       <= 1'b1;
                    end
                    7'd43: scl  <= 1'b0;
                    7'd45: begin
                        data_r[6] <= sda_in ;
                        scl       <= 1'b1   ;
                    end
                    7'd47: scl  <= 1'b0;
                    7'd49: begin
                        data_r[5] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd51: scl  <= 1'b0;
                    7'd53: begin
                        data_r[4] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd55: scl  <= 1'b0;
                    7'd57: begin
                        data_r[3] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd59: scl  <= 1'b0;
                    7'd61: begin
                        data_r[2] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd63: scl  <= 1'b0;
                    7'd65: begin
                        data_r[1] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd67: scl  <= 1'b0;
                    7'd69: begin
                        data_r[0] <= sda_in;
                        scl       <= 1'b1  ;
                    end
						 
                    7'd71: scl  <= 1'b0;
                    7'd72: begin
                        sda_dir <= 1'b1;              // 非应答
                        sda_out <= 1'b1;
                    end
                    7'd73: scl     <= 1'b1;
                    7'd74: st_done <= 1'b1;
                    7'd75: 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,请自行查阅手册得到计算公式和参数。

BH1750环境光强度传感器FPGA驱动_第4张图片

你可能感兴趣的:(fpga,嵌入式,verilog)