11.4 实验原理
11.4.1 BMP280模块介绍
BMP280是一款大气压力传感器,采用8pin的2.0mm x 2.5mm贴片LGA封装,数字I2C总线接口,管脚功能描述如下:

BMP280接口可以支持I2C和SPI总线,气I2C接口典型电路如下:

11.4.2 BMP280模块连接
STEP BaseBoard V4.0底板上的气压传感器BMP280模块电路图如下(上拉电阻未显示):

上图为气压传感器BMP280模块电路,与FPGA硬件接口有I2C总线(SCL、SDA)。SDO引脚不能悬空,用来确定I2C器件的地址,可以拉高(0x77)或者拉低(0x76),对于BMP280来说地址是一样的,都可以作为I2C寻址。
11.4.3.BMP280模块驱动设计
智能接近系统设计实验中我们已经讲述学习过I2C总线驱动的设计,本实验可以上原来的基础上调整,首先来了解BMP280时序中的参数要点。


通过BMP280时序参数了解,BMP280可以支持I2C高速模式。
- 分频得到400KHz的时钟,程序实现同智能接近系统设计实验。
I2C时序基本单元(启动、停止、发送、接收、发应答、读应答)协议里统一的,所以所以基本单元状态的设计也是不需要调整的。
- 启动时序状态设计程序实现同智能接近系统设计实验。
- 发 送单元和读应答单元合并,时序状态设计程序实现同智能接近系统设计实验。
- 接收单元和写应答单元合并,时序状态设计程序实现同智能接近系统设计实验。
- 停止时序状态设计程序实现同智能接近系统设计实验。
BMP280测量时会先测量温度,然后测量压力值,然后看设置需不需要进行滤波计算,最后结果存到寄存器。进行驱动时首先软件复位,然后设置配置寄存器,然后读取数据。

本实验涉及软件复位、温度和气压测量、读取配置参数以及读取数据。
软件复位:
I2C写操作时序:

