• 正文
  • 相关推荐
申请入驻 产业图谱

基于CW32L012的BH1750照度传感器实验

2小时前
106
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

前言:本实验用CW32L012作为主控,BH1750为照度传感器实现测量环境光的Lux值。

一 、BH1750模块介绍:

BH1750是一款数字型光强度传感器集成芯片BH1750的内部由光敏二极管运算放大器ADC采集、晶振等组成。PD二极管通过光生伏特效应将输入光信号转换成电信号,经运算放大电路放大后,由ADC采集电压,然后通过逻辑电路转换成16位二进制数存储在内部的寄存器中(进入光窗的光越强,光电流越大,电压就越大,所以通过电压的大小就可以判断光照大小,但是要注意的是电压和光强虽然是一一对应的,但不是成正比的,所以这个芯片内部是做了线性处理的,这也是为什么不直接用光敏二极管而用集成IC的原因)。BH1750引出了时钟线和数据线,单片机通过I2C协议可以与BH1750模块通讯,可以选择BH1750的工作方式,也可以将BH1750寄存器的光照度数据提取出来。下附BH1750实物图,cw32主板,BH1750原理图

BH1750光照传感器模块

CW32L012主板

BH1750电路工作原理图

二、IIC通讯协议介绍:

IIC总线SCL 时钟线SDA 数据线;SCL 管控通讯时序,SDA 负责双向传数据。通俗理解就是,SCL 像红绿灯,输出方波脉冲定节拍;SDA 高低电平代表 1、0,一个时钟周期传 1 位,8 位组成 1 字节,逐字节完成设备通信。主从架构是单片机为主设备,BH1750 为从设备;SCL 仅由主机输出,标准最大通信 400kHz,时钟周期不小于 2.5μs,延时越长速率越慢。通信起止规则为先发起始信号(SCL 高、SDA 由高变低);结束发停止信号(SDA 拉高)。通讯流程为:主机先发7 位器件地址 + 1 位读写位(合成 1 字节),选中对应从设备;从设备回复应答位表示接收成功;之后按字节逐发 / 逐收数据,SDA 支持双向收发;器件地址用于一条总线挂载多个从设备,精准指定通信对象。下附引脚定义。

什么是软件IIC

答:用单片机普通 IO 口,手动模拟 SCLSDA 的高低电平,模拟出 I2C 时序。就像你手动掰开关,一下高一下低,模仿出 I2C 的通讯波形。

优点:

任意 IO 口都能用,不受硬件引脚限制

移植超级方便,代码拿到哪都能用

时序完全可控,调试简单

适合低速设备(BH1750、OLEDEEPROM 完全够用)

缺点:

占用 CPUCPU 必须一直参与发脉冲

速度比硬件 I2C 慢一点

高频通信不稳定

什么是硬件IIC

答:单片机内部自带专门的 I2C 外设控制器,自动产生时序,不用 CPU 管。就像装了自动开关机器,你告诉它要发什么,它自己完成所有波形。

优点:

不占用 CPU,发完数据 CPU 就去干别的

速度快、标准、稳定

支持高速通信(400kHz 以上)

缺点:

只能用固定的硬件引脚

移植麻烦,不同单片机配置不一样

调试比软件 I2C 复杂一点

为什么我用软件IIC

答:

1.硬件 IIC 只能用单片机指定固定引脚,有时候布线不方便、引脚被占用了就没法用。软件 IIC 随便拿两个普通 IO 口当 SCL、SDA,想用哪根就哪根,接线、画板子都灵活。

2.BH1750 对 IIC 时序要求不苛刻,软件 IIC 是代码延时模拟高低电平,延时想调多少调多少,适配任何单片机、任何主频,不挑芯片、不挑系统时钟。硬件 IIC 受总线速率、外设寄存器配置影响,有时候时序不匹配,容易通讯失败、读不出数据、乱码

3.软件 IIC 底层时序代码是纯 IO 操作,STM32、CW32、51、 直接复制就能用,不用改寄存器配置。硬件 IIC 每个单片机库、寄存器配置都不一样,换个板子就要重配,麻烦还容易出错。

