BH1750光照传感器超详细攻略(从原理到代码讲解,看完你就懂了)

2023-05-16

目录

    • 一、前言
    • 二、芯片介绍
    • 三、IIC通讯介绍
      • IIC通讯过程简介
      • IIC通讯实例
      • BH1750的通讯过程
    • 四、BH1750的命令
    • 五、BH1750编程教学
    • 六、测试
    • 七、总结

一、前言

之所以写这篇文章,原因有两个。
一是:有个师弟跟我说我发布的文章都偏向于工作者,能不能写一些大学生能用到的东西,我想了一下,确实是,我写的文章大多是我在工作中总结出来的心得,对于初学者来说确实有点难以理解。
二是:我觉得这个光照传感器很多大学生都能用到,但是网上的教程虽多却也不一定能够帮助大家深入了解这款传感器。大家更多的是看完攻略之后能够驱动,但是其实并不了解它的工作原理,想要在光照传感器的基础上增加别的功能也无从下手。
所以,我觉得我还是有必要写一篇更加详细更加深入的攻略来帮助大家理解。我觉得能驱动一个芯片和会驱动一个芯片是不一样的,如果你学会了如何去驱动一个芯片,那么换了别的类似的芯片你也能够得举一反三。不然的话你每次换一个芯片都只能去找人家写好的代码。
好了,废话不多说了,BH1750的讲解马上开始。(注:请一定要从头到尾看下去,粗略看一下也行,因为内容是环环相扣的,一直看,一直爽!!!)
我再多说一句,就一句,真的,接下来我讲的所有代码以及相关的所有文件都可以免费发给你们,链接在文章底部,自己去下载吧。

除了本文这个驱动外,还有另外一种使用方法,可以参考我发布的博文:
基于stm32驱动bh1750光照传感器的一种超简单的编程方法

二、芯片介绍

BH1750FVI是一款数字型光强度传感器集成芯片。某宝上面很多写着GY30模块,那些其实也是用BH1750FVI芯片,只不过是它把BH1750FVI芯片以及外围的一些电路做到了一个板子上面,然后把BH1750FVI的通讯引脚引出来方便你们用单片机控制而已。(话说大部分国产芯片都是这个套路,把人家的芯片拿过来,加一点外围电路,然后重新包一层外壳,换个型号,就变成自己的产品了)
电路工作原理:如图1所示,BH1750的内部由光敏二极管、运算放大器、ADC采集、晶振等组成。PD二极管通过光生伏特效应将输入光信号转换成电信号,经运算放大电路放大后,由ADC采集电压,然后通过逻辑电路转换成16位二进制数存储在内部的寄存器中(注:进入光窗的光越强,光电流越大,电压就越大,所以通过电压的大小就可以判断光照大小,但是要注意的是电压和光强虽然是一一对应的,但不是成正比的,所以这个芯片内部是做了线性处理的,这也是为什么不直接用光敏二极管而用集成IC的原因)。BH1750引出了时钟线和数据线,单片机通过I2C协议可以与BH1750模块通讯,可以选择BH1750的工作方式,也可以将BH1750寄存器的光照度数据提取出来。
在这里插入图片描述
引脚定义:

引脚号名称说明
1VCC供电电压源正极
2SCLIIC时钟线,时钟输入引脚,由MCU输出时钟
3SDAIIC数据线,双向IO口,用来传输数据
4ADDRIIC地址线,接GND时器件地址为0100011 ,接VCC时器件地址为1011100
5GND供电电压源负极

三、IIC通讯介绍

IIC通讯过程简介

