草庐IT

STM32 驱动 GY-302 光照传感器 BH1750 模块(软件IIC与硬件IIC驱动)

惜暮怜曦 2023-06-15 原文

1.特别说明

​ 要是不想看原理和过程,直接下拉找代码吧,都是测试过的,很稳定,有硬件I2C驱动的,也有软件模拟I2C驱动的,基于STM32F103系列和STM32F4系列实现,基于标准库实现,条理清晰。

2.软硬件硬件准备

​ (1) Kile 5.27

​ (2) 串口助手

​ (3) STM32F103与STM32F4系列单片机

​ (4) CH340 串口转 TTL 工具

​ (5) BH1750-即-GY-302光照传感器

3.了解驱动原理

3.1.程序设计思路


这是官方数据手册给的驱动说明,这是基于标准I2C设计的传感器,也就是说它支持标准全速400KHz速率通信(实测确实没问题);这个流程图大概说的是程序设计:

1.上电初始化

2.掉电再上电(软指令,可省略步骤)

3.发送测量命令(单次测量或者连续测量)

4.读取测量数据

3.2.引脚接线说明

VCC5V~3.3V
GNDGND
SCLPB6
SDAPB7
ADDRVCC/GND

首先这个模块比较友好,支持3V3到5V供电,其次特别注意ADDR这个引脚的接线:

接高电平的硬件地址和接低电平的硬件地址是不一样的:

​ 接高电平硬件地址:0X5C (注意:这是高 7 位地址数据)

​ 接低电平硬件地址:0X23 (注意:这是高 7 位地址数据)

器件地址(7位-用左移1位-最后1位为读写位–1为读-0为写)

3.3驱动时序

写指令流程

1.发送起始信号 ————> 2.发送写指令地址(接收ACK信号)

3.发送操作指令(接收ACK信号) ————> 4.发送停止信号

读指令流程:

1.发送起始信号 ——> 2.发送读指令地址(接收ACK信号)

3.接收高 8 位数据(发送ACK信号) ——> 4.接收低 8 位数据(发送NACK)

5.发送结束信号

3.3.指令集说明

功能寄存器地址指令注释说明
断电0000_0000没有活动状态。
接通电源0000_0001正在等待测量命令。
重置0000_0111重置数据寄存器值。在关机模式下,不能接受重置命令。
连续H分辨率模式0001_0000以1lx的分辨率开始测量。测量时间通常为120ms。
连续h分辨率模式20001_0001在0.5lx的分辨率下开始测量。测量时间通常为120ms。
连续L分辨率模式0001_0011以4lx的分辨率开始测量。测量时间通常为16ms。
一次H分辨率模式0010_0000以1lx的分辨率开始测量。测量时间通常为120ms。测量后自动设置为关机模式。
一次H分辨率模式20010_0001在0.5lx的分辨率下开始测量。测量时间通常为120ms。测量后自动设置为关机模式。
一次性L分辨率模式0010_0011以4lx的分辨率开始测量。测量时间通常为16ms。测量后自动设置为关机模式。
更改测量时间(高位)01000_MT[7,6,5]改变测量时间。※请参考“调整测量结果对光学窗口的影响”。
更改测量时间(低位)011_MT[4,3,2,1,0]更改测量时间。※请参考“调整测量结果对光学窗口的影响”。

3.4数据转换

(uint16_t)Data_LX = (uint16_t)(H_data << 8) + (uint8_t)L_data

接收用无符号短 8 位整形接收,移位对齐数据再相加就好了;

4.上代码

4.1硬件IIC驱动

​ 特别说明,在刚开始硬件驱动的时候,出了个小问题:完全照着手册的时序写,就是会卡死在接收高 8 位数据的 EV7事件上;

​ 解决办法:在发送读指令并接收到回应后(EV6 事件),主机给光照模块发送一个 ACK 信号,相当于告诉它我们准备好接受数据了,然后等待光照模块发来数据(EV7事件),接着后面就没啥问题了。

4.1.1 适用STM32F103系列的硬件IIC驱动

BH1750.c

#include "BH1750.h"




