STM32 模拟I2C 读取BH1750光照传感器数据
时间:2023-07-23 17:37:00
BH1750是光强传感器,可以测量当前环境下的光强。VCC支持3.3V供电,通过I2C协议与STM32通信。
BH1750可直接通过开发板连接。
测量后,稳定可用的代码。
i2c.c文件内容如下:
/* 配置模拟I2C使用的GPIO*/
/* PC6配置为SDA,PC7配置为SCL */
void I2C_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStr;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_OD;///泄漏输出
GPIO_InitStr.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStr);
/* 给出停止信号,复位I2C总线上所有设备到待机模式*/
GPIO_ResetBits(GPIOC,GPIO_Pin_6);
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
}
/* 主机产生一个起始信号 */
/* SCL高电平时,SDA从高电平跳到低电平,产生起始信号 */
void I2C_Start(void)
{
/* 主机产生起始信号 */
GPIO_SetBits(GPIOC,GPIO_Pin_6);
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
GPIO_ResetBits(GPIOC,GPIO_Pin_6);
/* 主机准备发送数据 */
delay_us(5);
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
}
/* 主机产生一个停止信号 */
/* SCL高电平时,SDA从低电平跳到高电平,产生停止信号 */
void I2C_Stop(void)
{
GPIO_ResetBits(GPIOC,GPIO_Pin_6);
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_6);
}
/* 发送一个主机ACK给从机 */
void I2C_SendACK(void)
{
/* 生成一个主机ACK信号 */
GPIO_ResetBits(GPIOC,GPIO_Pin_6);
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
/* 准备下一次发送 */
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_6);
}
/* 主机发送NCAK信号给从机 */
void I2C_SendNAK(void)
{
/* 生成一个主机NACK信号 */
GPIO_SetBits(GPIOC,GPIO_Pin_6);
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
/* 为下一次发送作准你*/
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
}
/* 主机从机发送ACK信号 */
/* 返回1意味着获得了ACK信号 */
int I2C_WaitACK(void)
{
int ack;
GPIO_SetBits(GPIOC,GPIO_Pin_6);//主机将SDA置高
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_7);//之前SCL现在将是低电平SCL放高后,主机读取SDA的数据
delay_us(5);
ack = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6);//若SDA为了低电平,机发送了一个ACK给主机
GPIO_ResetBits(GPIOC,GPIO_Pin_7)///获取信号后记得SCL低,否则容易出错
if (ack == 0)
{
return 1;
}
else
{
return 0;
}
}
/* STM32(主机)向BH1750(从机)发送字节数据,等待BH1750返回一个ACK */
/* 若函数返回值为1,则表示数据发送成功,BH1750返回了一个ACK给主机;相反,数据发送失败;*/
/* 从最高位发送*/
int I2C_SendByte(unsigned char byte)
{
int i;
for (i = 0; i < 8; i )
{
if (byte & 0x80)
{
GPIO_SetBits(GPIOC,GPIO_Pin_6);
}
else
{
GPIO_ResetBits(GPIOC,GPIO_Pin_6);
}
delay_us(5);
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
if (i == 7)
{
GPIO_SetBits(GPIOC,GPIO_Pin_6);
}
delay_u(5);
byte <<= 1;
}
return I2C_WaitACK();
}
/* STM32(主机)从BH1750读取一个字节的数据 */
/* 读取的第一个bit是最高位 */
unsigned char I2C_ReadByte(void)
{
unsigned char result = 0;
unsigned char data;
int i;
for (i = 0; i < 8; i++)
{
data = 0;
GPIO_SetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
data = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6);
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
delay_us(5);
data <<= (7 - i);
result |= data;
}
return result;
}
bh1750.h文件内容如下:
#define ADDR 0x23 //0100011 ADDR = 'L'
#define BH_WRITEADDR 0x46 //01000110 BH1750写地址
#define BH_READADDR 0x47 //01000111 BH1750读地址
#define BH_POWEROFF 0x00 //0000_0000断电
#define BH_POWERON 0x01 //0000_0001 BH1750上电,等待测量指令
#define BH_RESET 0x07 //0000_0111 重置
#define BH_MODE_H 0x10 //连续H分辨率模式,精度为1x,测量时间为120ms,一般不超过180ms。
#define INTENSITY_WEAK 0
#define INTENSITY_STRONG 1
typedef struct _BH1750_STRUCT
{
volatile int open;
volatile int light_intensity;
} BH1750_STRUCTURE;
extern BH1750_STRUCTURE BH1750_Str;
void BH1750_Init(void);
void vTask_BH1750( void * pvParameters );
bh1750.c文件中的内容如下:
BH1750_STRUCTURE BH1750_Str = {0};
/*BH1750的ADDR引脚接到STM32的PC12上 */
/* 根据参考手册,ADDR为低电平时,BH1750的7位地址为0100011b */
static void BH1750_ADDR_Set(void)
{
GPIO_InitTypeDef GPIO_InitStr;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStr.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStr.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStr.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStr);
GPIO_ResetBits(GPIOC,GPIO_Pin_12);
}
static void BH1750_PowerOn(void)
{
I2C_Start();
I2C_SendByte(BH_WRITEADDR);
I2C_SendByte(BH_POWERON);
I2C_Stop();
}
static void BH1750_PowerOff(void)
{
I2C_Start();
I2C_SendByte(BH_WRITEADDR);
I2C_SendByte(BH_POWEROFF);
I2C_Stop();
}
static void BH1750_Reset(void)
{
I2C_Start();
I2C_SendByte(BH_WRITEADDR);
I2C_SendByte(BH_RESET);
I2C_Stop();
}
/* 设置BH1750的模式 为: 连续H分辨率模式 */
/* 返回0表示设置成功,否则失败 */
static int BH1750_Set_Mode(void)
{
I2C_Start();
if (I2C_SendByte(BH_WRITEADDR) == 0)
{
return 1;
}
if (I2C_SendByte(BH_MODE_H) == 0)
{
return 1;
}
I2C_Stop();
return 0;
}
void BH1750_Init(void)
{
I2C_GPIO_Init();
BH1750_ADDR_Set();
BH1750_PowerOn();
BH1750_Reset();
BH1750_Set_Mode();
}
/* 读取测量结果 */
static unsigned short BH1750_Read_Result(void)
{
unsigned short result = 0;
unsigned char data;
I2C_Start();
if (I2C_SendByte(BH_READADDR) == 0)
{
return 1;
}
data = I2C_ReadByte();
result = data;
result <<= 8;
I2C_SendACK();
data = I2C_ReadByte();
result += data;
I2C_SendNAK();
I2C_Stop();
result /= 1.2;
return result;
}
/* 业务处理*/
void vTask_BH1750( void * pvParameters )
{
unsigned int result;
#if LOG_OPEN
char s[20];
#endif
while (1)
{
if ( BH1750_Str.open != OPEN )
{
taskYIELD();
continue;
}
result = 0;
result = BH1750_Read_Result();
if (result < 100) //
{
if ( BH1750_Str.light_intensity != INTENSITY_WEAK )
{
BH1750_Str.light_intensity = INTENSITY_WEAK;
}
}
else //
{
if ( BH1750_Str.light_intensity != INTENSITY_STRONG )
{
BH1750_Str.light_intensity = INTENSITY_STRONG;
}
}
#if LOG_OPEN
my_itoa(result,s);
Usart_SendString(USART1,"BH1750 Result is ");
Usart_SendString(USART1,s);
vTaskDelay(500/portTICK_RATE_MS);//防止频繁打印,冲死串口
#endif
vTaskDelay(100/portTICK_RATE_MS);//加一个延时,没必要太勤劳
// taskYIELD();
}
}
void BH1750_Close(void)
{
BH1750_Str.open = CLOSE;
BH1750_Str.light_intensity = INTENSITY_STRONG;
BH1750_PowerOff();
}