既然BH1750是用IIC通讯的,那么我们就要先了解IIC的通讯原理。IIC由时钟线(SCL)和数据线(SDA)组成。时钟线,听这个名字就知道和时间有关系,没错,它其实管理着IIC的通讯时间。而数据线,顾名思义就是用来传输数据的线。那么时钟线和数据线它们是什么关系呢?你可以把时钟线理解为红绿灯,高电平是绿灯,低电平是红灯,而数据线传输的每一个数据则相当于一辆汽车,高电平是奔驰,低电平是宝马。当绿灯亮了的时候,汽车就可以过去,只不过这里的交通规则是每亮一次绿灯,只能通过一辆汽车。所以,IIC通讯的过程就是红绿灯交替闪烁(也就是时钟线输出方波脉冲),汽车跟着一辆一辆的过去,过去的是奔驰,就是传输了一个“1”,过去的宝马,就是传输了一个“0”,连续传输8次,就可以组成一个8位的二进制数,也就是一个字节的数据,反复这个过程就能实现两个设备之间的通讯。
好,上面已经大概讲解了IIC的通讯过程,那么下面来补充一些细节。IIC通讯的两个设备是有主从关系的,比如我们的单片机在这里就是主设备,BH1750是从设备。
时钟线是由主设备输出,从设备输入的,也就是单片机和BH1750通讯的时候,单片机的IO口要给SCL引脚输出一个方波脉冲,因为IIC设备支持的最大通讯频率一般都是400kHz,也就是说一个时钟周期(一个高电平加一个低电平为一个周期)不能小于2.5us。单片机输出时钟的时候一定要注意高低电平延时的时间,延时的时间越长,通讯的速率越慢。另外,时钟线不会一直输出脉冲,只会在需要通讯的时候输出,并且要遵循一定的规则。需要通讯的时候时钟线先要输出一个“起始信号”告诉从设备我要开始通讯了,其实就是电平由高到低跳变,但是这个高电平的持续时间不能太短,具体最少要多少时间需要看芯片手册,反正延长一点准没错。然后再根据固定的时间输出高低脉冲,直到到了要停止通讯的时候,时钟线要输出一个“结束信号”告诉从设备我不通讯了,其实就是电平一直拉高。
而数据线传输的数据是双向的,单片机可以给BH1750发数据,也可以读取BH1750的数据(也就是BH1750给单片机发)。需要注意的,单片机给BH1750发的数据不是随便发的,也要符合一定的规则。首先,单片机要先发一个器件地址(器件地址是7位的,详细的内容我后面再说),再发送一个读写位(0表示是写入,1表示读取),器件地址和读写位加起来刚好是一个字节,然后BH1750会给你回一个应答位,意思就是“我收到了”。然后单片机就可以接着发送数据了,每次都是以1个字节为间隔发。收也是类似的,只是把单片机发数据改成收数据,这里就不多说了,后面会详细讲。(注:器件地址是用来区分从设备的,因为有时候同一根时钟线和数据线可能会连接多个从设备,也就是说主设备发送的数据所有的从设备都可以收到,所以主设备要先发送一个器件地址,告诉所有的从设备我是给哪个设备发命令,其他设备收到了也不要执行)。

IIC通讯实例

下面我们看一个实际的例子。图2是OPT3001通讯的读写过程,(OPT3001是我在项目中用到一款低功耗光照传感器,和BH1750类似,也是IIC通讯协议,感兴趣的同学可以看一下我发之前的博文,有讲解这个IC的驱动方式),看懂了这个图你就理解IIC的通讯方式了,你就可以当着博主的面大声地说“你写的博文有毛用,你说的我全都知道”,如果你还有不理解的地方,那么就坐下来好好听我解说吧。
首先,我们看一下IIC的写入过程,最左边先是有一个“Start by Master”,也就是单片机先给一个“起始信号”,然后后面接着传输了8位数据(1 0 0 0 1 A1 A0 R/W)。其中,“1 0 0 0 1 A1 A0”是器件地址,因为这里的器件地址有4个可选,所以用了A1和A0表示,(注:BH1750只有2个器件地址),“R/W”是读写位,上面我有说到,这里是写入,所以这里的R/W应该是一个“0”。 接着是“ACK by OPT3001”,这是从设备给主设备发的应答,就是说“你发的数据我收到了,你可以接着发了”,然后接下来的RA7-RA0是寄存器地址(因为寄存器不止一个所以要先发地址,告诉它你接下来要把数据存到哪里),再后面的D15-D0是两个字节的数据(这些数据就是存到前面发的那个地址的寄存器里面)。
读取的过程和写入类似,先是“起始信号”,再是器件地址+读写位,接着是应答,然后开始接收数据(单片机的IO口要从输出改成输入了),D15-D0是接收到两个字节的数据,“ACK by Master”是单片机给OPT3001发的应答。(只要是接收的一方都要发应答,不应答的话通讯就会结束,比如读取的第二个字节后面的“No ACK by Master”)
在这里插入图片描述
好,如果你能坚持看到这里,那我敬你是条汉子!!如果你看懂了,那么恭喜你,如果没看懂,那也没关系,上面那是IIC一般的通讯方式,后面BH1750的通讯要更加简单。
(问:那你为什么不直接讲BH1750。答:我喜欢,你咬我呀,略略略….啪,略略啪,略别别….我错了。)

BH1750的通讯过程

