Contributed by WangChong, organized by Ai-Thinker
[Peripheral Porting] Ai-WB2 + BH1750 Light Intensity Sensor
本帖最后由 WangChong 于 2024-9-3 02:52 编辑
本帖最后由 WangChong 于 2024-9-3 02:19 编辑
本帖最后由 WangChong 于 2024-9-3 01:56 编辑
本帖最后由 WangChong 于 2024-9-3 01:56 编辑
本帖最后由 WangChong 于 2024-9-3 01:56 编辑
本帖最后由 WangChong 于 2024-9-3 01:55 编辑
Peripheral Introduction
BH1750 is a digital light intensity sensor based on photodiodes. It measures the ambient light intensity and converts it into a digital signal. It communicates over the I2C interface and is suitable for various light intensity detection applications, such as automatic display brightness adjustment and light-sensing switches.
Peripheral Specifications
Click to expand full code
**工作电压**: 2.4V 至 3.6V
**测量范围**: 1 至 65535 lux
**分辨率**:High resolution mode (1 lx)
Low resolution mode (4 lx)
Communication interface: I2C
I2C address: 0x23 (default), 0x5C (optional), selected according to whether the Address pin is pulled low or high
Measurement time:
- High resolution mode: 120ms - 180ms
- Low resolution mode: 16ms - 24 ms
- Standby mode: 0.1 µA (typical)
Power consumption:
Measurement mode: 0.12 mA (typical)
Porting Process
According to the datasheet, the BH1750 communicates over I2C, and since we are using the WB2, we need to confirm the IIC interface pins of the WB2 from the schematic. Since I am using the WB2-12F development board, the corresponding schematic can be found under the WB2 section of the Ai-Thinker community. Meanwhile, according to the Bouffalo Lab official GPIO function multiplexing, you can find which pins support IIC.

A complete implementation is already provided under application/iot-solution/demo_bh1750 in the SDK. Let's try changing PIN3 (SDA) to PIN17 (SDA) in the IIC configuration, and connect pin 12 and pin 17 of the WB2-12F devkit to the SCL and SDA of the BH1750 respectively for flashing and testing.


Flash and Verify

