本文主要是尝试在NodeMCU(硬件)上,使用RTOS(固件、OS)来控制bh1750光照传感器,
采集亮度数据。
关于RTOS固件的编译环境和编译方式的介绍,可参照之前的文章。
ESP8266固件的编译5(RTOS SDK固件)
但是在RTOS固件下,只内置了很少的传感器驱动,bh1750并没有被支持。
需要自己根据I2C协议实现简单的驱动。
(nodeMCU的lua固件预置了相当多的、也包含bh1750的传感器驱动,
作为参考,可参照lua版本的驱动实现 nodemcu-firmware/lua_modules)
bh1750通信通过I2C协议,关于I2C协议本身,可以参照如下文章,有清晰的解释。
http://blog.chinaunix.net/uid-25906157-id-3585430.html
Esp8266没有硬件I2C接口,是用GPIO软件模拟的,坏处是性能差,好在模拟也不复杂。
RTOS固件开发环境下提供了一个Driver库,内置I2C、SPI、UART等通信实现。
第一步自然是编译这个库。
编译过程中,出现一些问题,应该是维护人员的粗心所致。要如下方法修改:
进入ESP8266_RTOS_SDK\driver_lib\driver目录,如下修正i2c_master.c文件。
1) 增加头文件
#include "c_types.h"
#include "esp8266/ets_sys.h"
#include "esp8266/eagle_soc.h" /* 新增加头文件 */
#include "esp8266/gpio_register.h" /* 新增加头文件 */
#include "gpio.h"
2) 修改宏/函数名
在i2c_master_gpio_init函数中,将
ETS_GPIO_INTR_DISABLE() ;
ETS_GPIO_INTR_ENABLE() ;
修改为
ETS_INTR_LOCK();
ETS_INTR_UNLOCK();
3)默认的SDA和SCL管脚定义
默认的I2C的SDA和SCL管脚在i2c_master.h中定义,其默认值为
SDA -- GPIO2
SCL -- GPIO4
需要变更的话,可修改此文件。
4)编译
在ESP8266_RTOS_SDK\driver_lib目录下,执行
$ make_lib.sh driver
成功编译成功后,在ESP8266_RTOS_SDK/lib目录下会生成新的libdriver.a。
执行如下命令,能找到i2c对应函数的符号信息。
$nm –A libdriver.a | grep i2c
仿照lua版,只实现了【连续 H 分辨率模式】下的数据读取。
Bh1750的I2C接口的说明:
A)Slave地址有 2 种形式,由 ADDR 端口决定。
ADDR=“H” ( ADDR ≧ 0.7VCC ) →“1011100”
ADDR=“L” ( ADDR ≦ 0.3VCC ) →“0100011”
B)bh1750的I2C数据读取协议,如图
C)简单实现如下(错误处理等省略)
uint8 addr = 0x23; // bh1750 i2c地址(low) 00100011
uint8 cmd = 0x10; // bh1750 高分辨率模式 00010000
void init_bh1750()
{
// 初始化i2c总线
i2c_master_gpio_init();
}
uint16 read_bh1750_data(void)
{
uint8 b1 = 0x00;
uint8 b2 = 0x00;
uint32 data = 0x00;
// 设置高分辨率模式
i2c_master_start();
i2c_master_writeByte( (addr << 1) );
i2c_master_getAck();
i2c_master_writeByte(cmd);
i2c_master_getAck();
i2c_master_stop();
// 采集等待时间要求120ms以上
os_delay_us(60000);
os_delay_us(60000);
os_delay_us(60000);
// 读取数据
i2c_master_start();
i2c_master_writeByte( (addr << 1) | 0x01 );
i2c_master_getAck();
b1 = i2c_master_readByte();
i2c_master_send_ack();
b2 = i2c_master_readByte();
i2c_master_send_ack();
i2c_master_stop();
// 计算结果(取得亮度数据为2字节、除1.2的阈值)
data = b1 * 256 + b2;
return (uint16)(data / 1.2);
}
修改ESP8266_RTOS_SDK\examples\project_template\user\ user_main.c文件
(上面的bh1750驱动实现也可放在此文件中)
因为RTOS不允许在user_init中进行while(1)循环和睡眠等待,
因此用定时器来定时读取lux数据(也可以创建task)。代码如下:
LOCAL void ICACHE_FLASH_ATTR print_data(void* data)
{
printf("lux: %d\n", read_bh1750_data());
}
void user_init(void)
{
int i = 0;
// 设置串口为 115200 波特率。不设的情况,默认波特率为78400
UART_ConfigTypeDef uart_config;
uart_config.baud_rate = BIT_RATE_115200;
uart_config.data_bits = UART_WordLength_8b;
uart_config.parity = USART_Parity_None;
uart_config.stop_bits = USART_StopBits_1;
uart_config.flow_ctrl = USART_HardwareFlowControl_None;
uart_config.UART_RxFlowThresh = 120;
uart_config.UART_InverseMask = UART_None_Inverse;
UART_ParamConfig(UART0, &uart_config);
printf("----------------------\n");
printf("bh1750 reader\n");
printf("----------------------\n");
// 初始化I2C
init_bh1750();
// 设置定时器
os_timer_disarm(&timer);
os_timer_setfn(&timer, (os_timer_func_t *)print_data, NULL);
os_timer_arm(&timer, 2000, 1);
}
进入ESP8266_RTOS_SDK\examples\project_template目录,执行
make COMPILE=gcc BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=DIO SPI_SIZE_MAP=4
如编译成功,则在BIN_PATH下会生成固件。
1)接线
esp8266默认的GPIO定义和nodemcu的D管脚定义是不同的体系,使用nodemcu硬件是要注意。
直接用esp8266模块的话,根据gpio号接线就可以。
接线如下:
SDA(bh1750) ---- D4(nodeMCU) ---- GPIO2(esp8266)
SCL(bh1750) ---- D2(nodeMCU) ---- GPIO4(esp8266)
VCC(bh1750) ---- 3.3v
GND(bh1750) ---- GND(nodeMCU)
ADDR(bh1750) ---- GND (使用bh1750的0x23地址)
接线效果图
2)固件下载
可参照以前的文章
ESP8266固件的编译6(固件的下载)
3) 效果
用串口工具115200波特率连接nodeMCU硬件,
可以看到bh1750的数据被正常采集,且亮度能随场景变化而不同。