/****************************************************************************
**	函数名称:I2C_Configuration()
**	函数功能:显示用到的引脚初始化与 IIC初始化
**	参数说明:
**
**	返回值:无
**	IIC_InitTypeDef 说明:
		I2C_ClockSpeed;     	//设置SCL时钟频率,此值最高 400 000
		I2C_Mode;             	//指定工作模式,可选IIC模式及SMBUS模式
		I2C_DutyCycle;      	//时钟占空比,可选low/high = 2:0或16:9
		I2C_OwnAddress1;  		//自身的IIC设备地址
		I2C_Ack;                //使能或者关闭响应,一般是使能
		I2C_AcknowledgedAddress;//指定地址长度,可为7或10

******************************************************************************
	STM32F103C8T6芯片的硬件:
		IIC_1: PB6 -- SCL; PB7 -- SDA
		IIC_1(重映射):PB8 -- SCL; PB9 -- SDA
		IIC_2:
*****************************************************************************/
void I2C_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    /*	重映射开启AFIO时钟	*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

    GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE);	//重映射 IIC

    /*	重映射引脚	使用IIC_1  PB8 -- SCL; PB9 -- SDA 	*/
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;	//I2C必须开漏输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    I2C_InitTypeDef  I2C_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

    I2C_DeInit(I2C1);	//使用I2C1
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//选择IIC模式
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//占空比 1/2
    I2C_InitStructure.I2C_OwnAddress1 = 0x30;			//主机的I2C地址
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;			//应答信号使能
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//IIC 7 位寻址
    I2C_InitStructure.I2C_ClockSpeed = 100000;			//设置最大400K

    I2C_Cmd(I2C1, ENABLE);				//使能IIC1
    I2C_Init(I2C1, &I2C_InitStructure);	//初始化 IIC_1 相关

}

/****************************************************************************
**	函数名称:BH1750_WriteByte()
**	函数功能:BH1750 写数据
**	参数说明:
**			uint8_t addr: 寄存器地址
**
**	返回值:无
**	IIC函数说明:
**				I2C_GetFlagStatus(IIC_x, IIC_Flag_x)-- 检查标志位是否置 1
**
*****************************************************************************/
void BH1750_WriteByte(uint8_t addr)
{

    /*	先判断总线是否忙  */
    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));

    /* 发送Start信号,切换到主模式(M/SL 位置 1) */
    I2C_GenerateSTART(I2C1, ENABLE);
    /*	等待EV5事件,IIC开始信号已经发出 */
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));

    /*	发送器件地址,发送方向: I2C_Direction_Transmitter */
    I2C_Send7bitAddress(I2C1, (BH1750_Addr_GND_REG<<1|0), I2C_Direction_Transmitter);
    /*	等待EV6事件:表示地址已经发送  */
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    
    /*	发送操作的寄存器地址  */
    I2C_SendData(I2C1, addr);
    /*	等待EV8事件:数据寄存器DR为空,地址数据已经发送	*/
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
	
	    /*	产生应答信号	*/
    I2C_AcknowledgeConfig(I2C1, ENABLE);

    I2C_GenerateSTOP(I2C1, ENABLE);//产生停止信号,关闭I2C1总线
}


