基于小熊派光强传感器BH1750实践(multi_timer+状态机工程应用)

简介: 基于小熊派光强传感器BH1750实践(multi_timer+状态机工程应用)

本实践案例基于小熊派开发板:

640.png

实践光强传感器的开发,我们需要带上一个扩展模块:E53_SC1,如下图所示,最终连接的效果:

640.png

再来看看这个拓展板以及主板上对应的硬件接口,后面我们才能够去配置相应的硬件管脚,达到驱动使用的目的:

640.png

转接板E53_SC1在主板上的电路原理图:

640.png

640.png

BH1750光强传感器简介

BH1750是一种用于两线式串行总线接口的数字型光强度传感器集成电路。这种集成电路可以根据收集的光线强度数据来调整液晶或者键盘背景的的亮度,利用它的高分辨率可以探测较大范围的光强度变化。(1lx-65535lx)


点:

640.png

要控制这个传感器,当然要了解传感器支持的协议,以及一些指令,这是一个基于I2C接口的传感器。


I2C是(Inter-Integrated Circuit)的英文缩写,是Philips公司开发的一个通信协议,只有两根线(SDA/SCL)是用来通信的。

640.png

BH1750支持的命令:

640.png

BH1750从机地址计算:

640.png

根据文档提示,我们了解到光强传感器的从机地址是0100011


当主机向从机发送写命令时为:


0100011(从机地址:7位) 0(写数据位:1位) ===> 0x46


当主机向从机发送读命令时为:


0100011(从机地址:7位) 1(写数据位:1位) ===> 0x47

工程实践:stm32cubMx

1、芯片选型,这里选择stm32l431rctx

640.png

640.png

2、配置rcc时钟以及串行调试接口

640.png

640.png

640.png

3、配置调试LED

640.png

4、配置串口调试

方便根据调试信息查看程序执行流程(默认即可)。

640.png

5、配置I2C1(PC6/PC7位于I2C1)

默认即可

640.png

6、生成Keil5基础工程

640.png

实际开发建议硬件外设分模块,这样看起来不要把所有的生成全部都挤到main.c里面去了,这点让我非常讨厌,所以生成工程时候习惯点击设置以下这一项:

640.png

接下来点击生成代码:

640.png

640.png

640.png

工程实践:编写代码

1、将multi_timer添加到keil5工程

640.png

2、创建一个Package目录,将multi_timer的程序文件添加进来

640.png

3、编写代码

由于篇幅限制,只看我自己代码添加的位置:


usart.h


添加重定向打印

/* USER CODE BEGIN 1 */
int fputc(int ch, FILE *file)
{
    return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
}
/* USER CODE END 1 */

stm32l4xx_it.c


添加multi_timer计数

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
    timer_ticks();
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  /* USER CODE END SysTick_IRQn 1 */
}

main.h


添加相应的头文件

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "multi_timer.h"
/* USER CODE END Includes */

main.c


在程序中宏定义相应的值

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/*接收光强超时值*/
#define TIMEOUT 200
/*光强值检测阈值*/
#define LIGHT_SENSOR_THREHOLD 30
/*mode:模式选择*/
//连续高分辨率模式精度 1 lux
#define LUX_1_MODE   0x10
//连续高分辨率模式2精度 0.5 lux
#define LUX_0_5_MODE 0x11
//低分辨率模式
#define LUX_LOW_MODE 0x13
#define WRITE_ADDRESS 0x46    //0100 011  0
#define READ_ADDRESS  0x47    //0100 011  1
/* USER CODE END PD */

光强采集结构体

typedef struct light_sensor
{
    /*光强值*/
    int     Lux ;
    /*multi_timer定时器句柄*/
    Timer     timer1 ;
    /*定时器计数值*/
    uint16_t  Timer_Count ;
    /*是否采集完成标志*/
    uint8_t   Conver_completed ;
    /*定时回调*/
    void (*timeout_cb)(void);
} light_sensor_TypeDef;
light_sensor_TypeDef lsensor ;

定时回调函数以及结构体初始化

/*定时器回调函数*/
void timer1_callback(void)
{
    ++lsensor.Timer_Count;
}
/*初始化参数*/
void Init_BH750(void)
{
    lsensor.Lux = 0 ;
    lsensor.Timer_Count = 0 ;
    lsensor.Conver_completed = 0 ;
    lsensor.timeout_cb = timer1_callback ;
}

这里的读光强没有用小熊派例程里直接延时然后读取的方法,小熊派的读光强函数是这么写的:

640.png

而我是这么写的:

