BH1750 传感器实战教学 —— 驱动移植篇_bh1750 f4源码

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

void i2c\_start(void)  {                                     
	sda\_high(); 
		\_nop\_(); \_nop\_();\_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
	scl\_high();	
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); 
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();	
	sda\_low();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();	
	scl\_low();                                                   
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();
		\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_(); \_nop\_();\_nop\_();	
}

这里的延时时间是根据自己以前使用的经验而定的,前面也说了 I2C 通讯中延时多少个机器周期,并没有准确的要求,多几个 nop 都是无所谓的。

但是我们可以可以把这么多 _nop_ 写成一个函数,类似在 STM32 下一样,因为是不准确的,所以这里不用 us 表示:

void delay\_nop(uint32 delay){
	uint32\_t i = 0;
	for(i = 0; i < delay; i++)\_nop\_();
}

最终我们的 i2c.h 如下:

#include "i2c.h"

void delay\_nop(uint32 delay){
	uint32 i;
	for(i = 0; i < delay; i++)\_nop\_();
}

void i2c\_start(void)  {                                     
  	sda\_high(); 
		delay\_nop(28);
  	scl\_high();	
		delay\_nop(28);
  	sda\_low();
		delay\_nop(28);
	scl\_low();                                                   
		delay\_nop(28);
}
// ------------------------------------------------------------------
// send stop sequence (P)
void i2c\_stop(void)  {                             
  sda\_low();                                                                          
		delay\_nop(28);
  scl\_low();                                     
		delay\_nop(28);                                   
  scl\_high();                                    
		delay\_nop(28);                                             
  sda\_high();
		delay\_nop(28);
}
// ------------------------------------------------------------------
// returns the ACK or NACK
uint8 i2c\_write(uint8 u8Data)  
{
	uint8 u8Bit;
	uint8 u8AckBit;

  // write 8 data bits
	u8Bit = 0x80;                                //msb first 
	while(u8Bit) 
	{
		if(u8Data&u8Bit){ 
			sda\_high();
			delay\_nop(28); 
		}
		else{ 
			sda\_low();
			delay\_nop(28); 
		}			
		scl\_high();
		delay\_nop(80); 						
		u8Bit >>= 1; 
		//next bit
		scl\_low();
		delay\_nop(90); 		
	}

  	// read acknowledge (9th bit) 
 	sda\_high();                                               
	delay\_nop(45);	
		
	scl\_high();
	delay\_nop(45);	
	
	u8AckBit = sda\_read();	//#define sda\_read() (sda\_port & sda\_pin)? 1 :0 ack on bus is low -> u8AckBit = 1 sda\_port gpio0 sda\_pin SCSEDIO0
	delay\_nop(45);	

	scl\_low();                                              
	delay\_nop(45);

  	return u8AckBit;
}
// ------------------------------------------------------------------
// pass the ack/nack 
// returns the read data 
uint8 i2c\_read(uint8 u8Ack)  
{
	uint8 u8Bit;
	uint8 u8Data;

	u8Bit = 0x80;       // msb first
	u8Data = 0;
	// 8 data bits
	while(u8Bit) 
	{
		scl\_high();
		delay\_nop(70);		
			
		u8Bit >>= 1;    //next bit
		u8Data <<= 1;
			
		u8Data |= sda\_read();         //(sda\_port & sda\_pin)? 1 :0 sda\_port gpio0 sda\_pin SCSEDIO0
		delay\_nop(30);
		scl\_low();
		delay\_nop(80);		
	}
	// 9th bit acknowledge
	if(u8Ack==I2C_ACK){
		sda\_low();
		delay\_nop(30);
	}     //I2C\_ACK=0
	else                
	{
		sda\_high();
		delay\_nop(30);
	}

	scl\_high();
	delay\_nop(30);
		
	scl\_low();
	delay\_nop(30);

	sda\_high();
	delay\_nop(30);
											
	return u8Data;
}

二、 BH1750 驱动移植

通用驱动讲完了,我们 BH1750 驱动逻辑可以参考曾经分析的流程,具体的逻辑分析可参考下文:

BH1750 光照传感器文档详解 及 驱动设计