/****************************************************************************
**	函数名称:BH1750_Read_Data()
**	函数功能:BH1750 读取数据
**	参数说明:
**			uint8_t addr :寄存器地址
**
**	返回值:RX_Data
**	IIC函数说明:
**				I2C_GetFlagStatus(IIC_x, IIC_Flag_x)-- 检查标志位是否置 1
**
*****************************************************************************/
uint16_t BH1750_Read_Data(void)
{
    uint8_t 	H_Data;							//高 8 位数据
	uint8_t 	L_Data;							//低 8 位 数据
	uint16_t 	Rx_Data;						//完整 16 位数据

    while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));					/* 先判断总线是否忙 */

    I2C_GenerateSTART(I2C1, ENABLE); 								/* 发送Start信号,切换到主模式  */
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));		/* 等待EV5事件,IIC开始信号已经发出 */

    /*	发送读地址指令,发送方向: I2C_Direction_Receiver */
    I2C_Send7bitAddress(I2C1, BH1750_Addr_GND_REG<<1|1, I2C_Direction_Receiver);
    /*	等待EV6事件:地址已发送 (接收类型)  */
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
	
    
    I2C_AcknowledgeConfig(I2C1, ENABLE);							/*	产生应答信号	*/
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); 	/* 等待EV7事件 */
    H_Data = I2C_ReceiveData(I2C1);									/* 读取高8位数据  */

    
    I2C_AcknowledgeConfig(I2C1, ENABLE);							/* 产生应答信号 */
    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));	/*	等待EV7事件 */
	/* 与高8位合成16位完整数据 */
	L_Data = I2C_ReceiveData(I2C1);									/* 读取低8位 */
	   
    I2C_AcknowledgeConfig(I2C1, DISABLE); 							/*	产生非应答信号	*/
	I2C_GenerateSTOP(I2C1, ENABLE);									/**	发送停止信号	**/
    
    Rx_Data = ((uint16_t)H_Data << 8) + L_Data;						/* 16位数据合成 */

    return RX_Data;
}


/**	BH1750上电 **/
void BH1750_Power_ON(void)
{
	BH1750_WriteByte(BH1750_Power_ON_REG);
}

/**	BH1750断电 **/
void BH1750_Power_OFF(void)
{
	BH1750_WriteByte(BH1750_Power_OFF_REG);
}

/**	BH1750数据寄存器复位 **/
void BH1750_RESET(void)
{
	BH1750_WriteByte(BH1750_MODULE_RESET_REG);
}

/**	BH1750初始化 **/
void BH1750_Init(void)
{
    I2C_Configuration();
    delay_ms(100);
    BH1750_Power_OFF();
	BH1750_Power_ON();
	BH1750_RESET();
	BH1750_WriteByte(BH1750_CONTINUE_H_MODE2);
}

/**	获取光照强度 **/
/**	分辨率	光照强度(单位lx)=(High Byte + Low Byte)/ 1.2  **/
float Light_Intensity(void)
{
	return (float)(BH1750_Read_Data() / 1.2f );
}

/*************************************END***********************************************/

BH1750.h

#ifndef __BH1750_H
#define __BH1750_H


#include "stm32f10x.h"
#include "delay.h"





/*	器件地址(7位-用左移1位-最后1位为读写位--1为读-0为写) */
#define				BH1750_Addr_GND_REG				0X23	//ADDR引脚接低电平
#define				BH1750_Addr_VCC_REG				0X5C	//ADDR引脚接高电平


				/******	指令集宏定义*****/
#define				BH1750_Power_OFF_REG			0x00	//断电指令
#define				BH1750_Power_ON_REG				0x01	//通电,等待测量命令
#define				BH1750_MODULE_RESET_REG			0x07	//重置数据寄存器,关机模式下,不能接受重置命令


/*************************************************	
	不同模式下分辨率不同(也即精度不同)
	高分辨率模式2:分辨率是0.5lx
	高分辨率模式:分辨率1lx
	低分辨率模式:分辨率4lx
	不同模式只是精度不一样,对于计算没有区别
***************************************************/
	/**	工作模式指令集	**/
#define				BH1750_CONTINUE_H_MODE			0x10	//连续H分辨率模式
#define				BH1750_CONTINUE_H_MODE2			0x11	//连续H分辨率模式2
#define				BH1750_CONTINUE_L_MODE			0x13	//连续L分辨率模式
#define				BH1750_ONE_TIME_H_MODE			0x20	//一次H分辨率模式
#define				BH1750_ONE_TIME_H_MODE2			0x21	//一次H分辨率模式2
#define				BH1750_ONE_TIME_L_MODE			0x23	//一次性L分辨率模式



/**		函数声明区	**/
void 		I2C_Configuration				(void);
void 		BH1750_WriteByte				(uint8_t addr);
uint16_t 	BH1750_Read_Data				(void);
void 		BH1750_Power_ON					(void);
void 		BH1750_Power_OFF				(void);
void 		BH1750_RESET					(void);
void 		BH1750_Init						(void);
float 		Light_Intensity					(void);
		

	
#endif