其实前面之所以要先讲这个OPT3001而不是直接讲BH1750,是因为BH1750的IIC其实算是一个简化版的,不具有通用性,你学会了OPT3001的通讯方法,你再去驱动BH1750就很简单,相反,如果你只会驱动BH1750,那么换成别的IIC的芯片你就不一定会了。
好了,接下来我们来看一下BH1750的通讯,BH1750的通讯过程可以分成5步,中间3步如图3所示。
(啪啪,问:为什么要用英文的图,别以为我不知道有中文版的手册,说你是不是在装*。答:冤枉,真不是,那个中文版的图太糊了,而且英文版其实也不影响大家去看,老实说我是一个英语学渣,我还写了一篇博文讲述一个学渣如何看懂英文数据手册,有兴趣的同学可以看一下。真不是打广告哦。)
在这里插入图片描述
第1步:发送上电命令。(上电命令是0x01)。
因为这里没有图,我就不详细说了,发送的过程和第2步基本一致。就是把测量命令(0x10)改成上电命令(0x01)。
第2步:发送测量命令。
下面图片上的例子,ADDR引脚是接GND的,发送的测量命令是“连续高分辨率测量(0x10)”。
发送数据的过程和之前讲的OPT3001写入的过程基本一样,先是“起始信号(ST)”,接着是“器件地址+读写位”(器件地址我在上面引脚定义那里有写),然后是应答位,紧接着就是测量的命令“00010000”(关于测量命令,下面会详细说明),然后应答,最后是“结束信号(SP)”。(相比于OPT3001的写入过程,BH1750少了一个发送寄存器地址的步骤,因为它只有一个寄存器,所以就没必要了)
第3步:等待测量结束。
测量的时间手册上面有写,我这里就不列出来了,高分辨率连续测量需要等待的时间最长,手册上面写的是平均120ms,最大值180ms,所以为了保证每次读取到的数据都是最新测量的,程序上面可以延时200ms以上,当然也不用太长,浪费时间。如果你用别的测量模式,等待时间都比这个模式要短。
第4步:读取数据。
先是“起始信号(ST)”,接着是“器件地址+读写位”,然后是应答位,紧接着接收1个字节的数据(单片机在这个时候要把SDA引脚从输出改成输入了),然后给BH1750发送应答,继续接收1个字节数据,然后不应答(因为我们接收的数据只有2个字节,收完就可以结束通讯了),最后是“结束信号(SP)”。
第5步:计算结果。
接收完两个字节还不算完成,因为这个数据还不是测量出来的光照强度值,我们还需要进行计算,计算公式是:光照强度 =(寄存器值[15:0] * 分辨率) / 1.2 (单位:勒克斯lx)
因为我们从BH1750寄存器读出来的是2个字节的数据,先接收的是高8位[15:8],后接收的是低8位[7:0],所以我们需要先把这2个字节合成一个数,然后乘上分辨率,再除以1.2即可得到光照值。
例如:我们读出来的第1个字节是0x12(0001 0010),第2个字节是0x53(0101 0011),那么合并之后就是0x1253(0001 0010 0101 0011),换算成十进制也就是4691,乘上分辨率(我用的分辨率是1),再除以1.2,最后等于3909.17 lx。

四、BH1750的命令

BH1750所有的命令都在图4。这次我用的是中文版的图,方便大家看,有点糊勿怪。 这里的指令虽然多,但是实际上如果仅仅是测光照值,只用两条就够了,通电指令和测量指令。这里的几条测量指令我就不详细说了,手册上是有讲的,如果后面你们需要的话我再补上吧。寄存器也只有一个,没什么好说的。(才不是因为懒也不是因为天天都要加班)

在这里插入图片描述

五、BH1750编程教学

下面的编程我以stm32为例,其实换成51,stm8或者别的单片机,程序也基本一样的,不同的单片机在程序上只是引脚配置的写法不太一样,别的基本没差别。
我的这个程序是用OLED显示光照强度的,想用串口,蓝牙或者别的方式也可以。
注:我下面展示的程序跟我发给你们的工程会有一点不一样,主要是备注,因为为了让你们更好理解,我展示的代码是加了很多备注的,而工程是以前的,备注会少一点。

1、IIC驱动代码

//IIC的驱动程序没必要自己去写,能够看懂每一个函数的作用,知道IIC的通讯过程即可,我这里用的是正点原子的例程
//IIC通讯最基本的几个函数是:起始信号,结束信号,发送应答(或不应答),发送1个字节数据,接收1个字节数据
//这些我前面都有讲到,如果你前面看懂了,将上面OPT3001的时序图和这个程序结合起来看你就很容易想明白

//闲话(可以跳过):这一份程序是以前大学做项目的时候写的,其实大部分都是抄的,当时对程序的理解也是一知半解
//现在回头看,发现这个程序的兼容性很差
//虽然在这个工程上面运行是没有问题的,但是如果移植到别的工程或者用别的单片机,需要改动的地方就很多了
//比如引脚的拉高拉低,这里是直接写“SDA=1;”,但是这个SDA的定义是在正点原子自己写的一个库里面的
//如果你用别的工程,没有把这个库加进来,那么这个定义就不成立了
//最好的写法我觉得是分成两个定义SDA_High和SDA_Low
//然后在头文件声明#define SDA_High GPIO_SetBits(GPIOB,GPIO_Pin_0) 
//#define SDA_GPIO_ResetBits(GPIOB,GPIO_Pin_0) 
//这样写的好处是如果要移植,只需要把 GPIO_SetBits(GPIOB,GPIO_Pin_0) 这部分换掉就行了
//比如用51,我们就可以把GPIO_SetBits(GPIOB,GPIO_Pin_0)换成P1_0=1
//同样的IIC通讯的延时函数delay_us,这里用的是定时器,也是要用到正点原子的库delay.c
//其实这里我觉得可以用for函数延时,因为延时的时间比较短,也不需要很精确
//如果换了一个单片机,晶振频率不同,只需要改一下for函数延时的次数
//然后用示波器量一下这个时间,确保是在正常通讯的时间范围内即可

