yinwuqing 发表于 6 天前

【瑞萨RA-eco-RA6M4评测】BH1730环境光传感共IIC驱动

本帖最后由 yinwuqing 于 2026-8-16 00:36 编辑

   上期咱们分享了NSHT30温湿传感器在FreeRTOS中的实时高效采集,今天在原工程的基础上,增加对BH1730环境光传感的驱动。使其能够实时感应周围环境光照强度,进而可以实现一些感光类的智能设备应用开发。
      同样的,首先咱们直接上正常日光下实物连接状态示意图。


      关于BH1730环境光传感器,官方提供的参考资料文档如下:


       在扩展板部分提供的电路设计原理图发现与上期分享的NSHT30温湿传感器共用IIC接口。


    因此无需再使用FSP去使能其它硬件接口,笔者这里直接使用同一个线程任务去读取BH1730的光照强度。硬件接口的连接关系与上期一样,无需再变动。



       直接在new_thread2线程中添加关于BH1730环境光传感器相应的驱动接口,即可实现的周围环境光的强弱进行感应。相关代码如下:
new_thread2_entry.c

#include "new_thread2.h"
#include "lcd_init.h"
#include "lcd.h"
#include "nsht30.h"
#include "bh1730.h"

FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER

fsp_err_t err;
int timeout_ms = 10000;
i2c_master_event_t i2c_event;

/* I2C callback function */
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    if (NULL != p_args) {
      i2c_event = p_args->event;
    }
}