/*读取光强*/
void ReadBH1750(uint8_t mode)
{
    float lux = 0;
    uint8_t ReadData[2] = {0};
    static uint8_t Sensor_Status = 0 ;
    /*读取光强流程*/
    switch(Sensor_Status)
    {
        /*1、发送检测光强模式的指令*/
        case 0:
            if(HAL_OK != HAL_I2C_Master_Transmit(&hi2c1, WRITE_ADDRESS, (uint8_t *)&mode, 1, 0xff))
                return  ;
            /*切换为读地址状态*/
            Sensor_Status = 1 ;
            lsensor.Timer_Count = 0 ;
            lsensor.Conver_completed = 0 ;
            break ;
        case 1:
            /*
              2、发送命令后延时200ms等待读取
              定时200ms,判断是否已经到了
              这里相当于取代了延时等待,不占用CPU
            */
            if(TIMEOUT == lsensor.Timer_Count)
            {
                lsensor.Timer_Count = 0 ;
                //3、开始读取光强,发送读光强指令
                if(HAL_OK == HAL_I2C_Master_Receive(&hi2c1, READ_ADDRESS, ReadData, 2, 0xff))
                {
                    lux=(float)((ReadData[0]<<8)|ReadData[1]);
        lux=(double)lux/1.2;
                    lsensor.Lux = (int)lux ;
                    /*4、转换完成*/
                    lsensor.Conver_completed = 1 ;
        Sensor_Status = 0 ; /*切换为写地址状态*/
                }
            }
            break ;
        default:
            break;
    }
}

这里利用了switch case+multi_timer产生一个200ms的定时计数,通过状态1流程判断计数器是否到达设定值,从而达到延时的效果,这段程序在主程序的while循环中几乎不会占用CPU,非常高效!


主程序逻辑:

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* USER CODE BEGIN 1 */
    /* USER CODE END 1 */
    /* MCU Configuration--------------------------------------------------------*/
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
    /* USER CODE BEGIN Init */
    /* USER CODE END Init */
    /* Configure the system clock */
    SystemClock_Config();
    /* USER CODE BEGIN SysInit */
    /* USER CODE END SysInit */
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    /* USER CODE BEGIN 2 */
    /*串口初始化后加这个延时,防止后面的printf打印乱码*/
    HAL_Delay(200);
    printf("光强读取测试实验\n");
    Init_BH750();
    timer_init(&lsensor.timer1, lsensor.timeout_cb, 1, 1);
    timer_start(&lsensor.timer1);
    /* USER CODE END 2 */
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */
        /* USER CODE BEGIN 3 */
        //采用连续高分辨率模式精度 1 lux
        ReadBH1750(LUX_1_MODE);
        if(1 == lsensor.Conver_completed) /*如果转换完了才会执行*/
        {
            printf("当前光强值:%d\n", lsensor.Lux);
            //当光强值大于设定的光强值阈值,则关灯,否则开灯
            if(lsensor.Lux > LIGHT_SENSOR_THREHOLD)
            {
                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
            }
            else
            {
                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
            }
        }
        timer_loop();
    }
    /* USER CODE END 3 */
}

运行结果:

640.png

640.png

状态机思想前变万化,学会灵活应用则一定能够写出非常高效,简单易维护的程序。

实际例程下载:

链接:https://pan.baidu.com/s/1GA29ua5yVfvGFfRCcS52LA
提取码:afe5
复制这段内容后打开百度网盘手机App,操作更方便哦


往期精彩

网红物联网开发板小熊派使用评测


RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)


开源按键组件MultiButton支持菜单操作(事件驱动型)


超轻量级网红软件定时器multi_timer(51+stm32双平台实战)

目录
相关文章
|
存储 监控 C语言
西门子S7-1200编程实例,关断延迟定时器指令如何使用?
在西门子S7-1200中有四种类型的定时器:TON接通延迟定时器、TONR保持型接通延迟定时器、TOF关断延迟定时器、TP脉冲定时器。
西门子S7-1200编程实例,关断延迟定时器指令如何使用?
|
4月前
|
传感器
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
stm32f407探索者开发板(二十二)——通用定时器基本原理讲解
351 0
|
存储 芯片 异构计算
快速入门数字芯片设计,UCSD ECE111(三)System Verilog时序逻辑(上)
快速入门数字芯片设计,UCSD ECE111(三)System Verilog时序逻辑
103 0
|
存储 芯片
快速入门数字芯片设计,UCSD ECE111(三)System Verilog时序逻辑(下)
快速入门数字芯片设计,UCSD ECE111(三)System Verilog时序逻辑(下)
97 0
|
前端开发 芯片
【芯片前端】保持代码手感——同步FIFO
【芯片前端】保持代码手感——同步FIFO
|
传感器 物联网 芯片
超轻量级网红软件定时器multi_timer(51+stm32双平台实战)
超轻量级网红软件定时器multi_timer(51+stm32双平台实战)
419 0
|
传感器 物联网
基于小熊派光强传感器BH1750状态机驱动项目再度升级(带上位机曲线显示)
基于小熊派光强传感器BH1750状态机驱动项目再度升级(带上位机曲线显示)
84 0
|
传感器 数据采集 物联网
基于小熊派光强传感器BH1750状态机驱动项目升级(带LCD屏显示)
基于小熊派光强传感器BH1750状态机驱动项目升级(带LCD屏显示)
96 0
基于正点原子阿波罗 STM32F429 上手RT-Thread
基于正点原子阿波罗 STM32F429 上手RT-Thread