4.1.2 适用STM32F103系列的软件IIC驱动 ( BH1750.h 头文件基本一样)

BH1750.c

#include "BH1750.h"



/****************************************************************************
**	函数名称:BH1750_WriteByte()
**	函数功能:BH1750 写数据
**	参数说明:
**			uint8_t addr: 寄存器地址
**
**	返回值:无
**	IIC函数说明:
**				I2C_GetFlagStatus(IIC_x, IIC_Flag_x)-- 检查标志位是否置 1
**
*****************************************************************************/
void BH1750_WriteByte(uint8_t addr)
{	
	IIC_Start();								// 1.发送起始信号
	
	IIC_Send_Byte(BH1750_Addr_GND_REG << 1|0);	// 2.发送7位地址与读写位(0:写)
	while(!IIC_Wait_Ack());						// 3.等待从机应答信号
	
	IIC_Send_Byte(addr);						// 4.发送8位操作指令
	while(!IIC_Wait_Ack());						// 5.等待从机应答信号
	
	IIC_Stop();									// 6.发送停止信号
}



/****************************************************************************
**	函数名称:BH1750_Read_Data()
**	函数功能:BH1750 读取数据
**	参数说明:
**			uint8_t addr :寄存器地址
**
**	返回值:RX_Data
**	IIC函数说明:
**				I2C_GetFlagStatus(IIC_x, IIC_Flag_x)-- 检查标志位是否置 1
**
*****************************************************************************/
uint16_t BH1750_Read_Data(void)
{
    uint8_t 	H_Data;							//高 8 位数据
	uint8_t 	L_Data;							//低 8 位 数据
	uint16_t 	Rx_Data;						//完整 16 位数据
	
	IIC_Start();								// 1.发送起始信号
	IIC_Send_Byte(BH1750_Addr_GND_REG << 1|1);	// 2.发送7位地址与读写位(1:读)
	while(!IIC_Wait_Ack());						// 3.等待从机应答信号
	
	H_Data = IIC_Read_Byte(1);					// 4.读取高八位并发送ACK
	L_Data = IIC_Read_Byte(0);					// 5.读取低八位并发送NACK
	
	IIC_Stop();									// 6.发送停止信号
    Rx_Data = ((uint16_t)H_Data << 8) + L_Data;	// 7.16位数据合成
	
    return Rx_Data;								// 8.返回读取数据
}


/**	BH1750上电 **/
void BH1750_Power_ON(void)
{
	BH1750_WriteByte(BH1750_Power_ON_REG);
}

/**	BH1750断电 **/
void BH1750_Power_OFF(void)
{
	BH1750_WriteByte(BH1750_Power_OFF_REG);
}

/**	BH1750数据寄存器复位 **/
void BH1750_RESET(void)
{
	BH1750_WriteByte(BH1750_MODULE_RESET_REG);
}

/**	BH1750初始化 **/
void BH1750_Init(void)
{
	IIC_Init();
    delay_ms(100);
	BH1750_Power_OFF();
	BH1750_Power_ON();
	BH1750_RESET();
	BH1750_WriteByte(BH1750_CONTINUE_H_MODE2);
}

/**	
**	获取光照强度
**	分辨率	光照强度(单位lx)=(High Byte + Low Byte)/ 1.2  
**/
float Light_Intensity(void)
{
	return (float)(BH1750_Read_Data() / 1.2f );
}

/*************************************END***********************************************/

头文件基本一样,只需要导入软件模拟 IIC 的头文件就行,(#include "IIC.h"),工程导入模拟IIC.c文件

IIC.c

#include "IIC.h"



/**
*	函数说明:软件模拟 IIC
*
*	函数功能:GPIO 初始化
*
*	注意事项:GPIO 可自行选择
*
*	记录:	GPIO 引脚初始化,相同的归
*			属GPIOX尽量放同一语句中;
**/
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//SCL & SDA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ;   	//推挽/开漏
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;		//速度 10 MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 				//PB6,PB7 输出高
}