软件复位操作寄存器是0xE0,往寄存器写数值0xB6就能完成器件的复位。同样道理0xF4、0xF5寄存器也是需要进行配置的寄存器,具体参数可以参考数据手册。
我们将这种操作设计成一个一个状态,程序实现如下:
MODE1:begin //16位寄存器写操作
if(cnt_mode1 >= 4'd5) cnt_mode1 <= 1'b0; //对START中的子状态执行控制cnt_start
else cnt_mode1 <= cnt_mode1 + 1'b1;
state_back <= MODE1;
case(cnt_mode1)
4'd0: begin state <= START; end //I2C通信时序中的START
4'd1: begin data_wr <= dev_addr<<1; state <= WRITE; end //设备地址
4'd2: begin data_wr <= reg_addr[15:8]; state <= WRITE; end //寄存器地址
4'd3: begin data_wr <= reg_addr[7:0]; state <= WRITE; end //寄存器地址
4'd4: begin state <= STOP; end //I2C通信时序中的STOP
4'd5: begin state <= MAIN; end //返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
补偿参数读取:
芯片内部有NVM非易事性内存存储了一系列用于补偿计算的参数,这些参数出厂时就写入了不能修改,用于计算测量的温度和气压指。首先我们要读取出这些参数:

I2C读操作:

读取参数用连续两次读操作完成,程序实现如下:
MODE2:begin //两次读操作
if(cnt_mode2 >= 4'd10) cnt_mode2 <= 1'b0;
else cnt_mode2 <= cnt_mode2 + 1'b1;
state_back <= MODE2;
case(cnt_mode2)
4'd0: begin state <= START; end //I2C通信时序中的START
4'd1: begin data_wr <= dev_addr<<1; state <= WRITE; end //设备地址
4'd2: begin data_wr <= reg_addr; state <= WRITE; end //寄存器地址
4'd3: begin state <= START; end //I2C通信时序中的START
4'd4: begin data_wr <= (dev_addr<<1)|8'h01; state <= WRITE; end //设备地址
4'd5: begin ack <= ACK; state <= READ; end //读寄存器数据
4'd6: begin dat_l <= data_r; end
4'd7: begin ack <= NACK; state <= READ; end //读寄存器数据
4'd8: begin dat_h <= data_r; end
4'd9: begin state <= STOP; end //I2C通信时序中的STOP
4'd10: begin state <= MAIN; end //返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下:
MAIN:begin
if(cnt_main >= 4'd4) cnt_main <= 4'd2; //写完控制指令后循环读数据
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
4'd0: begin dev_addr <= 7'h44; reg_addr <= 16'h30a2; state <= MODE1; end //软件复位
4'd1: begin num_delay <= 24'd600; state <= DELAY; end //1.5ms延时
4'd2: begin dev_addr <= 7'h44; reg_addr <= 16'h2c06; state <= MODE1; end //写入配置
4'd3: begin num_delay <= 24'd6000; state <= DELAY; end //15ms延时
4'd4: begin dev_addr <= 7'h44; state <= MODE2; end //读取配置
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
测量数据读取:
BMP280的温度和气压测量数据都是三个字节存储,读取数据时参数用连续三次次读操作完成,程序实现如下:
MODE3:begin //三次读操作
if(cnt_mode3 >= 4'd12) cnt_mode3 <= 1'b0; //对START中的子状态执行控制cnt_start
else cnt_mode3 <= cnt_mode3 + 1'b1;
state_back <= MODE3;
case(cnt_mode3)
4'd0: begin state <= START; end //I2C通信时序中的START
4'd1: begin data_wr <= dev_addr<<1; state <= WRITE; end //设备地址
4'd2: begin data_wr <= reg_addr; state <= WRITE; end //寄存器地址
4'd3: begin state <= START; end //I2C通信时序中的START
4'd4: begin data_wr <= (dev_addr<<1)|8'h01; state <= WRITE; end //设备地址
4'd5: begin ack <= ACK; state <= READ; end //读寄存器数据
4'd6: begin dat_h <= data_r; end
4'd7: begin ack <= ACK; state <= READ; end //读寄存器数据
4'd8: begin dat_l <= data_r; end
4'd9: begin ack <= NACK; state <= READ; end //读寄存器数据
4'd10: begin dat_xl <= data_r; end
4'd11: begin state <= STOP; end //I2C通信时序中的STOP
4'd12: begin state <= MAIN; end //返回MAIN
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end
最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下:
MAIN:begin
if(cnt_main >= 8'd34) cnt_main <= 8'd28; //写完控制指令后循环读数据
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
8'd0: begin dev_addr <= 7'h76; reg_addr <= 8'hE0; reg_data <= 8'hb6; state <= MODE1; end //写入配置
8'd1: begin state <= DELAY; end //200ms延时
8'd2: begin dev_addr <= 7'h76; reg_addr <= 8'hf4; reg_data <= 8'h55; state <= MODE1; end //写入配置
8'd3: begin dev_addr <= 7'h76; reg_addr <= 8'hf5; reg_data <= 8'h00; state <= MODE1; end //写入配置
8'd4: begin dev_addr <= 7'h76; reg_addr <= 8'h88; state <= MODE2; end //读取配置
8'd5: begin dig_t1 <= {dat_h,dat_l}; end //读取数据
8'd6: begin dev_addr <= 7'h76; reg_addr <= 8'h8a; state <= MODE2; end //读取配置
8'd7: begin dig_t2 <= {dat_h,dat_l}; end //读取数据
8'd8: begin dev_addr <= 7'h76; reg_addr <= 8'h8c; state <= MODE2; end //读取配置
8'd9: begin dig_t3 <= {dat_h,dat_l}; end //读取数据
8'd10: begin dev_addr <= 7'h76; reg_addr <= 8'h8e; state <= MODE2; end //读取配置
8'd11: begin dig_p1 <= {dat_h,dat_l}; end //读取数据
8'd12: begin dev_addr <= 7'h76; reg_addr <= 8'h90; state <= MODE2; end //读取配置
8'd13: begin dig_p2 <= {dat_h,dat_l}; end //读取数据
8'd14: begin dev_addr <= 7'h76; reg_addr <= 8'h92; state <= MODE2; end //读取配置
8'd15: begin dig_p3 <= {dat_h,dat_l}; end //读取数据
8'd16: begin dev_addr <= 7'h76; reg_addr <= 8'h94; state <= MODE2; end //读取配置
8'd17: begin dig_p4 <= {dat_h,dat_l}; end //读取数据
8'd18: begin dev_addr <= 7'h76; reg_addr <= 8'h96; state <= MODE2; end //读取配置
8'd19: begin dig_p5 <= {dat_h,dat_l}; end //读取数据
8'd20: begin dev_addr <= 7'h76; reg_addr <= 8'h98; state <= MODE2; end //读取配置
8'd21: begin dig_p6 <= {dat_h,dat_l}; end //读取数据
8'd22: begin dev_addr <= 7'h76; reg_addr <= 8'h9a; state <= MODE2; end //读取配置
8'd23: begin dig_p7 <= {dat_h,dat_l}; end //读取数据
8'd24: begin dev_addr <= 7'h76; reg_addr <= 8'h9c; state <= MODE2; end //读取配置
8'd25: begin dig_p8 <= {dat_h,dat_l}; end //读取数据
8'd26: begin dev_addr <= 7'h76; reg_addr <= 8'h9e; state <= MODE2; end //读取配置
8'd27: begin dig_p9 <= {dat_h,dat_l}; end //读取数据
8'd28: begin dev_addr <= 7'h76; reg_addr <= 8'hf4; reg_data <= 8'h55; state <= MODE1; end //写入配置
8'd29: begin state <= DELAY; dat_valid <= 1'b0; end //200ms延时
8'd30: begin dev_addr <= 7'h76; reg_addr <= 8'hf7; state <= MODE3; end //读取配置
8'd31: begin adc_p <= {dat_h,dat_l,dat_xl[7:4]}; end //读取数据
8'd32: begin dev_addr <= 7'h76; reg_addr <= 8'hfa; state <= MODE3; end //读取配置
8'd33: begin adc_t <= {dat_h,dat_l,dat_xl[7:4]}; end //读取数据
8'd34: begin dat_valid <= 1'b1; end //读取数据
default: state <= IDLE; //如果程序失控,进入IDLE自复位状态
endcase
end