2.1 bh1750.h

我们的 bh1750.h 完全可以和上文中的一样(但是注意一下头文件包含以及数据类型),如下图 :

在这里插入图片描述

2.2 bh1750.c

我们的主要任务在于 bh1750.c 如何实现,我们按照定好的逻辑来:

在这里插入图片描述

这里有一个问题需要注意,因为我们本次是需要低功耗设计,所以我们要考虑到模块通电以后默认状态是怎么状态? 是单次测量还是连续模式?这关系到我们是否每次上电都需要初始化。

带着这个问题我重新看了一遍资料的流程图:

在这里插入图片描述

所以其实 BH1750 并不需要我们曾经文章中提到的 void bh1750_init() 初始化函数,当然有也没有问题 ,只不过当成了进行一次单次测量。

那么我们本次初始化函数也不用写了,直接写测量读取函数就行了。

其实 I2C开始,结束这个倒直接换就行了,我们主要是要注意一下接收不接收 ACK 的处理。 当然,因为我在本次芯片上使用的函数是上面的 i2c.c 提供的,需要注意,如果大家愿意,可以自己修改一下驱动,改成和我们在 stm32 上面一样的,这样子把 通用驱动 修改,传感器驱动基本就一致了,这个看个人。

在我们以前的驱动中,发送一条消息等待 ACK 的语句如下:

IIC\_Send\_Byte(BH1750_ADDRESS << 1); //地址,和读写指令
MYIIC\_Wait\_Ack();

而在我们这个驱动中,我们需要这样做:

u8Ack = i2c\_write(BH1750_ADDRESS << 1);

直接上一下修改的驱动程序把,其中与以前的驱动对比的注释我留着没删除,以做比较:

#include "bh1750.h"

void bh1750\_read(uint16 \*lux)
{
	uint8 read_buffer[2];
	uint32 lv_lux;
	uint8 u8Ack;

	SensorPowerOn();
	time\_wait(200);
	
	i2c\_start();

	// IIC\_Send\_Byte(BH1750\_ADDRESS << 1); //????????
	// MYIIC\_Wait\_Ack();
	u8Ack = i2c\_write(BH1750_ADDRESS << 1);

	// delay\_us(150); 
	delay\_nop(500);

	// IIC\_Send\_Byte(BH1750\_MODE\_ONE\_H\_RES); //????
	// MYIIC\_Wait\_Ack();
	u8Ack = i2c\_write(BH1750_MODE_ONE_H_RES);

	i2c\_stop();

	// HAL\_Delay(BH1750\_MEASURE\_DURATION\_MS);
	time\_wait(BH1750_MEASURE_DURATION_MS);

	i2c\_start();

	// IIC\_Send\_Byte((BH1750\_ADDRESS << 1)|1); //????????
	// MYIIC\_Wait\_Ack();
	u8Ack = i2c\_write((BH1750_ADDRESS << 1)|1);

	// read\_buffer[0] = IIC\_Read\_Byte(1);
	// delay\_us(120);
	// read\_buffer[1] = IIC\_Read\_Byte(0);
	// delay\_us(120);
	read_buffer[0] = i2c\_read(I2C_ACK);
	delay\_nop(450);
	read_buffer[1] = i2c\_read(I2C_NACK);
	delay\_nop(450);

	i2c\_stop();

	SensorPowerOff();

	lv_lux = ((read_buffer[0] << 8) | read_buffer[1]) \* 10 / 12;
	\*lux = (uint16)lv_lux;
}

三、 测试

开始测试……

在需要读取光照的地方使用 bh1750_read(&lux_data); 读取即可。

3.1 问题一 (数据完全不对)

数据是有了,但是数据有点不正常

我们前面都是按照顺序一步一步走过来的,使用电筒照着数据不正确…… 先让我理一理……

这就对了,我早就知道会有问题,要不然也没必要写一篇移植的文章!

……测试中…… 测试中……

其实出了问题也比较麻烦,因为相对 STM32 来说,使用的这个 51 调试起来很麻烦。

还记得我们当时硬件设计的时候使用了电源开关电路(本次测试飞线使用的下图中第一个电路):