/**
*	函数说明:产生IIC起始信号
*
*	原理说明:	当 SCL 是高电平状态时,
*				数据线 SDA 由高拉低;
**/
void IIC_Start(void)
{
    SDA_OUT();		/* SDA 线输出模式 */
    IIC_SDA = 1;	/* 先拉高 SDA 状态 */
    IIC_SCL = 1;	/* 再拉高 SCL 状态 */
    IIC_Delay();	/* 延时保持电平状态 */
    IIC_SDA = 0;	/* 后拉低 SDA 状态 */
    IIC_Delay();	/* 延时保持电平状态 */
    IIC_SCL = 0;	/* 钳住I2C,发送或接收 */
}



/**
*	函数说明:产生IIC停止信号
*
*	原理说明:	SCL线为高电平状态时, SDA 线
*				由低电平向高电平跳变(上升沿)
**/
void IIC_Stop(void)
{
    SDA_OUT();		/* SDA 线输出模式 */
    IIC_SCL = 0;	/* 先拉低 SCL 状态 */
    IIC_SDA = 0;	/* 先拉低 SDA 状态 */
    IIC_Delay();	/* 延时保持电平状态 */
    IIC_SCL = 1;	/* 后拉高 SCL 状态 */
    IIC_SDA = 1;	/* 后拉高 SDA 状态 */
    IIC_Delay();	/* 延时保持电平状态 */
}



/**
*	函数说明:等待应答信号到来
*
*	原理说明:	先将数据线拉高,延时等待稳定,然后将时钟线拉高,
*				延时等待稳定,最后采样数据线电平状态,如果是高
*				电平,未应答,如果是低电平,应答;
*
*	返回值:0, 接收应答失败
*			1, 接收应答成功
**/
uint8_t IIC_Wait_Ack(void)
{
    uint16_t ErrorTime;
	
    SDA_IN();      			/** SDA 线输入模式 **/
    IIC_SDA = 1;			/* 先拉高 SDA 状态 */
    IIC_Delay();			/* 延时保持电平状态 */
    IIC_SCL = 1;			/* 后拉高 SCL 状态 */
    IIC_Delay();			/* 延时保持电平状态 */
	
    while(Read_SDA)			/* 采样 SDA 电平状态 */
    {
        ErrorTime ++;
		
        if(ErrorTime > 250)
        {
            IIC_Stop();		/* 非应答,结束通信 */
            return 0;
        }
    }
    IIC_SCL = 0;			/** 钳住,等待发或收 **/
	
    return 1;				/** 接收到应答信号 **/
}


/**
*	函数说明:产生 ACK 应答
*
*	原理说明:SCL 在高电平期间 SDA 始终处于低电平
*			 (SCL 保持时间 <= SDA 保持时间)
*			  需要在传输完毕一个字节后发送
*
**/
void IIC_Ack(void)
{
	SDA_OUT();					/** SDA 线输出模式 **/
    IIC_SCL = 0;				/* 时钟线 SCL 拉低 */
    IIC_SDA = 0;				/* 数据线 SDA 拉低 */
    IIC_Delay();				/* 延时保持电平状态 */
    IIC_SCL = 1;				/* 时钟线 SCL 拉高 */
    IIC_Delay();				/* 延时保持电平状态 */
    IIC_SCL = 0;				/* 时钟线 SCL 拉低 */
}


/**
*	函数说明:产生 NACK 应答
*
*	原理说明:SCL在高电平期间SDA始终处于高电平
*			 (SCL 保持时间 <= SDA 保持时间)
*			  需要在传输完毕一个字节后发送
*
**/
void IIC_NAck(void)
{
	SDA_OUT();					/** SDA 线输出模式 **/
    IIC_SCL = 0;				/* 数据线 SCL 拉低 */
    IIC_SDA = 1;				/* 数据线 SDA 拉高 */
    IIC_Delay();				/* 延时保持电平状态 */
    IIC_SCL = 1;				/* 时钟线 SCL 拉高 */
    IIC_Delay();				/* 延时保持电平状态 */
    IIC_SCL = 0;				/* 时钟线 SCL 拉低 */
}



