ESP8266 开发学习笔记_By_GYC 【BH1750光强传感器】
BH1750是一个光强传感器,能够对环境光强度进行量化,转化为环境中的光强度lux。它是使用IIC总线进行通信,通过读取寄存器来获得传感器的真实数据。我们常说的智能调光,就要用到此类传感器,因为这个传感器的精度还是很不错的,可以使用这个传感器作为反馈,将环境光照度稳定在一个期望的数值。本章主要介绍在 ESP8266 上使用 IIC 总线驱动 BH1750 光强传感器的方法。通过PID稳定环境光强度的内容将在后面介绍。
一、BH1750是什么
BH1750是一个光强传感器,能够对环境光强度进行量化,转化为环境中的光强度lux。它是使用IIC总线进行通信,通过读取寄存器来获得传感器的真实数据。我们常说的智能调光,就要用到此类传感器,因为这个传感器的精度还是很不错的,可以使用这个传感器作为反馈,将环境光照度稳定在一个期望的数值。本章只介绍环境光传感器的驱动,通过PID稳定环境光强度的内容将在后面介绍。
二、驱动原理&代码
BH1750是使用IIC总线进行驱动的,IIC协议仅用4根线 VCC、GND、SCL 、SDA 就可以实现数据的交互,在BH1750传感器中还有一个位(AD0)是来控制不同地址的,置不同的电平可以改变传感器内部的地址,方便用来在IIC总线上做设备扩展。
关于IIC
IIC驱动我己经介绍过多次了,很多设备都使用IIC协议进行通信,但是他们实现的代码略有差异,有些只用到了部分功能,有些则是用到了全部的功能,有些通信速率高,有些通信速率低,但他们都是IIC协议,基本原理不变,规则不变。接下来我就针对这个传感器编写了适应的IIC驱动(C++语言版本)。(PS:获取以后有机会写一个完整的软件IIC驱动可以应对所有传感器而不需要特别写一个)
IIC类文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class IIC_Device { private: gpio_num_t sda_io_num; gpio_num_t scl_io_num; public: IIC_Device(gpio_num_t sda_io, gpio_num_t scl_io) :sda_io_num(sda_io),scl_io_num(scl_io) { gpio_init(sda_io_num,scl_io_num); }
esp_err_t gpio_init(gpio_num_t sda_io, gpio_num_t scl_io); protected: void IIC_Start(void); void IIC_Stop(void);
uint8_t IIC_Wait_Ask(void);
void IIC_WriteByte(uint8_t data);
uint8_t IIC_ReadByte(void);
void SendACK(uint8_t ack); };
|
IIC实现函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
esp_err_t IIC_Device::gpio_init(gpio_num_t sda_io_num, gpio_num_t scl_io_num) { gpio_config_t io_conf; printf("init BH1750 i2c\n"); io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT_OD; io_conf.pin_bit_mask = (1ULL << sda_io_num) | (1ULL << scl_io_num); io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; io_conf.pull_up_en = GPIO_PULLUP_ENABLE; ESP_ERROR_CHECK(gpio_config(&io_conf)); ESP_ERROR_CHECK(gpio_set_level(sda_io_num, 1)); ESP_ERROR_CHECK(gpio_set_level(scl_io_num, 1)); printf("\nBH1750_SDA_GPIO:%d BH1750_SCL_GPIO:%d", sda_io_num, scl_io_num); return ESP_OK; }
void IIC_Device::IIC_Start(void) { gpio_set_direction(sda_io_num, GPIO_MODE_OUTPUT); I2C_MASTER_GPIO_OUT(sda_io_num, 1); I2C_MASTER_GPIO_OUT(scl_io_num, 1); delay_us(2); I2C_MASTER_GPIO_OUT(sda_io_num, 0); delay_us(2); I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); }
void IIC_Device::IIC_Stop(void) { I2C_MASTER_GPIO_OUT(scl_io_num, 1); I2C_MASTER_GPIO_OUT(sda_io_num, 0); delay_us(2); I2C_MASTER_GPIO_OUT(sda_io_num, 1); delay_us(2); }
uint8_t IIC_Device::IIC_Wait_Ask(void) { int count = 0; gpio_set_direction(sda_io_num, GPIO_MODE_INPUT); I2C_MASTER_GPIO_OUT(scl_io_num, 1); delay_us(2); while (gpio_get_level(sda_io_num)) { count++; if (count > 250) { IIC_Stop(); return 1; } } I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); return 0; }
void IIC_Device::IIC_WriteByte(uint8_t data) { uint8_t i; gpio_set_direction(sda_io_num, GPIO_MODE_OUTPUT); for (i = 0; i < 8; i++) { I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); if (data & 0x80) I2C_MASTER_GPIO_OUT(sda_io_num, 1); else I2C_MASTER_GPIO_OUT(sda_io_num, 0); I2C_MASTER_GPIO_OUT(scl_io_num, 1); delay_us(2); I2C_MASTER_GPIO_OUT(scl_io_num, 0); data <<= 1; } }
uint8_t IIC_Device::IIC_ReadByte(void) { uint8_t data = 0, i = 0; I2C_MASTER_GPIO_OUT(sda_io_num, 1); delay_us(2); gpio_set_direction(sda_io_num, GPIO_MODE_INPUT); for (i = 0; i < 8; i++) { data <<= 1; I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); I2C_MASTER_GPIO_OUT(scl_io_num, 1); delay_us(2); if (gpio_get_level(sda_io_num)) data = data | 0x01; else data = data & 0xFE; } I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); return data; }
void IIC_Device::SendACK(uint8_t ack) { gpio_set_direction(sda_io_num, GPIO_MODE_OUTPUT); gpio_set_level(scl_io_num, 0); I2C_MASTER_GPIO_OUT(sda_io_num, ack); I2C_MASTER_GPIO_OUT(scl_io_num, 1); delay_us(2); I2C_MASTER_GPIO_OUT(scl_io_num, 0); delay_us(2); }
|
BH1750驱动
本次所写的BH1750是 通过使用IIC类作为父类进行实现的。BH1750继承了IIC的特性,所以可以复用所有IIC中定义的功能。
本次编写的驱动库中,支持对传感器测量精度的控制和传感器值得读取。
光强传感器类定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| class BH1750_Device : public IIC_Device { private: uint8_t SlaveAddress = 0x46; uint8_t BUF[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void BH1750_SendByte(uint8_t data);
uint8_t BH1750_RecvByte();
void Single_Write_BH1750(uint8_t REG_Address);
void Multiple_Read_BH1750(void); public: float data = 0;
BH1750_MODE currect_mode = BH1750_FAST_MODE; BH1750_Device(gpio_num_t sda_io_num, gpio_num_t scl_io_num) : IIC_Device(sda_io_num, scl_io_num) { init(); }
void init();
float read_data();
void set_mode(BH1750_MODE mode); esp_err_t delay_ms(uint32_t time); };
|
光强传感器函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
void BH1750_Device::BH1750_SendByte(uint8_t data) { IIC_WriteByte(data); IIC_Wait_Ask(); }
uint8_t BH1750_Device::BH1750_RecvByte() { return IIC_ReadByte(); }
void BH1750_Device::Single_Write_BH1750(uint8_t REG_Address) { IIC_Start(); BH1750_SendByte(SlaveAddress); BH1750_SendByte(REG_Address); IIC_Stop(); }
void BH1750_Device::Multiple_Read_BH1750(void) { uint8_t i; IIC_Start(); BH1750_SendByte(SlaveAddress | 0x01); for (i = 0; i < 3; i++) { BUF[i] = BH1750_RecvByte(); if (i == 3) { SendACK(1); } else { SendACK(0); } } IIC_Stop(); delay_ms(5); }
void BH1750_Device::init() { delay_ms(10); Single_Write_BH1750(0x01); }
void BH1750_Device::set_mode(BH1750_MODE mode) { currect_mode = mode; }
float BH1750_Device::read_data() { float temp = 0; int dis_data = 0; if (currect_mode == BH1750_FAST_MODE) { Single_Write_BH1750(0x01); Single_Write_BH1750(0x13); delay_ms(18); } else if (currect_mode == BH1750_ACCURATE_MODE) { Single_Write_BH1750(0x01); Single_Write_BH1750(0x10); delay_ms(180); } Multiple_Read_BH1750(); dis_data = BUF[0]; dis_data = (dis_data << 8) + BUF[1]; temp = (float)dis_data / 1.2; data = temp; return temp; }
|
需要注意的是,BH1750光强传感器有三种精度,一种精度较低(4lx),但是可以转换速度快,每18ms就能够完成一次光强转换;另两种种转换精度高(0.5lx 1lx),但是转换速度低180ms(手册上写120ms~180ms)才能完成一次光强转换。
此外该传感器还有两种读取方式,一种是连续读取,该模式电源一直处于打开状态,还有一种是单次读取模式,此模式每次读取结束之后都会自动关闭电源,以达到节能的目的。
本次驱动中并没有写这一部分,只分了快速连续模式和精确连续模式,或许以后用到会再进行完善,或者如果大家有兴趣可以添加这部分简单的代码推送到我的github仓库,我会将好的代码merge进去。
三、结语
最近写博客的质量下降的很厉害,我自己也有感觉到,但是最近实在是太累了,每天很晚才回到家中。每天都在学习没有接触过得新鲜知识,感觉没有太多的精力去对以前的项目做博客分享这些事情,但我还是会坚持下去的,毕竟这是自己喜欢的事情。现在是凌晨1:30分,我已经困倦不堪,希望等我下一次闲下来的时候能够好好地再梳理一下我想要分享的这些东西。
本次项目的例程在我的github仓库上:https://github.com/gengyuchao/ESP8266_example/tree/master/project_BH1750 欢迎大家来我的博客评论和给我留言,或者给我的github项目点星星,提issue,提交pull request。把更多更好更有趣的知识传递下去。O(∩_∩)O哈哈~