STM32 驱动 GY-302 BH1750 光照传感器模块:软件IIC与硬件IIC双驱动方式

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.引脚接线说明

VCC 5V~3.3V
GND GND
SCL PB6
SDA PB7
ADDR VCC/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分辨率模式2 0001_0001 在0.5lx的分辨率下开始测量。测量时间通常为120ms。
连续L分辨率模式 0001_0011 以4lx的分辨率开始测量。测量时间通常为16ms。
一次H分辨率模式 0010_0000 以1lx的分辨率开始测量。测量时间通常为120ms。测量后自动设置为关机模式。
一次H分辨率模式2 0010_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);
    }
}


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

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

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 驱动 GY-302 BH1750 光照传感器模块:软件IIC与硬件IIC双驱动方式

发表评论