/**
*	函数功能:IIC发送一个字节
*	参数说明:Txd	无符号 8 位
*	
*	返回值:1, 有应答
*			0, 无应答
**/
void IIC_Send_Byte(uint8_t Txd)
{
    uint8_t T;
	
    SDA_OUT();						/** 数据线SDA输出态 **/
    IIC_SCL = 0;					/** 时钟线 SCL 拉低 **/
	
    for(T = 0; T < 8; T++)			/** 由高到低发送数据 */
    {
        IIC_SDA = (Txd & 0x80) >> 7;/** 每次发送最高位 **/
        Txd <<= 1;					/* 更新数据的最高位 */
        IIC_Delay();				/* 延时保持电平状态 */
        IIC_SCL = 1;				/* 时钟线 SCL 拉高 */
        IIC_Delay();				/* 延时保持电平状态 */
        IIC_SCL = 0;				/* 时钟线 SCL 拉低 */
        IIC_Delay();				/* 延时保持电平状态 */
    }
}


/*
 * 函数功能:IIC读字节
 * 参数说明:ack = 1, 发送ACK
 *			 ack = 0, 发送NACK
 *
 * 返回值:返回读取到的一个字节
 */
uint8_t IIC_Read_Byte(uint8_t ACK)
{
	uint8_t	T;
	uint8_t	Receive;

    SDA_IN();				/** SDA 线设置为输入 **/
	
    for(T = 0; T < 8; T++ )	/** 由高到低接收数据**/
    {
        IIC_SCL = 0;		/* 时钟线拉低的时候,SDA才允许变化 */
        IIC_Delay();		/* 延时保持电平状态 */
        IIC_SCL = 1;		/* 拉高时钟线,不允许SDA变化,可以读取 SDA */
        Receive <<= 1;		/** 接收到数据位左移 **/
        if(Read_SDA)
        {
            Receive++;		/** 高电平,则最低位为1 **/
        }
        IIC_Delay();		/* 延时保持电平状态 */
    }
	
    if (!ACK)	
		IIC_NAck();			/** 发送 NACK **/
    else		
		IIC_Ack(); 			/** 发送  ACK **/
    return Receive;
}

IIC.h

#ifndef __IIC_H
#define __IIC_H




#include "stm32f10x.h"
#include "delay.h"
#include "sys.h" 


/*********************************
**	引脚选择:					**
**			SCL ---> PXx		**
**			SDA ---> PXx		**
**********************************/
	
/*******************************保持时间***********************************/  
#define  IIC_Delay()		{	delay_us(5);	}	   		   

/*************************** IO方向设置(GPIOx * 4) ****************************/
 
					/**		PXx 上拉输入模式		**/
#define  SDA_IN()  {GPIOB -> CRL &= 0X0FFFFFFF; GPIOB -> CRL |= (u32)8 << 28;}
					/**		PXx 推挽输出模式		**/
#define  SDA_OUT() {GPIOB -> CRL &= 0X0FFFFFFF; GPIOB -> CRL |= (u32)3 << 28;}





/*************************** IO操作函数 *************************/	 
#define 		IIC_SCL    		PBout(6) 	//SCL 输出
#define 		IIC_SDA    		PBout(7) 	//SDA 输出	
#define 		Read_SDA   		PBin (7)  	//SDA 输入





/***************************** IIC所有操作函数 **********************************/

void 				IIC_Init				(void);             //初始化IIC的IO口
void 				IIC_Start				(void);				//发送IIC开始信号
void 				IIC_Stop				(void);	  			//发送IIC停止信号
void 				IIC_Send_Byte			(uint8_t Txd);		//IIC发送一个字节
uint8_t 			IIC_Read_Byte			(uint8_t ACK);		//IIC读取一个字节
uint8_t 			IIC_Wait_Ack			(void); 			//IIC等待ACK信号
void 				IIC_Ack					(void);				//IIC发送ACK信号
void 				IIC_NAck				(void);				//IIC不发送ACK信号


 
#endif

4.2 STM32F4系列的大同小异,只是硬件IO驱动函数不太一样而已

