硬件IIC主发送器传送序列图:
硬件IIC主接收器传送序列图:
BH1750官方文档"写指示"、"读结果"时序实例图:
BH1750指令集:
指令 | 功能代码 | 注释 |
断电 | 0000_0000 | 无激活状态 |
通电 | 0000_0001 | 等待测量指令 |
重置 | 0000_0111 | 重置寄存器 |
连续高分辨率模式 | 0001_0000 | 1LX分辨率,测量时间120ms |
连续高分辨率模式2 | 0001-0001 | 0.5LX分辨率,测量时间120ms |
连续低分辨率模式 | 0001_0011 | 4LX分辨率,测量时间16ms |
一次高分辨率模式 | 0010_0000 | 1LX分辨率,测量时间120ms |
一次高分辨率模式2 | 0010_0001 | 0.5LX分辨率,测量时间120ms |
一次低分辨率模式 | 0010_0011 | 4LX分辨率,测量时间120ms |
接线:
VCC | 3.3V |
GNG | 接地 |
SCL | PB6 |
SDL | PB7 |
ADDR | 接地 |
代码:
lightgrad.c
#include "stm32f10x.h"
#include "OLED.h"
#define SlaveAddress 0x46 //定义器件在IIC总线中的从地址,根据ALT ADDRESS地址引脚不同修改
//ALT ADDRESS引脚接地时地址为0x46,接电源时地址为0xB8
uint8_t Lightgrad[2]={0};//光照强度数值
//带超时退出的事件等待函数
void WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t timecount=40000;
while( I2C_CheckEvent(I2Cx,I2C_EVENT) == ERROR )
{
timecount--;
if(timecount == 0)//超时等待退出
{
break;
}
}
}
//写一个字节命令
void BH1750_WriteCommand(uint8_t command)
{
I2C_GenerateSTART(I2C1, ENABLE);//生成起始条件
WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件的到来
I2C_Send7bitAddress(I2C1, SlaveAddress, I2C_Direction_Transmitter);
//硬件I2C会自动获取应答标志位,根据标志位,若未应答申请中断
WaitEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件的到来
I2C_SendData(I2C1,command);//发送命令
WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待EV8_2事件的到来 停止事件
I2C_GenerateSTOP(I2C1, ENABLE);
}
//连续读出光照强度传感器寄存器中的数据
void BH1750_Read(void)
{
uint8_t i;
uint16_t t=40000;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY))//等待I2C总线空闲,加循环减减防止一直等待卡死在这
{
t--;
if(t == 0)//超时等待退出
{
return;
}
}
I2C_GenerateSTART(I2C1, ENABLE);//生成起始条件
WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件的到来
I2C_Send7bitAddress(I2C1, SlaveAddress, I2C_Direction_Receiver);//发送设备地址+读信号
WaitEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//等待EV6事件的到来
for(i=0;i<2;i++)
{
if(i == 1)//最后一个数据读到之前,发送非应答和停止信号
{
I2C_AcknowledgeConfig(I2C1,DISABLE);//取消应答
I2C_GenerateSTOP(I2C1, ENABLE);//停止信号
}
WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED);//等待EV7事件的到来 每次读数据都要先等EV7事件到来 ,容易出错点
Lightgrad[i]=I2C_ReceiveData(I2C1);//读数据
}
I2C_AcknowledgeConfig(I2C1,ENABLE);//恢复应答
}
void LightGard_Init(void)
{
//RCC开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//开漏输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//I2C配置
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//接收数据后是否应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位地址位
I2C_InitStructure.I2C_ClockSpeed = 50000;//速度
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//占空比2:1
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//I2C模式
I2C_InitStructure.I2C_OwnAddress1 = 0x00;//作为从机时自身地址
I2C_Init(I2C1,&I2C_InitStructure);//
//开启I2C
I2C_Cmd(I2C1, ENABLE);
//光照强度传感器控制信号
//因为此处初始化为”连续“模式,所以只需要在初始化时发送一次通电指令,和一次分辨率模式指令,
//在主函数的while循环中不要需要在写这两个命令
BH1750_WriteCommand(0x01); // 通电指令
BH1750_WriteCommand(0x10); // 连续高分辨率模式
}
lightgrad.h
#ifndef __LIGHTGARD_H
#define __LIGHTGARD_H
extern uint8_t Lightgrad[2];//光照强度数值
void BH1750_WriteCommand(uint8_t command);//写入BH1750命令
void BH1750_Read(void);//读取BH1750数值
void LightGard_Init(void);//初始化BH750
#endif
main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "lightgard.h"
#include "OLED.h"
uint16_t data=0;
int main(void)
{
OLED_Init();
LightGard_Init();
while(1)
{
BH1750_Read();//读取数值
data=Lightgrad[0];
data=(data<<8) | Lightgrad[1];
OLED_ShowBinNum(1,1,Lightgrad[0],8);
OLED_ShowBinNum(2,1,Lightgrad[1],8);
OLED_ShowNum(3,1,data,8);
}
}
易错点:
1、BH1750读数读出刚刚写入的器件地址:
原因:在读函数数据中,读数据时没有等待EV7事件的到来;或者把等待EV7事件的函数放在了读数据后面;或者忘记循环等待Ev7事件
2:BH1750上电后只能读取一次数值
原因:当初始化设置为连续读模式后,在主函数中只需要初始化一次写命令,while中不在需要重复写命令;
当初始化设置为一次读模式后,需要在主函数的while循环中每次读数据之前发送一次命令设置(上电、模式选择命令)