在这里插入图片描述

我们在上面的程序中使用了 200 ms 的延时:

在这里插入图片描述

当我们在做低功耗的传感器遇到问题了,为了解决问题我们要先排除电源的问题,所以这里我们先让传感器一直供电。

在这里插入图片描述

当然我以前也说过,I2C 通讯中很有可能出问题的地方是通讯的等待延时,传感器驱动 bh1750.c 中的延时我也修改了,我把驱动中需要的延时 等待改成了 1 ms,如下(这是前后测试了很多的得出的结论):

在这里插入图片描述

修改完成以后,我们测试了一下数据,看上去好像正常了:

在这里插入图片描述

3.2 问题二 (光强时数据异常)

我们测试光照往往是让他测一测正常环境,然后用手电筒照着看看数据是否变大。

经过一系列的折腾,最后测试我发现,在光照强度比较低的时候数据基本是正常的,但是光照强度太高的时候数据就异常了,如下图所示。

正常情况:

在这里插入图片描述

在这里插入图片描述

灯光照射异常情况:

在这里插入图片描述

这不由得让我想起难道是数据读取的时候,高字节的数据读取异常一直为0 ,只能读到 低 8位的数据?

我们来计算机看一下:

在这里插入图片描述

那么这样的话,会不会是读取这个地方有问题 ? 还是说数据处理的时候有问题?

数据问题解决

最后测试来测试去,发现是其中有一条语句有问题:

lv_lux = ((read_buffer[0] << 8) | read_buffer[1]) \* 10 / 12;

在程序中 read_bufferuint8 类型,我们这样直接位移然后与一下是否会有问题?

我把程序改成:

lv_lux = ((read_buffer[0] << 8) + read_buffer[1]) \* 10 / 12;

发现数据就正常了!

为了验证一下是否是数据类型的问题,我把语句改成:

lv_lux = ((uint16)(read_buffer[0] << 8) | read_buffer[1]) \* 10 / 12;

能够正常的读取到光强时候的数据:

在这里插入图片描述

到头来,其实数据异常并不是驱动有问题,而是我们数据处理的细节问题!

3.3 再次处理电源控制的问题

我们把数据读取的问题解决以后,我们还得回到我们的应用上来,电源还是需要不用的时候关短,读取传感器数据时候打开。

那么我们这个问题一般如何解决,大部分情况下,都是加大打开电源后的延时时间!

这个延时时间越大,传感器采集的时候功耗就越大,因为这时候并不是休眠状态,但是太小我们前面测试的 200 ms,发现传感器数据会不正常,可能是电源没有稳定下来,也可能是传感器也需要准备时间,所以这个时间需要自己衡量和测试。

这里把时间改成 500 ms ,发现数据就可以正常的采集。

在这里插入图片描述

最后周期数据采集如下图,测试的时候 6s 采集一次,实际使用根据情况而定:

在这里插入图片描述

结语

本文我们把 BH1750 传感器移植到一个 51 内核的芯片上使用。

过程不算顺利,出了很多小问题,但是整体来说,本文所讲解的知识点都是没有问题的,驱动的移植也算是成功的。

居然在数据处理的时候出了问题,虽然我们当时在 STM32 中程序中的语句是这么写的,而且也测试过了,但是确实在 51 上这条语句确实出了问题,而且中途还找错了方向,以至于我画了很多时间在其他地方 = =!

不过最后通过找到问题,也算是给了大家一个很好的示例。

完成本文,BH1750 的实战教学篇就算完成了,相信大家学习以后,不管在什么芯片上使用 BH1750 甚至是其他 I2C 通讯的传感器,都会顺顺利利!

本文就到这里,谢谢大家! 另外,别忘了下面可以加我的技术群哦!

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

示例。

完成本文,BH1750 的实战教学篇就算完成了,相信大家学习以后,不管在什么芯片上使用 BH1750 甚至是其他 I2C 通讯的传感器,都会顺顺利利!

本文就到这里,谢谢大家! 另外,别忘了下面可以加我的技术群哦!

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-8jgMpBPD-1715845027206)]
[外链图片转存中…(img-jMtOPH7d-1715845027206)]

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值