需要改动的就只有两个函数:I2C_Configuration() 和 IIC_Init() ;头文件也需要改的一点;

/********************************** BH1750.c 改动 ***************************************/

void I2C_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
	I2C_InitTypeDef  I2C_InitStructure;

    RCC_AHB1PeriphClockCmd(BH1750_GPIO_CLOCK, ENABLE);		/*使能 GPIOB 时钟*/
    RCC_APB1PeriphClockCmd(BH1750_IIC_CLOCK, ENABLE);		/*使能 IIC_x 时钟*/

    /* STM32F401CCU6芯片的硬件I2C1: 
			
			I2C1:PB6 -- SCL  PB7 -- SDA
			I2C1:PB8 -- SCL	 PB9 -- SDA	
			I2C2:PB10 --SCL  PB3 -- SDA 
			I2C3:PA8 ---SCL  PB4 -- SDA
	**/
    GPIO_InitStructure.GPIO_Pin =  BH1750_SCL | BH1750_SDA;	//IIC引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//高速模式
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;			//复用模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;			//开漏输出
    GPIO_Init(BH1750_GPIOx, &GPIO_InitStructure);

		/**	连接 AFx,这里必须要开启引脚重映射	**/
	//GPIO_PinAFConfig(BH1750_GPIOx, GPIO_PinSource8, GPIO_AF_I2C1);
	//GPIO_PinAFConfig(BH1750_GPIOx, GPIO_PinSource9, GPIO_AF_I2C1);
	
	GPIO_PinAFConfig(BH1750_GPIOx, GPIO_PinSource6, GPIO_AF_I2C1);
	GPIO_PinAFConfig(BH1750_GPIOx, GPIO_PinSource7, GPIO_AF_I2C1);
	

    I2C_DeInit(BH1750_IIC);								//使用I2Cx
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//选择IIC模式
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//占空比 1/2
    I2C_InitStructure.I2C_OwnAddress1 = 0x30;			//主机的I2C地址,随便写的
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;			//应答信号使能
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//IIC 7 位寻址
    I2C_InitStructure.I2C_ClockSpeed = 10000;			//设置最大400K
    I2C_Cmd(BH1750_IIC, ENABLE);						//使能IIC
    I2C_Init(BH1750_IIC, &I2C_InitStructure);			//初始化 IIC 相关
	    
}

头文件(.h文件)改动

/********************************** BH1750.h 改动 ***************************************/
/************************************硬件使用到的相关************************************/
#define				BH1750_GPIOx					GPIOB					// GPIOx
#define				BH1750_GPIO_CLOCK				RCC_AHB1Periph_GPIOB	//GPIOx_CLOCK
#define				BH1750_IIC_CLOCK				RCC_APB1Periph_I2C1		// IIC_CLOCK
#define				BH1750_SCL						GPIO_Pin_6				// GPIO_SCL
#define				BH1750_SDA						GPIO_Pin_7				// GPIO_SDA
#define				BH1750_IIC						I2C1					// IIC_X


软件IIC模拟改动

/************************************ IIC.c 改动 ****************************************/
void IIC_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

		/**	自定义 GPIO 初始化**/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;	//SCL&&SDA
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;				//普通输出
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;				//开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;			//设置2MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;				//引脚上拉
    GPIO_Init(GPIOB, &GPIO_InitStructure);						//初始化IO

    IIC_SCL = 1;												//上拉 SCL 
	IIC_SDA = 1;												//上拉 SDA
}
/************************************ IIC.h 改动 ****************************************/
/*********************************
**	引脚选择:					**
**			SCL ---> PXx		**
**			SDA ---> PXx		**
**********************************/


/************************* IO方向设置(GPIOx * 2) ****************************/

/**		PXx输入模式		**/
#define SDA_IN()  {GPIOB -> MODER &=~(3 << (13*2));GPIOB -> MODER |= 0 << 13*2;}
/**		PXx输出模式		**/
#define SDA_OUT() {GPIOB -> MODER &=~(3 << (13*2));GPIOB -> MODER |= 1 << 13*2;}