/***起始信号***/
void BH1750_Start()
{
  SDA=1;                    //拉高数据线
  SCL=1;                   //拉高时钟线
  delay_us(5);                 //延时
  GPIO_ResetBits(bh1750_PORT, sda);                    //产生下降沿
  delay_us(5);                 //延时
  GPIO_ResetBits(bh1750_PORT, scl);                    //拉低时钟线
}

/*****停止信号******/
void BH1750_Stop()
{
    SDA=0;                   //拉低数据线
    SCL=1;                      //拉高时钟线
    delay_us(5);                 //延时
    GPIO_SetBits(bh1750_PORT, sda);                    //产生上升沿
    delay_us(5);                 //延时
}

/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void BH1750_SendACK(int ack)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Pin = sda;
  GPIO_Init(bh1750_PORT, &GPIO_InitStruct);  
	
	if(ack == 1)   //写应答信号
		SDA=1; 
	else if(ack == 0)
		SDA=0; 
	else
		return;			
  SCL=1;     //拉高时钟线
  delay_us(5);                 //延时
  SCL=0;      //拉低时钟线
  delay_us(5);                //延时
}

/**************************************
接收应答信号
**************************************/
int BH1750_RecvACK()
{
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;  /*这里一定要设成输入上拉,否则不能读出数据*/
  GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Pin=sda;
  GPIO_Init(bh1750_PORT,&GPIO_InitStruct);
	
  SCL=1;            //拉高时钟线
  delay_us(5);               //延时	
  if(GPIO_ReadInputDataBit(GPIOA,sda)==1)//读应答信号
    mcy = 1 ;  
  else
    mcy = 0 ;				
  SCL=0;                    //拉低时钟线
  delay_us(5);                 //延时
  GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
  GPIO_Init(bh1750_PORT,&GPIO_InitStruct);
  return mcy;
}

/**************************************
向IIC总线发送一个字节数据
**************************************/
void BH1750_SendByte(uchar dat)//dat是要发送的一个字节的数据
{
  uchar i;
  for (i=0; i<8; i++)         //8位计数器
  {
	if( 0X80 & dat )         	//如果要发送的是1
      GPIO_SetBits(bh1750_PORT,sda);
    else                        //如果要发送的是0    
      GPIO_ResetBits(bh1750_PORT,sda);
	dat <<= 1;      //for循环每执行一次,要发送的数据左移1位,循环8次就把一个字节的数据发送出去了
    SCL=1;               //拉高时钟线
    delay_us(5);             //延时
    SCL=0;                //拉低时钟线
    delay_us(5);            //延时
  }
  BH1750_RecvACK();
}

/**************************************
在IIC总线接收一个字节数据
**************************************/
uchar BH1750_RecvByte()
{
  uchar i;
  uchar dat = 0;  //dat是存放接收到的一个字节的数据
  uchar bit;
	
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;   /*这里一定要设成输入上拉,否则不能读出数据*/
  GPIO_InitStruct.GPIO_Pin = sda;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(bh1750_PORT,&GPIO_InitStruct );
	
  GPIO_SetBits(bh1750_PORT,sda);          //使能内部上拉,准备读取数据,
  for (i=0; i<8; i++)         //8位计数器
  {
    dat <<= 1;       //循环8次,每次接收一个位,8次之后完成一个字节数据的接收
    SCL=1;               //拉高时钟线
    delay_us(5);             //延时
			
	if( SET == GPIO_ReadInputDataBit(bh1750_PORT,sda))//读取SDA引脚的电平,如果是高电平,就是传输“1”
      bit = 0X01;
    else                     //电平传输的是“0”
      bit = 0x00;  
	dat |= bit;           //读数据    
	SCL=0;                //拉低时钟线
    delay_us(5);          //延时
  }		
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(bh1750_PORT, &GPIO_InitStruct );
  return dat;
}

2、BH1750写入和读取的函数

//上面讲了IIC的几个基本的函数,包括了发送1字节和接收1字节
//但是和BH1750通讯,不仅仅是发送1个字节或者接收1个字节那么简单
//我们对BH1750发送命令的时候,是要先发送器件地址+写入位,然后发送指令
//读取数据的时候,需要先发送器件地址+读取位,然后连续读取2个字节
//如果我上面说的你都懂了,那么你就可以去看代码了,如果能跟时序图一一对应上,你就理解代码了