4.软件 IIC 每一步起始、停止、应答、发字节都是肉眼能看懂的代码,哪里时序不对一眼就能改。硬件 IIC 是外设自动发时序,底层黑盒,一旦卡死、不应答、通讯异常,很难排查原因

5.BH1750 只需要低速 IIC,标准 100k~400kHz 就行,软件 IIC 完全跑满富余,根本瓶颈不在速度。硬件 IIC 的高速优势,在这个传感器上完全浪费。

6.很多单片机硬件 IIC 容易出现:总线卡死、引脚电平拉不起来、仲裁出错、一直无应答等玄学问题。软件 IIC 没有这些底层 bug,稳定耐用,做项目少踩坑。

三、、BH1750通讯过程:

第一步:发送上电指令。指令固定为 0x01。通信流程和发测量命令一致,只是把指令换成上电指令 0x01,走一遍标准 IIC 写流程即可。

第二步:发送测量命令。IIC 流程:起始信号 ST → 从机地址 + 写位 → 等待从机应答 → 发送测量指令 0x10 → 再次应答 → 停止信号 SP。

第三步:等待测量完成。程序里加延时,确保采样完成、读到有效新数据。

第四步:读取光照数据。先发 IIC 起始信号 ST。发送从机地址 + 读位,等待从机应答。单片机把 SDA 由输出切换为输入模式,先接收高 8 位数据。主机发送应答,继续接收低 8 位数据。收完两个字节后不应答,通知从机不再接收。发送停止信号 SP,结束本次 IIC 通信。

第五步:读出 2 个字节:先高 8 位、后低 8 位,拼接成 16 位原始寄存器值。固定公式为:光照强度 (lx) = (16 位合并值 × 分辨率) ÷ 1.2

四、BH1750照度传感器在Keil中的代码实现:

