基于小熊派光强传感器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双平台实战)

目录
相关文章
|
10月前
|
数据采集 缓存 搜索推荐
服务端渲染(SSR)与静态站点生成(SSG)结合使用
服务端渲染(SSR)与静态站点生成(SSG)结合使用
|
11月前
|
数据处理 Python
如何优化Python读取大文件的内存占用与性能
如何优化Python读取大文件的内存占用与性能
670 0
|
定位技术 Android开发 iOS开发
引入百度地图,安卓出现白屏问题
引入百度地图,安卓出现白屏问题
344 57
|
10月前
|
NoSQL 关系型数据库 MySQL
百万数据量优化实战
在现代互联网业务中,处理百万级别的数据量是家常便饭。传统的单体数据库架构在面对如此庞大的数据量时,往往显得力不从心。本文将分享一次实际的优化案例,探讨如何利用MySQL和Redis共同实现百万级数据统计的优化。
592 4
|
存储 分布式计算 大数据
《数据湖的时空穿越:Delta Lake如何用版本控制解锁历史迷雾》
【8月更文挑战第27天】Delta Lake作为一个开源的存储层为Apache Spark及大数据工作流带来了事务性支持与数据版本控制功能。通过将数据表视作一系列不可变的事务日志记录,Delta Lake实现了数据一致性的保障。它支持ACID事务并允许用户追踪和管理数据表的不同版本。利用提供的示例代码可以看到如何对Delta Lake表进行操作、查询特定版本甚至回滚至早期版本。随着数据湖架构的发展,Delta Lake正逐渐成为管理大规模数据集的关键工具。
159 0
|
数据安全/隐私保护
爱问云classin伯索云cctalk学堂录屏翻录提取工具使用方法
近期专注于网课加密视频难题,因播放器检测机制,多数录屏工具无法正常使用或录制为黑屏。为解决此问题,推荐一款专用软件:只需确保视频正常播放,运行软件并点击“一键解除录屏限制”,待提示成功后启用配套录屏工具即可
|
存储 数据采集 数据可视化
基于Python flask+MySQL+echart的电影数据分析可视化系统
该博客文章介绍了一个基于Python Flask框架、MySQL数据库和ECharts库构建的电影数据分析可视化系统,系统功能包括猫眼电影数据的爬取、存储、展示以及电影评价词云图的生成。
675 1
|
缓存 BI
[计算机网络(第八版)]第二章 物理层(学习复习笔记)
[计算机网络(第八版)]第二章 物理层(学习复习笔记)
|
编解码 前端开发 JavaScript
node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)
node实战——koa实现文件下载和图片/pdf/视频预览(node后端储备知识)
533 1
|
SQL
.Net Core EF 日志打印 SQL 语句
.Net Core EF 日志打印 SQL 语句
251 0