一.序言
BH1750是用IIC协议进行数据传输的。有SCL,SDA,VCC,GND四根线。下图是原理图
二.BH1750指令
我们先看芯片手册的操作指令(下图)含有掉电,上电,连续转化,一次转化以及转化的分辨率等。具体可以看下面的表格写的很详细。写代码时候需要用到(非常关键)。
这里我也整理出来了一个指令表格,方便阅读,和后序的操作。
指令 | 操作值 |
POWER_OFF | 0x00 |
POWER_OFF | 0x01 |
MODULE_RESET | 0x07 |
CONTINUE_H_MODE | 0x10 |
CONTINUE_H_MODE2 | 0x11 |
CONTINUE_L_MODE | 0x13 |
ONE_TIME_H_MODE | 0x20 |
ONE_TIME_H_MODE2 | 0x21 |
ONE_TIME_L_MODE | 0x23 |
三.IIC协议
IIC协议是在嵌入式中比较常用通信协议的,也是非常重要的。所以我们务必需要掌握IIC协议。
这里可以参考我的上一篇博文 一文教你彻底学会IIC协议
四.代码实例
4.1 bh1750.c源文件
#include "bh1750.h" /* 应用说明: 在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO */ static void I2C_BH1750_GPIOConfig(void); /* ********************************************************************************************************* * 函 数 名: i2c_Delay * 功能说明: I2C总线位延迟,最快400KHz * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void i2c_Delay(void) { uint8_t i; /* 下面的时间是通过逻辑分析仪测试得到的。 工作条件:CPU主频72MHz ,MDK编译环境,1级优化 循环次数为10时,SCL频率 = 205KHz 循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us */ for (i = 0; i < 10; i++); } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线启动信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Start(void) { /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */ BH1750_I2C_SDA_1(); BH1750_I2C_SCL_1(); i2c_Delay(); BH1750_I2C_SDA_0(); i2c_Delay(); BH1750_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: i2c_Start * 功能说明: CPU发起I2C总线停止信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Stop(void) { /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */ BH1750_I2C_SDA_0(); BH1750_I2C_SCL_1(); i2c_Delay(); BH1750_I2C_SDA_1(); } /* ********************************************************************************************************* * 函 数 名: i2c_SendByte * 功能说明: CPU向I2C总线设备发送8bit数据 * 形 参:_ucByte : 等待发送的字节 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_SendByte(uint8_t _ucByte) { uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) { if (_ucByte & 0x80) { BH1750_I2C_SDA_1(); } else { BH1750_I2C_SDA_0(); } i2c_Delay(); BH1750_I2C_SCL_1(); i2c_Delay(); BH1750_I2C_SCL_0(); if (i == 7) { BH1750_I2C_SDA_1(); // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } } /* ********************************************************************************************************* * 函 数 名: i2c_ReadByte * 功能说明: CPU从I2C总线设备读取8bit数据 * 形 参:无 * 返 回 值: 读到的数据 ********************************************************************************************************* */ uint8_t i2c_ReadByte(void) { uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) { value <<= 1; BH1750_I2C_SCL_1(); i2c_Delay(); if (BH1750_I2C_SDA_READ()) { value++; } BH1750_I2C_SCL_0(); i2c_Delay(); } return value; } /* ********************************************************************************************************* * 函 数 名: i2c_WaitAck * 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号 * 形 参:无 * 返 回 值: 返回0表示正确应答,1表示无器件响应 ********************************************************************************************************* */ uint8_t i2c_WaitAck(void) { uint8_t re; BH1750_I2C_SDA_1(); /* CPU释放SDA总线 */ i2c_Delay(); BH1750_I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (BH1750_I2C_SDA_READ()) /* CPU读取SDA口线状态 */ re = 1; else re = 0; BH1750_I2C_SCL_0(); i2c_Delay(); return re; } /* ********************************************************************************************************* * 函 数 名: i2c_Ack * 功能说明: CPU产生一个ACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_Ack(void) { BH1750_I2C_SDA_0(); /* CPU驱动SDA = 0 */ i2c_Delay(); BH1750_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); BH1750_I2C_SCL_0(); i2c_Delay(); BH1750_I2C_SDA_1(); /* CPU释放SDA总线 */ } /* ********************************************************************************************************* * 函 数 名: i2c_NAck * 功能说明: CPU产生1个NACK信号 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void i2c_NAck(void) { BH1750_I2C_SDA_1(); /* CPU驱动SDA = 1 */ i2c_Delay(); BH1750_I2C_SCL_1(); /* CPU产生1个时钟 */ i2c_Delay(); BH1750_I2C_SCL_0(); i2c_Delay(); } /* ********************************************************************************************************* * 函 数 名: I2C_BH1750_GPIOConfig * 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ static void I2C_BH1750_GPIOConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(BH1750_RCC_I2C_PORT, ENABLE); /* 打开GPIO时钟 */ GPIO_InitStructure.GPIO_Pin = BH1750_I2C_SCL_PIN | BH1750_I2C_SDA_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */ GPIO_Init(BH1750_GPIO_PORT_I2C, &GPIO_InitStructure); /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */ i2c_Stop(); } /* ********************************************************************************************************* * 函 数 名: i2c_CheckDevice * 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在 * 形 参:_Address:设备的I2C总线地址 * 返 回 值: 返回值 0 表示正确, 返回1表示未探测到 ********************************************************************************************************* */ uint8_t i2c_CheckDevice(uint8_t _Address) { uint8_t ucAck; i2c_Start(); /* 发送启动信号 */ /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */ i2c_SendByte(_Address | BH1750_I2C_WR); ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */ i2c_Stop(); /* 发送停止信号 */ return ucAck; } //BH1750写一个字节 //返回值 成功:0 失败:非0 uint8_t BH1750_Byte_Write(uint8_t data) { i2c_Start(); //发送写地址 i2c_SendByte(BH1750_Addr|0); if(i2c_WaitAck()==1) return 1; //发送控制命令 i2c_SendByte(data); if(i2c_WaitAck()==1) return 2; i2c_Stop(); return 0; } //BH1750读取测量数据 //返回值 成功:返回光照强度 失败:返回0 uint16_t BH1750_Read_Measure(void) { uint16_t receive_data=0; i2c_Start(); //发送读地址 i2c_SendByte(BH1750_Addr|1); if(i2c_WaitAck()==1) return 0; //读取高八位 receive_data=i2c_ReadByte(); i2c_Ack(); //读取低八位 receive_data=(receive_data<<8)+i2c_ReadByte(); i2c_NAck(); i2c_Stop(); return receive_data; //返回读取到的数据 } //BH1750s上电 void BH1750_Power_ON(void) { BH1750_Byte_Write(POWER_ON); } //BH1750s断电 void BH1750_Power_OFF(void) { BH1750_Byte_Write(POWER_OFF); } //BH1750复位 仅在上电时有效 void BH1750_RESET(void) { BH1750_Byte_Write(MODULE_RESET); } //BH1750初始化 uint8_t BH1750_Init(void) { I2C_BH1750_GPIOConfig(); /* 配置GPIO */ BH1750_Power_ON(); //BH1750s上电 //BH1750_RESET(); //BH1750复位 return BH1750_Byte_Write(Measure_Mode); //SysTick_Delay_ms(120); } //获取光照强度 int LIght_Intensity(void) { float v; v = (float)(BH1750_Read_Measure()/1.1f*Resolurtion); return (int) v; }
4.2 bh1750.h头文件
#ifndef __BH1750_H #define __BH1750_H #include "stm32f10x.h" //BH1750的地址 #define BH1750_Addr 0x46//0x46 //BH1750指令码 #define POWER_OFF 0x00 #define POWER_ON 0x01 #define MODULE_RESET 0x07 #define CONTINUE_H_MODE 0x10 #define CONTINUE_H_MODE2 0x11 #define CONTINUE_L_MODE 0x13 #define ONE_TIME_H_MODE 0x20 #define ONE_TIME_H_MODE2 0x21 #define ONE_TIME_L_MODE 0x23 //测量模式 #define Measure_Mode CONTINUE_H_MODE //分辨率 光照强度(单位lx)=(High Byte + Low Byte)/ 1.2 * 测量精度 #if ((Measure_Mode==CONTINUE_H_MODE2)|(Measure_Mode==ONE_TIME_H_MODE2)) #define Resolurtion 0.5 #elif ((Measure_Mode==CONTINUE_H_MODE)|(Measure_Mode==ONE_TIME_H_MODE)) #define Resolurtion 1 #elif ((Measure_Mode==CONTINUE_L_MODE)|(Measure_Mode==ONE_TIME_L_MODE)) #define Resolurtion 4 #endif #define BH1750_I2C_WR 0 /* 写控制bit */ #define BH1750_I2C_RD 1 /* 读控制bit */ /* 定义I2C总线连接的GPIO端口, 只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */ #define BH1750_GPIO_PORT_I2C GPIOB /* GPIO端口 */ #define BH1750_RCC_I2C_PORT RCC_APB2Periph_GPIOB /* GPIO端口时钟 */ #define BH1750_I2C_SCL_PIN GPIO_Pin_6 /* 连接到SCL时钟线的GPIO */ #define BH1750_I2C_SDA_PIN GPIO_Pin_7 /* 连接到SDA数据线的GPIO */ /* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */ #define BH1750_I2C_SCL_1() GPIO_SetBits(BH1750_GPIO_PORT_I2C, BH1750_I2C_SCL_PIN) /* SCL = 1 */ #define BH1750_I2C_SCL_0() GPIO_ResetBits(BH1750_GPIO_PORT_I2C, BH1750_I2C_SCL_PIN) /* SCL = 0 */ #define BH1750_I2C_SDA_1() GPIO_SetBits(BH1750_GPIO_PORT_I2C, BH1750_I2C_SDA_PIN) /* SDA = 1 */ #define BH1750_I2C_SDA_0() GPIO_ResetBits(BH1750_GPIO_PORT_I2C, BH1750_I2C_SDA_PIN) /* SDA = 0 */ #define BH1750_I2C_SDA_READ() GPIO_ReadInputDataBit(BH1750_GPIO_PORT_I2C, BH1750_I2C_SDA_PIN) /* 读SDA口线状态 */ void i2c_Start(void); void i2c_Stop(void); void i2c_SendByte(uint8_t _ucByte); uint8_t i2c_ReadByte(void); uint8_t i2c_WaitAck(void); void i2c_Ack(void); void i2c_NAck(void); uint8_t i2c_CheckDevice(uint8_t _Address); uint8_t BH1750_Init(void); //未包含IIC初始化 int LIght_Intensity(void); //读取光照强度的值 uint8_t BH1750_Byte_Write(uint8_t data); uint16_t BH1750_Read_Measure(void); void BH1750_Power_ON(void); void BH1750_Power_OFF(void); void BH1750_RESET(void); #endif