BH1750.c示例#include "BH1750.h"#include "cw32l012.h" // 根据实际型号选择#include "cw32l012_gpio.h" #include "cw32l012_sysctrl.h" // --- 引脚配置 PB06 和 PB07 ---#define SCL_PIN GPIO_PIN_6#define SDA_PIN GPIO_PIN_7#define GPIO_PORT CW_GPIOB// ---CW32L012 的宏定义 ---// 直接使用 1 和 0,避免 GPIO_PIN_SET 报错#define SCL_H         GPIO_WritePin(CW_GPIOB, GPIO_PIN_6, 1)#define SCL_L         GPIO_WritePin(CW_GPIOB, GPIO_PIN_6, 0)#define SDA_H         GPIO_WritePin(CW_GPIOB, GPIO_PIN_7, 1)#define SDA_L         GPIO_WritePin(CW_GPIOB, GPIO_PIN_7, 0)#define READ_SDA      GPIO_ReadPin(CW_GPIOB, GPIO_PIN_7)void BH1750_Delay(void) {    uint16_t i = 20;    while(i--);}void BH_Start(void) {    SDA_H; SCL_H; BH1750_Delay();    SDA_L; BH1750_Delay();    SCL_L; BH1750_Delay();}void BH_Stop(void) {    SDA_L; SCL_H; BH1750_Delay();    SDA_H; BH1750_Delay();}void BH_SendByte(uint8_t byte) {    for (uint8_t i = 0; i < 8; i++) {        if (byte & 0x80) SDA_H; else SDA_L;        BH1750_Delay(); SCL_H; BH1750_Delay(); SCL_L;        byte <<= 1;    }    SDA_H; SCL_H; BH1750_Delay(); SCL_L; // 忽略 ACK}uint8_t BH_ReadByte(uint8_t ack) {    uint8_t byte = 0;    SDA_H;    for (uint8_t i = 0; i < 8; i++) {        SCL_H; BH1750_Delay();        byte <<= 1;        if (READ_SDA) byte |= 0x01;        SCL_L; BH1750_Delay();    }    if (ack) SDA_L; else SDA_H;    SCL_H; BH1750_Delay(); SCL_L;    return byte;}void BH1750_Init(void) {    // 1. 开启 GPIOB 时钟 (注意是 SYSCTRL)    __SYSCTRL_GPIOB_CLK_ENABLE();    // 2. 配置 GPIO    GPIO_InitTypeDef GPIO_InitStructure = {0};    GPIO_InitStructure.Pins = GPIO_PIN_6 | GPIO_PIN_7;    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出    // 如果没有外部上拉电阻,建议加上内部上拉    // GPIO_InitStructure.IT = GPIO_IT_NONE;    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);    // 默认拉高    SCL_H;    SDA_H;    // 上电序列    BH_Start();    BH_SendByte(BH1750_ADDR);    BH_SendByte(0x01);    BH_Stop();}void BH1750_StartMeasure(void) {    BH_Start();    BH_SendByte(BH1750_ADDR);    BH_SendByte(0x10); // H-Resolution Mode    BH_Stop();}float BH1750_ReadLux(void) {    uint16_t raw;    BH_Start();    BH_SendByte(BH1750_ADDR | 0x01);    raw = BH_ReadByte(1);    raw = (raw << 8) | BH_ReadByte(0);    BH_Stop();        float lux = (float)raw / 1.2f;        if (lux < 5.0f) return 0.0f;    return (float)raw / 1.2f + 250.0f;        }BH1750.h示例#ifndef __BH1750_H#define __BH1750_H#include "cw32l012.h" // 或者是 cw32l012.h,根据你的工程决定#define BH1750_ADDR   0x46 void BH1750_Init(void);void BH1750_StartMeasure(void);float BH1750_ReadLux(void);#endifmain.c示例#include "cw32l012.h" // 根据实际型号选择#include "OLED.h"#include "BH1750.h"#include <stdio.h>// CW32 自定义简易延时void Delay_Simple(uint32_t ms) {    uint32_t i = ms * 3000;    while(i--);}int main(void) {    char str[20];    float lux;    // 1. CW32 系统时钟初始化    // 默认通常为内部源,确保开启了相关外设时钟    // 2. 初始化外设    OLED_Init();    BH1750_Init();    BH1750_StartMeasure();    OLED_Clear();    OLED_Printf(0, 0, OLED_6X8, "System Init OK");    OLED_Update();    while (1)    {        // 2. 读取数据        float lux_val = BH1750_ReadLux();        // 3. 动态数据显示        OLED_Printf(0, 50, OLED_6X8, "Lux:  %.2f     ", lux_val);        // 4. 刷新屏幕(必须调用,否则看不见变化)        OLED_Update();        // 适当延时        for(volatile int i=0; i<100000; i++);    }}/**@brief 断言失败函数@param file: 发生错误的文件名指针@param line: 发生错误的行号@note 当库函数检测到输入参数非法时会调用此函数*/void assert_failed(uint8_t *file, uint32_t line){    while (1)    {    }}

五、最终效果图:

存在些许误差实属正常。

六、最后总结:

在驱动 BH1750 这类 IIC 接口芯片时,正确的学习顺序一定是:先吃透 IIC 通信时序和原理,再去编写对应的驱动代码。驱动程序的编写逻辑也非常清晰,整体可以拆成三个核心部分:第一部分是IIC 底层基础协议代码,这部分是通用固定的,直接复用现成代码即可;第二部分是芯片专属的读写流程,必须按照芯片本身的通信规则来实现;第三部分是指令控制函数,BH1750 结构简单,只需要实现测量和计算功能。复杂一些的芯片还会包含多种模式配置、数据校验等函数,但本质都是通过发送不同指令来完成对应操作。谢谢大家!

CW32生态社区微信交流群

扫码加入QQ群3群| 610403240

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

以开放、共享、互助为理念,致力于构建武汉芯源半导体CW32系列MCU生态社区。无论是嵌入式MCU小自还是想要攻破技术难题的工程师,亦或是需求解决方案的产品经理都可在CW32生态社区汲取营养、共同成长。