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);
}
}
结果不稳定是因为为了演示效果,我一直在晃动模块,调整角度。
有问题的可以私信或者留言评论区,想要工程代码可以留联系邮箱,我发一份就好了