/* New Thread entry function */
/* pvParameters contains TaskHandle_t */
void new_thread2_entry(void * pvParameters)
{
                FSP_PARAMETER_NOT_USED(pvParameters);
      
                NSHT30_Init();
                BH1730_Init();
                BH1730_Config(BH1730_GAIN_X1, BH1730_ITIME_DEFAULT);
               
                /* TODO: add your own code here */
                while(1)
                {
                         if (NSHT30_ReadData() == 1) {
                                        LCD_ShowFloatNum1(88,74,nsht30_temperature,4,RED,WHITE,12);
                                        LCD_ShowFloatNum1(88,94,nsht30_humidity,4,RED,WHITE,12);
                           } else {
                                        LCD_ShowString(84,74,(uint8_t *)"GetFail",RED,WHITE,12,0);
                                        LCD_ShowString(84,94,(uint8_t *)"GetFail",RED,WHITE,12,0);
                            }bh1730.h

#ifndef BH1730_H
#define BH1730_H

#include <stdint.h>

/* I2C 地址 */
#define BH1730_ADDR             0x29

/* 命令寄存器标志 (必须与地址或命令进行或运算) */
#define BH1730_CMD            0x80

/* 特殊命令 */
#define BH1730_CMD_SPECIAL      0x60
#define BH1730_CMD_SOFT_RESET   0x04

/* 普通寄存器地址 (使用时必须 |= BH1730_CMD) */
#define BH1730_REG_CONTROL      0x00
#define BH1730_REG_GAIN         0x07
#define BH1730_REG_TIMING       0x01
#define BH1730_REG_PART_ID      0x12
#define BH1730_REG_DATA0_LOW    0x14
#define BH1730_REG_DATA0_HIGH   0x15
#define BH1730_REG_DATA1_LOW    0x16
#define BH1730_REG_DATA1_HIGH   0x17

/* CONTROL 寄存器位定义 */
#define BH1730_CTRL_POWER       0x01
#define BH1730_CTRL_ADC_EN      0x02
#define BH1730_CTRL_ONE_TIME    0x08
#define BH1730_CTRL_ADC_VALID   0x10

/* 增益 */
#define BH1730_GAIN_X1          0x00
#define BH1730_GAIN_X2          0x01
#define BH1730_GAIN_X64         0x02
#define BH1730_GAIN_X128      0x03

/* 积分时间 (ITIME) 值 (测量时间 = (256 - ITIME) * 2.7ms) */
#define BH1730_ITIME_DEFAULT    0xDA   // 102.6ms
#define BH1730_ITIME_LONG       0x01   // 688.5ms

/* 外部变量 */
extern float bh1730_lux;

/* 函数声明 */
void BH1730_Init(void);
uint8_t BH1730_ReadID(uint8_t *id);
void BH1730_Config(uint8_t gain, uint8_t itime);
float BH1730_ReadLight(void);

#endifbh1730.c

#include "bh1730.h"
#include "hal_data.h"
#include <string.h>

extern int timeout_ms;
extern i2c_master_event_t i2c_event;

float bh1730_lux = 0.0f;

/* 内部平均次数 */
#define AVERAGE_TIMES5

/* ========== 底层I2C操作 ========== */

/**
* @brief 写命令(单个字节,自动或上 BH1730_CMD)
*/
static uint8_t BH1730_WriteCmd(uint8_t cmd)
{
    uint8_t buf = cmd | BH1730_CMD;   // 必须置最高位
    i2c_event = I2C_MASTER_EVENT_ABORTED;

    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, BH1730_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    R_SCI_I2C_Write(&g_i2c2_ctrl, &buf, 1, false);

    timeout_ms = 10000;
    while ((i2c_event != I2C_MASTER_EVENT_TX_COMPLETE) && (timeout_ms > 0)) {
      R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
      timeout_ms--;
    }
    if (i2c_event == I2C_MASTER_EVENT_ABORTED || timeout_ms == 0) {
      i2c_event = I2C_MASTER_EVENT_ABORTED;
      return 0;
    }
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    return 1;
}

/**
* @brief 写寄存器(两字节:寄存器地址|CMD,数据)
*/
static uint8_t BH1730_WriteReg(uint8_t reg, uint8_t data)
{
    uint8_t buf;
    buf = reg | BH1730_CMD;   // 高位置1
    buf = data;

    i2c_event = I2C_MASTER_EVENT_ABORTED;
    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, BH1730_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    R_SCI_I2C_Write(&g_i2c2_ctrl, buf, 2, false);

    timeout_ms = 10000;
    while ((i2c_event != I2C_MASTER_EVENT_TX_COMPLETE) && (timeout_ms > 0)) {
      R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
      timeout_ms--;
    }
    if (i2c_event == I2C_MASTER_EVENT_ABORTED || timeout_ms == 0) {
      i2c_event = I2C_MASTER_EVENT_ABORTED;
      return 0;
    }
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    return 1;
}

/**
* @brief 读取指定寄存器(单字节或双字节)
* @param reg    寄存器地址 (函数内部或上BH1730_CMD)
* @param len    读取长度 (1或2)
* @param buf    输出缓冲区
*/
static uint8_t BH1730_ReadReg(uint8_t reg, uint8_t len, uint8_t *buf)
{
    /* 1. 写命令:设置要读取的寄存器地址 (reg | BH1730_CMD) */
    uint8_t cmd = reg | BH1730_CMD;
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, BH1730_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    R_SCI_I2C_Write(&g_i2c2_ctrl, &cmd, 1, false);

    timeout_ms = 10000;
    while ((i2c_event != I2C_MASTER_EVENT_TX_COMPLETE) && (timeout_ms > 0)) {
      R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
      timeout_ms--;
    }
    if (i2c_event == I2C_MASTER_EVENT_ABORTED || timeout_ms == 0) {
      i2c_event = I2C_MASTER_EVENT_ABORTED;
      return 0;
    }
    i2c_event = I2C_MASTER_EVENT_ABORTED;

    /* 2. 读取数据 */
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, BH1730_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    R_SCI_I2C_Read(&g_i2c2_ctrl, buf, len, false);

    timeout_ms = 10000;
    while ((i2c_event != I2C_MASTER_EVENT_RX_COMPLETE) && (timeout_ms > 0)) {
      R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
      timeout_ms--;
    }
    if (i2c_event == I2C_MASTER_EVENT_ABORTED || timeout_ms == 0) {
      i2c_event = I2C_MASTER_EVENT_ABORTED;
      return 0;
    }
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    return 1;
}

void BH1730_Init(void)
{
    R_SCI_I2C_Open(&g_i2c2_ctrl, &g_i2c2_cfg);
    R_BSP_SoftwareDelay(5, BSP_DELAY_UNITS_MILLISECONDS);
}

uint8_t BH1730_ReadID(uint8_t *id)
{
    return BH1730_ReadReg(BH1730_REG_PART_ID, 1, id);
}

void BH1730_Config(uint8_t gain, uint8_t itime)
{
    /* 软件复位:通过特殊命令 */
    BH1730_WriteReg(BH1730_CMD_SPECIAL | BH1730_CMD_SOFT_RESET, 0x80);
    R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);

    /* 设置增益 */
    BH1730_WriteReg(BH1730_REG_GAIN, gain);

    /* 设置积分时间 */
    BH1730_WriteReg(BH1730_REG_TIMING, itime);

    /* 上电并开启ADC (Power On + ADC Enable + One Time 可选,**读取时再触发) */
    BH1730_WriteReg(BH1730_REG_CONTROL, BH1730_CTRL_POWER | BH1730_CTRL_ADC_EN);
    R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
}

/**
* @brief 读取一次光照值(多次平均)
* @return 光照强度 (lux)
*/
float BH1730_ReadLight(void)
{
    uint8_t buf;
    uint16_t data0, data1;
    float lx_total = 0.0f;
    float gain = 1.0f;   // 默认GAIN_1X

    /* 先触发单次测量 (CONTROL = POWER|ADC_EN|ONE_TIME) */
    BH1730_WriteReg(BH1730_REG_CONTROL, BH1730_CTRL_POWER | BH1730_CTRL_ADC_EN | BH1730_CTRL_ONE_TIME);

    /* 等待转换完成 (根据ITIME等待,这里简单固定等待100ms,实际可根据配置动态计算) */
    R_BSP_SoftwareDelay(120, BSP_DELAY_UNITS_MILLISECONDS);

    for (int i = 0; i < AVERAGE_TIMES; i++) {
      /* 读取 DATA0 (可见光) */
      if (!BH1730_ReadReg(BH1730_REG_DATA0_LOW, 2, buf)) {
            return 0.0f;
      }
      data0 = (uint16_t)(buf | (buf << 8));

      /* 读取 DATA1 (红外) */
      if (!BH1730_ReadReg(BH1730_REG_DATA1_LOW, 2, buf)) {
            return 0.0f;
      }
      data1 = (uint16_t)(buf | (buf << 8));

      /* 计算此单次 lux (参考RT1021驱动的分段公式) */
      float lx = 0.0f;
      float div = (data1 == 0) ? 0.0f : ((float)data1 / data0);
      if (div < 0.26f) {
            lx = (1.29f * data0 - 2.733f * data1) / gain * 102.6f / BH1730_ITIME_DEFAULT;
      } else if (div < 0.55f) {
            lx = (0.795f * data0 - 0.859f * data1) / gain * 102.6f / BH1730_ITIME_DEFAULT;
      } else if (div < 1.09f) {
            lx = (0.51f * data0 - 0.345f * data1) / gain * 102.6f / BH1730_ITIME_DEFAULT;
      } else if (div < 2.13f) {
            lx = (0.276f * data0 - 0.13f * data1) / gain * 102.6f / BH1730_ITIME_DEFAULT;
      }
      if (lx < 0) lx = 0;

      lx_total += lx;
      R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
    }

    bh1730_lux = lx_total / AVERAGE_TIMES;
    return bh1730_lux;
}       工程完善好后,将程序更新到开发板中,由于手机拍摄会自动调节聚光,台灯的三档位变化不便于录制LCD屏上亮度值清晰显示,这里笔者使用可移动的小台灯进行演示,演示视频见如下,由此可见,FreeRTOS中可高效显示室内温湿度与光照强度值。
https://www.bilibili.com/video/BV1ynb26jExw/

      整个工程的源码见如下附件:



页: [1]
查看完整版本: 【瑞萨RA-eco-RA6M4评测】BH1730环境光传感共IIC驱动