At this point, you can correctly view the light intensity read by the WB2-12F from the BH1750 through the serial port assistant.
Code Analysis
Click to expand full code
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <hosal_i2c.h>
#include <bl_gpio.h>
#include <blog.h>
// BH1750 的默认 I2C 地址
#define BH1750_DEFAULT_ADDR BH1750_ADDR_L
#define BH1750_ADDR_H 0x5c // BH1750 高地址
#define BH1750_ADDR_L 0x23 // BH1750 低地址
// BH1750 的各种操作命令
#define BH1750_POWER_DOWN 0x00 // 断电命令
#define BH1750_POWER_ON 0x01 // 开机命令
#define BH1750_RESET 0x07 // 重置命令
#define BH1750_CONTINUOUS_H_MODE 0x10 // 高分辨率模式,持续测量
#define BH1750_CONTINUOUS_H_MODE2 0x11 // 高分辨率模式2,持续测量
#define BH1750_CONTINUOUS_L_MODE 0x13 // 低分辨率模式,持续测量
#define BH1750_ONETIME_H_MODE 0x20 // 高分辨率模式,一次性测量
#define BH1750_ONETIME_H_MODE2 0x21 // 高分辨率模式2,一次性测量
#define BH1750_ONETIME_L_MODE 0x23 // 低分辨率模式,一次性测量
int main(void)
{
// 定义并初始化 I2C 设备 i2c0
static hosal_i2c_dev_t i2c0 = {
.config = {
.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT, // 7位地址模式
.freq = 100000, // I2C 通信频率为 100kHz
.mode = HOSAL_I2C_MODE_MASTER, // I2C 主机模式
.scl = 12, // 时钟引脚 GPIO 12
.sda = 17, // 数据引脚 GPIO 17
},
.port = 0, // I2C 端口号
};
// 初始化 I2C 接口
hosal_i2c_init(&i2c0);
for (;;) {
uint8_t buffer[2]; // 用于接收光照强度数据的缓冲区
uint8_t cmd = BH1750_ONETIME_H_MODE; // 设置 BH1750 的测量模式为高分辨率一次性测量
hosal_i2c_master_send(&i2c0, BH1750_DEFAULT_ADDR, &cmd, 1, HOSAL_WAIT_FOREVER); // 发送测量命令到 BH1750
// 接收来自 BH1750 的测量结果(2字节)
int ret = hosal_i2c_master_recv(&i2c0, BH1750_DEFAULT_ADDR, buffer, 2, 100);
if (ret) {
// 如果接收超时,重新发送上电命令并记录错误日志
cmd = BH1750_POWER_ON;
hosal_i2c_master_send(&i2c0, BH1750_DEFAULT_ADDR, &cmd, 1, 100);
blog_error("i2c timeout\r\n");
} else {
// 将接收到的两个字节数据合并为一个16位整数
uint16_t result = buffer[0];
result <<= 8; // 左移8位,放置高8位
result |= buffer[1]; // 组合低8位
// 将测量值转换为光照强度(lux)
float luxlevel = result;
result /= 1.2f;
// 输出光照强度日志
blog_info("lux level: %.02f\r\n", luxlevel);
}
// 任务延时 1000 毫秒,即每隔 1 秒执行一次测量
vTaskDelay(portTICK_RATE_MS * 1000);
}
return 0;
}Library Functions
Let's briefly organize the library functions above.
bh1750.c
Click to expand full code
#include "bh1750.h"
#include <blog.h>
#include <FreeRTOS.h>
#include <task.h>
static hosal_i2c_dev_t i2c0 = {
.config = {
.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT,
.freq = 100000,
.mode = HOSAL_I2C_MODE_MASTER,
.scl = 12,
.sda = 17,
},
.port = 0,
};
void bh1750_init()
{
blog_info("Initializing I2C...\n");
hosal_i2c_init(&i2c0);
}
void read_bh1750(void *args)
{
for (;;)
{
uint8_t buffer[2];
uint8_t cmd = BH1750_ONETIME_H_MODE;
int ret;
blog_info("Sending command to BH1750...\n");
ret = hosal_i2c_master_send(&i2c0, BH1750_DEFAULT_ADDR, &cmd, 1, HOSAL_WAIT_FOREVER);
if (ret != 0)
{
blog_error("I2C send failed with error: %d\n", ret);
continue;
}
blog_info("Receiving data from BH1750...\n");
ret = hosal_i2c_master_recv(&i2c0, BH1750_DEFAULT_ADDR, buffer, 2, 100);
if (ret != 0)
{
cmd = BH1750_POWER_ON;
hosal_i2c_master_send(&i2c0, BH1750_DEFAULT_ADDR, &cmd, 1, 100);
blog_error("I2C receive failed with error: %d\n", ret);
}
else
{
uint16_t result = (buffer[0] << 8) | buffer[1];
float luxlevel = result / 1.2f;
blog_info("Lux level: %.02f\n", luxlevel);
}
vTaskDelay(pdMS_TO_TICKS(1000)); // 使用宏将毫秒转换为FreeRTOS时间单位
}
}bh1750.h
Click to expand full code
#ifndef BH1750_H
#define BH1750_H
#include <hosal_i2c.h>
// BH1750 的默认 I2C 地址
#define BH1750_DEFAULT_ADDR BH1750_ADDR_L
#define BH1750_ADDR_H 0x5c // BH1750 高地址
#define BH1750_ADDR_L 0x23 // BH1750 低地址
// BH1750 的各种操作命令
#define BH1750_POWER_DOWN 0x00 // 断电命令
#define BH1750_POWER_ON 0x01 // 开机命令
#define BH1750_RESET 0x07 // 重置命令
#define BH1750_CONTINUOUS_H_MODE 0x10 // 高分辨率模式,持续测量
#define BH1750_CONTINUOUS_H_MODE2 0x11 // 高分辨率模式2,持续测量
#define BH1750_CONTINUOUS_L_MODE 0x13 // 低分辨率模式,持续测量
#define BH1750_ONETIME_H_MODE 0x20 // 高分辨率模式,一次性测量
#define BH1750_ONETIME_H_MODE2 0x21 // 高分辨率模式2,一次性测量
#define BH1750_ONETIME_L_MODE 0x23 // 低分辨率模式,一次性测量
// 函数声明
void bh1750_init();
void read_bh1750(void *args);
#endif // BH1750_Hmain.c
Click to expand full code
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <hosal_i2c.h>
#include <bl_gpio.h>
#include <blog.h>
#include "bh1750.h"
int main(void)
{
bh1750_init();
xTaskCreate(read_bh1750, "BH1750 Task", 4096, NULL, 10, NULL);
return 0;
}Notes
Remember: do not start the task scheduler in the main function. The main function is referenced with extern elsewhere, acting as a transit intermediate method that can be used to initialize tasks.