//另外,如果你用的不是BH1750,而是别的IIC通讯的芯片,这两个函数的写法可能也是不同的
//比如OPT3001,发送命令的时候不仅发发送命令数据还需要发送寄存器地址,所以一般函数定义的时候就要定义两个变量
//又或者一些命令是两个字节的,那么你定义的变量类型就需要注意了,函数里面也需要多发送一个字节数据
//这里不理解也无所谓,不影响学习BH1750的驱动,以后你做项目用到了别的芯片你可能就突然理解了

//写入指令
void Single_Write_BH1750(uchar REG_Address)//REG_Address是要写入的指令
{
  BH1750_Start();                  //起始信号
  BH1750_SendByte(SlaveAddress);   //发送设备地址+写信号
  BH1750_SendByte(REG_Address);    //写入指令
  BH1750_Stop();                   //发送停止信号
}

//读取指令
void mread(void)
{   
  uchar i;	
  BH1750_Start();                          //起始信号
  BH1750_SendByte(SlaveAddress+1);         //发送设备地址+读信号

  //注意:这里的for函数的i<2和下面的if函数的i==2,我发现以前的工程写的居然是3
  //这里其实我们只需要读取2个字节就行了,后面的合成数据也是只用了BUF的前2个字节
  //工程文件我没改,这个驱动程序以前也用在了多个项目上,读取3个字节肯定是也可以正常运行的
  //但是我觉得还是改成2比较好,你们可以测试一下改成2有没有问题,测试之后一定要告诉我结果,谢谢!!
  for (i=0; i<2; i++)                      //连续读取2个数据,存储到BUF里面
  {
    BUF[i] = BH1750_RecvByte();          //BUF[0]存储高8位,BUF[1]存储低8位
    if (i == 1)
    {
      BH1750_SendACK(1);                //最后一个数据需要回NOACK
    }
    else
    {		
      BH1750_SendACK(0);                //回应ACK
    }
  }
  BH1750_Stop();                          //停止信号
  delay_ms(5);
}

3、BH1750初始化函数

//初始化BH1750,根据需要请参考pdf进行修改****
void Init_BH1750()
{
  GPIO_InitTypeDef GPIO_InitStruct;
  /*开启GPIOB的外设时钟*/ 
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); 
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Pin = sda | scl ;
  GPIO_Init(bh1750_PORT,&GPIO_InitStruct); 
	
  Single_Write_BH1750(0x01);  
  delay_ms(180);            //延时180ms
}

4、获取光照度函数

float read_BH1750(void)
{
  int dis_data;                       //变量	
  float temp1;
  float temp2;
  Single_Write_BH1750(0x01);   //发送上电命令(0x01)
  Single_Write_BH1750(0x10);   //发送高分辨率连续测量命令(0x10)
  delay_ms(200); //等待测量结束,其实延时180ms就行了,延时200ms只是预留多一点时间,保证通讯万无一失
  mread();       //连续读出数据,存储在BUF中
  dis_data=BUF[0];
  dis_data=(dis_data<<8)+BUF[1]; //2个字节合成数据 
  temp1=dis_data/1.2;//计算光照度
  temp2=10*dis_data/1.2;//把光照度放大10倍,目的是把小数点后一位数据也提取出来	
  temp2=(int)temp2%10;//求余得到小数点后一位
  OLED_ShowString(87,2,".",12); //OLED显示小数点
  OLED_ShowNum(94,2,temp2,1,12);//OLED显示小数	
  return temp1;//返回整数部分
}
//这里写的程序还是有点乱的,小数部分直接在read_BH1750()显示,整数部分返回,在main()函数调用的时候显示
//这...其实最好是要么都在这个函数显示,要么把temp1和temp2改成全局变量,然后都在main函数显示
//这个变量的名字也是[捂脸],算了算了,往事不堪回首。要吐槽的地方有点多,也没时间去一一改了
//不过其实也不影响你们学IIC通讯的编程方式,就这样吧

5、main函数

int main(void)
{ 
  float light;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
  delay_init();	    	 //延时函数初始化	  
  uart_init(9600);	 	//串口初始化为9600
  LED_Init();				//初始化与LED连接的硬件接口
  Init_BH1750();       //初始化BH1750
  OLED_Init();     //初始化OLED
  OLED_Clear();     //清屏
	
  while(1)
  {
    	light=read_BH1750();  //读取BH1750的光强数据
		OLED_ShowString(0,2,"light:",12);  //显示光照强度
		OLED_ShowNum(48,2,light,6,12);	//显示光照度
		OLED_ShowString(110,2,"lx",12); //显示“lx”
		if(light<100)//光照度小于100lx,点亮LED灯
		{
			LED1=0;
			OLED_ShowString(38,5,"LED-ON ",12);
		}
		else
		{
			LED1=1;
			OLED_ShowString(38,5,"LED-OFF",12);
		}
  }		
}	

