STM32和BH1750光照传感器和IIC总线通讯OLED显示程序源码通过BH1750,光照传感器采集光照信息通过oled显示光照值。 包括程序源码和原理图程序源码注释详细需要的可以看下刚玩STM32那会儿总想搞点有意思的传感器联动最近用BH1750光照传感器配了个OLED屏实测效果还不错。今天就带大家手搓这个光照监测小装置从硬件连接到代码实现都会讲到文末有完整工程自取。先看硬件接线原理图其实就几条线BH1750和0.96寸OLED都是I2C设备直接挂在同一组I2C总线上就行。STM32F103的PB6接SCLPB7接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分辨率模式0x10OLED的I2C地址可能因厂商不同有变化常用0x3C或0x3D光照值突变时建议做滑动平均滤波完整工程里包含了自适应I2C扫描、异常重连机制实测在STM32F103C8T6上稳定运行。下次考虑加上光强阈值报警功能用蜂鸣器做个智能光控装置应该挺有意思。