在智慧农业、智能家居、环境监测等物联网应用中,光照强度是一个常见且重要的采集参数。通过实时获取光照数据,系统可以自动调节灯光、控制遮阳设备、调节植物补光等,实现智能化控制。本文将详细介绍如何使PIC12F1822 这款8脚单片机,通过IIC总线读取BH1750 光照传感器的数据,并提供完整的代码实现。
一、 BH1750 传感器简介
BH1750是一款数字型环境光强度传感器,采用IIC接口通信,具备以下特点:
- 测量范围:1 ~ 65535 Lux
- 高分辨率:支持0.5 Lux精度
- 内置ADC,直接输出数字信号
- 低功耗,适用于电池供电设备
- 支持多种测量模式(连续/单次、高/低分辨率)
其典型应用电路简洁:

二、硬件连接: PIC12F1822 与 BH1750
PIC12F1822是一款小巧且功能丰富的8位单片机,本例中我们使用软件模拟IIC方式。
连接方式如下:
- BH1750 VCC → PIC +3.3V
- BH1750 GND → PIC GND
- BH1750 SDA → PIC RA0(模拟SDA)
- BH1750 SCL → PIC RA1(模拟SCL)
三、软件实现:
PIC12F1822与BH1750例程如下:
#include<pic12f1822.h>
__CONFIG(0x083c);
__CONFIG(0x3612);
#define uint16 unsigned int
#define uint8 unsigned char
#define fint32 float
#define uint32 unsigned long
#define uchar unsigned char
#define uint unsigned int
//引脚定义
#define B_LUX_SCL0_O TRISA1=0
#define B_LUX_SCL0_H RA1=1
#define B_LUX_SCL0_L RA1=0
#define B_LUX_SDA0_O TRISA0=0
#define B_LUX_SDA0_H RA0=1
#define B_LUX_SDA0_L RA0=0
#define B_LUX_SDA0_I TRISA0=1
#define B_LUX_SDA0_DAT RA0
#define B_LUX_SlaveAddress 0x46 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改
/*-----------------------------函数声明-------------------------------*/
void B_LUX_delay_nms(uint16 k);
void B_LUX_Init(void);
void B_LUX_Single_Write(uint8 REG_Address); //单个写入数据
uint8 B_LUX_Single_Read(uint8 REG_Address); //单个读取内部寄存器数据
void B_LUX_Multiple_read(void); //连续的读取内部寄存器数据
//------------------------------------
void B_LUX_Delay5us(void);
void B_LUX_Delay5ms(void);
void B_LUX_Start(void); //起始信号
void B_LUX_Stop(void); //停止信号
void B_LUX_SendACK(uint8 ack); //应答ACK
uint8 B_LUX_RecvACK(void); //读ack
void B_LUX_SendByte(uint8 dat); //IIC单个字节写
uint8 B_LUX_RecvByte(void); //IIC单个字节读
uint16 B_LUX_GetLux(void);
uint8 BUF_0[8]; //接收数据缓存区
uint16 dis_data_0; //变量
/*---------------------------------------------------------------------
功能描述: 延时纳秒 不同的工作环境,需要调整此函数
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_delay_nms(uint16 k)
{
uint16 i,j;
for(i=0;i<k;i++)
{
for(j=0;j<1760;j++)
{
;
}
}
}
/*---------------------------------------------------------------------
功能描述: 延时5微秒 不同的工作环境,需要调整此函数
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Delay5us()
{
uint8 n = 20;
while (--n);
}
/*---------------------------------------------------------------------
功能描述: 延时5毫秒 不同的工作环境,需要调整此函数
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Delay5ms()
{
uint16 n = 10000;
while (--n);
}
/*---------------------------------------------------------------------
功能描述: 起始信号
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Start()
{
B_LUX_SDA0_H; //拉高数据线
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
B_LUX_SDA0_L; //产生下降沿
B_LUX_Delay5us(); //延时
B_LUX_SCL0_L; //拉低时钟线
}
/*---------------------------------------------------------------------
功能描述: 停止信号
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Stop()
{
B_LUX_SDA0_L; //拉低数据线
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
B_LUX_SDA0_H; //产生上升沿
B_LUX_Delay5us(); //延时
B_LUX_SCL0_L;
B_LUX_Delay5us();
}
/*---------------------------------------------------------------------
功能描述: 发送应答信号
参数说明: ack - 应答信号(0:ACK 1:NAK)
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_SendACK(uint8 ack)
{
if (ack&0x01) B_LUX_SDA0_H; //写应答信号
else B_LUX_SDA0_L;
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
B_LUX_SCL0_L; //拉低时钟线
B_LUX_SDA0_H;
B_LUX_Delay5us(); //延时
}
/*---------------------------------------------------------------------
功能描述: 接收应答信号
参数说明: 无
函数返回: 返回应答信号
---------------------------------------------------------------------*/
uint8 B_LUX_RecvACK()
{
uint8 CY = 0x00;
B_LUX_SDA0_H;
B_LUX_SDA0_I;
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
if(B_LUX_SDA0_DAT)
CY |=1 ; //读应答信号
else CY|=0;
B_LUX_Delay5us(); //延时
B_LUX_SCL0_L; //拉低时钟线
B_LUX_SDA0_O;
return CY;
}
/*---------------------------------------------------------------------
功能描述: 向IIC总线发送一个字节数据
参数说明: dat - 写字节
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_SendByte(uint8 dat)
{
uint8 i;
for (i=0; i<8; i++) //8位计数器
{
if (dat&0x80) B_LUX_SDA0_H;
else B_LUX_SDA0_L; //送数据口
B_LUX_Delay5us(); //延时
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
B_LUX_SCL0_L; //拉低时钟线
B_LUX_Delay5us(); //延时
dat <<= 1; //移出数据的最高位
}
B_LUX_RecvACK();
}
/*---------------------------------------------------------------------
功能描述: 从IIC总线接收一个字节数据
参数说明: 无
函数返回: 接收字节
---------------------------------------------------------------------*/
uint8 B_LUX_RecvByte()
{
uint8 i;
uint8 dat = 0;
B_LUX_SDA0_I;
// B_LUX_SDA0_H; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
B_LUX_SCL0_H; //拉高时钟线
B_LUX_Delay5us(); //延时
if(B_LUX_SDA0_DAT)
dat|=1 ;
else dat|=0;
B_LUX_SCL0_L; //拉低时钟线
B_LUX_Delay5us(); //延时
dat <<= 1;
}
B_LUX_SDA0_O;
return dat;
}
/*---------------------------------------------------------------------
功能描述: 写BH1750
参数说明: REG_Address - 寄存器地址
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Single_Write(uint8 REG_Address)
{
B_LUX_Start(); //起始信号
B_LUX_SendByte(B_LUX_SlaveAddress); //发送设备地址+写信号
B_LUX_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf22页
// BH1750_SendByte(REG_data); //内部寄存器数据,请参考中文pdf22页
B_LUX_Stop(); //发送停止信号
}
/*---------------------------------------------------------------------
功能描述: 连续读出BH1750内部数据
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Multiple_read(void)
{
uint8 i;
B_LUX_Start(); //起始信号
B_LUX_SendByte(B_LUX_SlaveAddress+1); //发送设备地址+读信号
for (i=0; i<3; i++) //连续读取6个地址数据,存储中BUF
{
BUF_0[i] = B_LUX_RecvByte(); //BUF[0]存储0x32地址中的数据
if (i == 3)
{
B_LUX_SendACK(1); //最后一个数据需要回NOACK
}
else
{
B_LUX_SendACK(0); //回应ACK
}
}
B_LUX_Stop(); //停止信号
B_LUX_Delay5ms();
}
/*---------------------------------------------------------------------
功能描述: 初始化光照传感器
参数说明: 无
函数返回: 无
---------------------------------------------------------------------*/
void B_LUX_Init()
{
// P1SEL &= ~(0x48);
B_LUX_SCL0_O;
B_LUX_SDA0_O;
B_LUX_delay_nms(100); //延时100ms
B_LUX_Single_Write(0x01);
}
/*---------------------------------------------------------------------
功能描述: 光照读取函数
参数说明: 无
函数返回: 返回光照值
---------------------------------------------------------------------*/
uint16 B_LUX_GetLux()
{
uint32 temp;
B_LUX_Single_Write(0x01); // power on
B_LUX_Single_Write(0x10); // H- resolution mode
B_LUX_delay_nms(50); //延时180ms
B_LUX_Multiple_read(); //连续读出数据,存储在BUF中
B_LUX_Single_Write(0x00); // power off
dis_data_0=BUF_0[0];
dis_data_0=(dis_data_0<<8)+BUF_0[1];//合成数据,即光照数据
// temp=dis_data_0*10;
// dis_data_0=temp/12;
// return (uint32)(temp);
return dis_data_0;
}
四、扩展建议:更高范围的传感器选择
一般场景65535LX的光照强度就够用了,若项目需要检测更强光照(如室外直射阳光),可选用MAX44009 (1 ~ 188,000 Lux),电路和程序稍做调整就可以,但MAX44009价格很高,差不多是BH1750的10倍多,性价比要差一些,适用于对范围有严格要求的场景。
附:《 PIC 单片机入门实战》 8 篇文章内容来源于我自己画的电路原理图及程序, 若你对 PIC 单片机感兴趣,或正在入门嵌入式开发, 关注我,免费赠送资料(包括原理图、数据手册、各种例程等)。
此外,我也设计了对应的多功能开发板,集成MP1584与HT7333电源方案,适合学习与项目原型开发。欢迎交流合作,共同进步。