六、测试

我这个程序是大学的时候做的一个课程设计,现在也没有实物可以测试了,我就发我以前报告里的图给你们看一下效果吧。图5是亮度大于100lx的时候,图6是低于100lx的时候。
在这里插入图片描述在这里插入图片描述

七、总结

要驱动BH1750,或者其他IIC通讯的芯片,最好还是先了解IIC通讯的时序,了解通讯的原理,然后才是写驱动程序。驱动程序也可以分成三部分,第一部分是IIC通讯基本的协议(一般抄就完事了),第二部分是芯片的读写过程,需要根据实际芯片的通讯方式写。第三部分是指令控制相关的函数,BH175比较简单,只有测量和计算。有些可能还有多个设置不同的模式的函数,校验数据的函数等等,不过它们其实都是发送指令,只是发不同的指令执行不同的操作而已。
OPT3001的驱动教程你们可以大概看一下:https://blog.csdn.net/ShenZhen_zixian/article/details/102876443

最后再说点闲话吧,写到这里刚好有点感触,其实写博文的初衷只是为了把工作中总结出来的一些经验记录下来,加强记忆。因为像我们这种做硬件研发的,经验是最值钱的,然后上传资源也只是为了赚点积分,因为还在大学那会想下载别人的程序的时候总是恨自己没有积分。后来发现我写的博文和上传的资源能够帮助到一些人,所以我就一直坚持着更新,哪怕每天加班也会抽空写一下文章写一下代码,即使我自己赚的积分其实一次都没用过。可能这就是传承吧,以前遇到问题的时候总能在前辈的博文中找到答案,现在轮到自己分享自己的经验给后来者了,希望我的文章也能够帮助到你。

本文用到的工程源码可以在下面的链接下载:
源码下载链接1:https://pan.baidu.com/s/1HnedCg3sC4HU8iEOf4dYOw ,提取码:xs8o
源码下载链接2:https://pan.baidu.com/s/1QOC01P5M99LzP4i1Voro6g,提取码:abcd

除了本文这个驱动外,还有另外一种使用方法,可以参考我的博文:
基于stm32驱动bh1750光照传感器的一种超简单的编程方法