/*************************** IO操作函数 *************************/
#define 	IIC_SCL    			PBout(12) 			//SCL 输出
#define 	IIC_SDA    			PBout(13) 			//SDA 输出	 
#define 	Read_SDA   			PBin (13)  			//SDA 输入


5.搭配主函数与串口函数打印,结果如下:

int main(void)
{
    SystemInit();				//系统初始化
	
    delay_init();				//延时初始
	
    USART1_init(115200);    	//初始化串口波特率为115200
	
	BH1750_Init();				// BH1750初始化

	printf("Test\r\n");

    while (1)
    {
		printf("光照强度:%f\r\n",Light_Intensity());
		delay_ms(500);
    }
}


结果不稳定是因为为了演示效果,我一直在晃动模块,调整角度。

有问题的可以私信或者留言评论区,想要工程代码可以留联系邮箱,我发一份就好了

有关STM32 驱动 GY-302 光照传感器 BH1750 模块(软件IIC与硬件IIC驱动)的更多相关文章

  1. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  2. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  3. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  4. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

  5. ruby - 运行测试时静音 Chrome 驱动程序控制台输出 - 2

    我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_

  6. node.js - 从未编写过任何自动化测试,我应该如何开始行为驱动开发? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。多年来,我一直在使用多种语言进行编程,并且认为自己总体上相当擅长。但是,我从未编写过任何自动化测试:没有单元测试,没有TDD,没有BDD,什么都没有。我已经尝试开始为我的项目编写适当的测试套件。我可以看到在进行任何更改后能够自动测试项目中所有代码的理论值(value)。我可以看到像RSpec和Mocha这样的测试框架应该如何使设置和运行所述测试变得相当容易

  7. ruby-on-rails - 在 Rails/Capybara/Poltergeist 规范中使用 url_for 将驱动程序发送到 example.com 而不是应用程序 - 2

    如果我在功能规范中调用url_for,它会返回一个以http://www.example.com/开头的绝对URL.Capybara会很乐意尝试加载该站点上的页面,但这与我的应用程序无关。以下是重现该问题的最少步骤:从这个Gemfile开始:source'https://rubygems.org'gem"sqlite3"gem"jquery-rails"gem"draper"gem"rails",'4.1.0'gem"therubyracer"gem"uglifier"gem"rspec-rails"gem"capybara"gem"poltergeist"gem"launchy"运行

  8. 驱动开发:内核无痕隐藏自身分析 - 2

    在笔者前面有一篇文章《驱动开发:断链隐藏驱动程序自身》通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的进程隐藏的,总体来说作者的思路是最终寻找到MiProcessLoaderEntry的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。MiProcessLoaderEntry(pDriverObject->DriverSection,1)添加MiProcessLoaderEntry(pDriverObject->DriverSection,

  9. ruby - 事件/观察者驱动的 Ruby on Rails - 2

    我有一个适用于事件/监听器模型的应用程序。发布了几种不同类型的数据(事件),然后许多不同的事情可能需要也可能不需要对该数据(监听器)采取行动。监听器的发生没有特定的顺序,每个监听器将决定是否需要对事件采取行动。Rails应用程序有哪些工具可以完成此任务?我希望自己不必这样做(尽管我可以。这没什么大不了的。)编辑:观察者模式可能是更好的选择 最佳答案 查看EventMachine.它是一个非常流行的Ruby事件处理库。它看起来相当不错,而且很多其他库似乎都在利用它(Cramp)。这是一个很好的介绍:http://rubylearnin

  10. 蓝桥杯 stm32 MCP4017 - 2

    本文代码使用HAL库。文章目录前言一、MCP4017的重要特性二、MCP4017计算RBW阻值三、MCP4017地址四、MCP4017读写函数五、CubeMX创建工程(利用ADC测量MCP4017电压)、对应代码:总结前言一、MCP4017的重要特性蓝桥杯板子上的是MCP4017T-104ELT,如图1。MCP4017是一个可编程电阻,通过写入的数值可以改变电阻的大小。重点在于6引脚(W),5引脚(B&#

随机推荐