STM32与BH1750光照传感器通过IIC总线通讯实现OLED光照显示程序分享
先看硬件接线(原理图其实就几条线):BH1750和0.96寸OLED都是I2C设备,直接挂在同一组I2C总线上就行。刚玩STM32那会儿总想搞点有意思的传感器联动,最近用BH1750光照传感器配了个OLED屏,实测效果还不错。STM32和BH1750光照传感器和IIC总线通讯OLED显示程序源码,通过BH1750,光照传感器采集光照信息,通过oled显示光照值。STM32和BH1750光照传感器和
STM32和BH1750光照传感器和IIC总线通讯OLED显示程序源码,通过BH1750,光照传感器采集光照信息,通过oled显示光照值。 包括程序源码和原理图,程序源码注释详细需要的可以看下
刚玩STM32那会儿总想搞点有意思的传感器联动,最近用BH1750光照传感器配了个OLED屏,实测效果还不错。今天就带大家手搓这个光照监测小装置,从硬件连接到代码实现都会讲到,文末有完整工程自取。
先看硬件接线(原理图其实就几条线):BH1750和0.96寸OLED都是I2C设备,直接挂在同一组I2C总线上就行。STM32F103的PB6接SCL,PB7接SDA,记得给两个设备都加上上拉电阻(4.7K就行)。这里有个坑:BH1750的地址默认是0x23,如果遇到设备不响应记得用逻辑分析仪抓包确认。

上代码!先看I2C初始化的骚操作:
// 硬件I2C初始化
void I2C_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// PB6-SCL, PB7-SDA 配置为开漏输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 重点!复用开漏
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0xAA; // 随便填个不冲突的地址
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000; // 400KHz够用了
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
}
这段配置最容易被忽略的是GPIO模式——必须用开漏!之前用推挽输出结果I2C总线直接卡死,折腾了半天才发现是这里的问题。
STM32和BH1750光照传感器和IIC总线通讯OLED显示程序源码,通过BH1750,光照传感器采集光照信息,通过oled显示光照值。 包括程序源码和原理图,程序源码注释详细需要的可以看下
BH1750的驱动代码有个小技巧,上电后要发个唤醒指令:
void BH1750_PowerOn(void)
{
I2C_Start();
I2C_Send_Byte(0x23 << 1); // 器件地址+写操作
I2C_Wait_Ack();
I2C_Send_Byte(0x01); // POWER ON
I2C_Wait_Ack();
I2C_Stop();
DelayMs(180); // 等待传感器稳定,实测不能少于180ms
}
注意这里用的是0x23左移1位,因为I2C协议里地址位最后一位表示读写方向。连续模式下的数据读取更简单:
float BH1750_Read(void)
{
uint8_t buf[2];
I2C_Start();
I2C_Send_Byte((0x23 << 1) | 0x01); // 读模式
I2C_Wait_Ack();
buf[0] = I2C_Read_Byte(1); // 带ACK读取
buf[1] = I2C_Read_Byte(0); // 最后一个字节NACK
I2C_Stop();
uint16_t val = (buf[0]<<8) | buf[1];
return val / 1.2; // 根据手册转换公式
}
这里有个精度问题:BH1750原始数据单位是lux/1.2,所以最终要除以1.2。实测在室内灯光下数值在200-500lux之间,阳光下能到上万。

OLED显示部分用到了u8g2库,重点在数值刷新策略:
void OLED_Refresh(float lux)
{
char str[16];
sprintf(str, "Lux: %.1f", lux);
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
u8g2_DrawStr(&u8g2, 5, 30, str);
u8g2_SendBuffer(&u8g2);
// 低功耗时可设置局部刷新
if(lux > 1000)
u8g2_SetContrast(&u8g2, 255); // 强光下提高亮度
else
u8g2_SetContrast(&u8g2, 120);
}
为了省电加了自动亮度调节——当光照足够时降低OLED背光。注意sprintf浮点数会占用较多资源,在资源紧张的单片机上可以考虑用整型运算。
主循环里控制采样频率很重要:
while(1)
{
static uint32_t last = 0;
if(HAL_GetTick() - last > 500) // 500ms采样一次
{
float lux = BH1750_Read();
OLED_Refresh(lux);
last = HAL_GetTick();
// 超过20000lux时闪屏警告
if(lux > 20000) OLED_Blink(3);
}
__WFI(); // 进入休眠省电
}
这里用到了WFI指令让CPU在空闲时休眠,实测整机电流从8mA降到了3mA左右。遇到强光照时的闪屏提示是个实用小功能,防止传感器过曝。
最后说几个踩过的坑:
- I2C上拉电阻不接或阻值太大会导致波形畸变
- BH1750的测量模式要选连续H分辨率模式(0x10)
- OLED的I2C地址可能因厂商不同有变化(常用0x3C或0x3D)
- 光照值突变时建议做滑动平均滤波
完整工程里包含了自适应I2C扫描、异常重连机制,实测在STM32F103C8T6上稳定运行。下次考虑加上光强阈值报警功能,用蜂鸣器做个智能光控装置应该挺有意思。

更多推荐



所有评论(0)