创作不易,希望你们尊重别人的劳动,点赞+关注支持一下吧,谢谢大家了,博主也会继续更新更多的大学生专栏,如果你们还有什么问题,可以评论留言或者私信给我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BH1750光照传感器超详细攻略(从原理到代码讲解,看完你就懂了) 的相关文章

  • 胶囊网络(Capsule Network)的TensorFlow实现

    现在我们都知道Geoffrey Hinton的胶囊网络 xff08 Capsule Network xff09 震动了整个人工智能领域 xff0c 它将卷积神经网络 xff08 CNN xff09 的极限推到一个新的水平 网上已经有很多的帖
  • 【laravel5.1】Blade模板继承简要使用

    模板继承什么用 xff1f 自然是增强基础页面的复用 xff0c 有利于页面文档的条理 xff0c 也便于更改多处使用的内容 xff0c 如页头 页脚 1 用法概要 64 include 39 common header 39 包含子视图
  • PLC梯形图编程入门

    梯形图 xff08 LAD xff09 是PLC编程的最佳可视化语言 xff0c 它看起来非常类似于继电器电路图 xff0c 因此如果 你对继电器控制和电子电路有所了解的话 xff0c 那么学起来会非常容易 xff01 在这个教程中 xff
  • 软件开发入门自学指南

    每天都看到很多对编程感兴趣的人在问是不是可以自学软件开发 xff0c 或者应该怎么自学编程才能入门 在这篇文章里 xff0c 我将尝试重现一个初学者在学习计算机编程时可能会碰到的问题 xff0c 并尽量提供相应的解决思路 xff0c 希望对
  • 回望2014

    时光荏苒 xff0c 流光飞逝 xff0c 一转眼的时间又是一年 回望一下2014年 xff0c 这一年应该是成长的一年 xff0c 是温暖的一年 xff0c 也是丰收的一年 在这过去的一年里 xff0c 大概可以从工作和生活两方面说说吧
  • 2011,我的IT我的梦

    代码不过十万行 xff0c 别娶媳妇别买房 xff0c 这句诙谐的语言 xff0c 形象的描述了IT行业对程序员的最基本IT素质的要求 xff0c 很荣幸的是 xff0c 我在大学时代就完成了这个基本的任务 xff0c 因此我人生的第一份I
  • LitePal编译出错Error:Unsupport type 'dbname'解决方法

    使用LitePal前 xff0c 需要先配置一下第三方库 xff0c 我们可以通过两种方式来配置 xff1a 1 通过官网下载链接 在官方下载地址上下载需要的版本库 xff0c 然后放到工程目录下的libs文件夹中 xff0c 并右键jar
  • select 建立了索引但是不走索引

    create index indexName on A abc 此时select abc from A 不走索引 可用如下方法让搜索走索引 select distinct abc from A a where exists select 1
  • 关于conda install命令出现CondaHTTPError问题

    在安装过程中 xff0c 安装包时报错 xff1a 打开C Users 用户名 condarc文件 xff0c 没有则创建 xff0c 复制如下内容保存 xff08 一行不能多 xff0c 一行不能少 xff09 xff1a channel
  • 【MySQL技术内幕】15-InnoDB存储引擎文件

    之前介绍的文件都是 MySQL数据库本身的文件 和存储引擎无关 除了这些文件外 每个表存储引擎还有其自己独有的文件 本节将具体介绍与 InnoDB存储引擎密切相关的文件 这些文件包括重做日志文件 表空间文件 1 表空间文件 InnoDB采用
  • Linux进程的睡眠和唤醒

    在Linux中 xff0c 仅等待CPU时间 的进程称为就绪进程 xff0c 它们被放置在一个运行队列中 xff0c 一个就绪进程的状态标志位为TASK RUNNING 一旦一个运行中的进程时间片用完 xff0c Linux内核的调度器会剥
  • mkdir命令详解

    mkdir命令来自于英文词组 make directories 的缩写 xff0c 其功能是用来创建目录文件 使用简单 xff0c 但需要注意若要创建的目标目录已经存在 xff0c 则会提示已存在而不继续创建 xff0c 不覆盖已有文件 而
  • 如何在 MySQL 中创建超级用户

    如何在 MySQL 中创建超级用户 xff1f 本指南将引导您完成在 MySQL 中创建新用户并使其成为具有类似于 root 的数据库访问权限的超级用户的步骤 1 首先 xff0c 你必须使用具有CREATE USER权限的root用户登录
  • 统计员工年度,月度的加班和调休统计

    根据需求 xff0c 写了一个统计部门员工年度的加班与调休统计报表 实现结果如下 xff1a 页面代码如下所示 xff1a 64 model IEnumerable lt lvElecCenter Areas HR Models Leave
  • 我的2014-前进在梦想的道路上

    离2014的结束还有2天 xff0c 一年终去 xff0c 感触颇多 xff1a 或是振奋 xff0c 或是感动 xff0c 或是美好 停下手头繁忙的工作 xff0c 静下心来 xff0c 我们一起 来 盘点这一年的工作和生活 这一年你是否
  • 云计算知识3:弹性计算云EC2的基本架构

    弹性计算云EC2主要特性 灵活性 xff1a EC2允许用户对运行实例类型 数量自行配置 xff0c 还可以选择实例运行的地理位置 xff0c 根据用户的需求随时改变实例的使用数量 低成本 xff1a EC2使得企业不必为暂时的业务增长而购
  • 如何将一个网络分为两个子网、如何通过已知IP和子网掩码计算其同一网段的主机IP

    IPV4的IP地址格式通常表示为xxx xxx xxx xxx xff0c 其中xxx为十进制数 xff0c 取值范围是 0 xff0c 255 xff0c 若用16进制表示则为xx xx xx xx xff0c 其中xx的取值范围是 0
  • Unity3D 人称设置(第一人称视角、第三人称视角)

    设置第一人称视角 1 把物体的坐标和摄像机的坐标设置成一样的 xff0c 这样摄像机就在物体内部 xff0c 就是第一人称的视角 2 把摄像机拖拽进物体对象内 xff0c 摄像机变成物体的子对象 xff0c 这样摄像机就能跟着物体一起移动
  • Unity3D 射击游戏练习实例

    知识点 xff1a 刚体组件 xff08 rigidbody xff09 xff0c 受力和碰撞的组件触发器 xff08 Trigger xff09 xff0c 开启后物体碰撞效果取消 xff0c 但仍会返回碰撞消息复制物体对象 xff1a
  • Cocos2dx 环境搭建

    Cocos2dx 环境搭建 准备软件和工具包 xff0c 参考 xff1a Cocos2dx 入门学习准备安装visual studio 安装Python2 7 xff08 直接默认下一步就可以 xff0c 要注意Python安装的路径 x

随机推荐

  • 缺失MSVCR相关文件怎么办

    根据系统的提示 xff0c 看缺失的是哪个文件 xff0c 正常是msvcr100 120 dll在百度上搜索对应文件下载把下载好的msvcr文件 xff0c 放到 C Windows SysWOW64 目录下正常msvcr文件会缺失好几个
  • Cocos2dx 源码解释

    程序入口 AppDelegate AppDelegate在AppDelegate h中定义的 AppDelegate h中的AppDelegate类 xff1a AppDelegate类下的applicationDidFinishLaunc
  • Visual Studio 2019(VS2019) 基本操作

    卸载 加载项目 1 卸载项目 xff1a 不删除项目代码 xff0c 但是停止对该项目的一切使用和调用 xff08 好处是保留代码 xff09 2 加载项目 xff1a 重新加载已停用的项目 xff0c 可以继续使用和调用 修改VS主题风格
  • Elasticsearch 中文分词&多词搜索&权重

    目录 中文分词器 一 安装中文分词器ik 二 使用中文分词器 多词搜索 权重 中文分词器 一 安装中文分词器ik 源码地址 xff1a https github com medcl elasticsearch analysis ik 根据提
  • C# do while循环结构

    注意 循环结构一共有三种 xff1a while循环 https blog csdn net shenqiankk article details 96299600do while循环for循环 https blog csdn net sh
  • C# for循环结构

    注意 循环结构一共有三种 xff1a while循环 https blog csdn net shenqiankk article details 96299600do while循环 https blog csdn net shenqia
  • C# 构造方法(函数)

    构造方法的作用 构造方法用来创建对象 xff0c 并且在构造方法中对对象进行初始化 构造方法的特殊性 没有返回值 xff0c 不需要写类型 xff0c 连void都不要写 构造方法的方法名 xff0c 与类名要相同 构造方法结构 publi
  • GDI+ 绘图方法

    GDI绘直线步骤 创建GDI对象 xff1a Graphics g 61 this CreateGraphics 创建画笔对象 xff1a Pen pen 61 new Pen Brushes Red 创建两个点 xff1a Point p
  • C# 连接MySQL数据库

    C 引用MySQL步骤 xff1a 下载mysql data dll xff1a http soft onlinedown net soft 618668 htm将文件放在项目目录下在VS2019项目内 xff0c 引用mysql data
  • Navicat of MySQL连接和使用

    请先安装MySQL服务 MySQL数据库安装 xff1a https blog csdn net shenqiankk article details 99756531 新建连接 如果出现Can t connect to MySQL ser
  • Mysql创建数据库字符集的选择

    转载 xff1a https blog csdn net JingChC article details 82908686 字符集选择 xff1a 在国内正常都是用 UTF 8 排序选择 xff1a 排序一般分为两种 xff1a utf b
  • Linux进程状态解析 之 R、S、D、T、Z、X (主要有三个状态)

    linux是一个多用户 xff0c 多任务的系统 xff0c 可以同时运行多个用户的多个程序 xff0c 就必然会产生很多的进程 xff0c 而每个进程会有不同的状态 Linux进程状态 xff1a R TASK RUNNING xff0c
  • python装饰器(详解)

    1 什么是装饰器 器指的是工具 xff0c 可以定义成成函数 装饰指的是为其他事物添加额外的东西点缀 合到一起的解释 xff1a 装饰器指的定义一个函数 xff0c 该函数是用来为其他函数添加额外的功能 就是拓展原来函数功能的一种函数 2
  • linux基础---常用命令学习

    1 显示日期的指令 xff1a date Linux时钟分为系统时钟 xff08 System Clock xff09 和硬件 xff08 Real Time Clock xff0c 简称RTC xff09 时钟 系统时钟是指当前Linux
  • JdbcTemplate queryForObject Incorrect result size: expected 1, actual 0

    使用Spring中的jdbcTemplate 时 xff0c 通过id查询不到结果时返回Incorrect result size expected 1 actual 0 64 Override public lt T gt T query
  • Ubuntu usb wifi驱动安装(MT7601u芯片)

    软件环境 Ubuntu 14 04 硬件环境 芯片 xff1a MT7601u 确定芯片 xff0c 在电脑插上usb wifi xff0c 在ubuntu命令行 xff0c 输入lsusb命令 xff0c 在所列的列表中有MT7601u即
  • 【目标检测】Fast RCNN算法详解

    Girshick Ross Fast r cnn Proceedings of the IEEE International Conference on Computer Vision 2015 继2014年的RCNN之后 xff0c Ro
  • 【目标检测】RCNN算法详解

    Girshick Ross et al Rich feature hierarchies for accurate object detection and semantic segmentation Proceedings of the
  • SHTC3的研发经历

    SHTC3自学笔记 一 SHTC3介绍 SHTC3是一款低功耗温湿度传感器 xff0c 温度的采集范围为 40 125 xff0c 湿度的采集范围为 0 100 通讯方式是I2C xff0c 引脚定义如图1所示 I2C的器件地址只有一个 x
  • BH1750光照传感器超详细攻略(从原理到代码讲解,看完你就懂了)

    目录 一 前言二 芯片介绍三 IIC通讯介绍IIC通讯过程简介IIC通讯实例BH1750的通讯过程 四 BH1750的命令五 BH1750编程教学六 测试七 总结 一 前言 之所以写这篇文章 xff0c 原因有两个 一是 xff1a 有个师