STM32案例学习 GY-39环境监测传感器模块
硬件平台
- 野火STM32F1系列开发板
- 正点STM32F1系列开发板
- STM32F103ZET6核心板
- GY-39环境监测传感器模块
GY-39环境监测传感器模块
- GY-39 是一款低成本,气压,温湿度,光强度传感器模块。工作电压 3-5v,功耗小,安装方便。
- 其工作原理是,MCU 收集各种传感器数据,统一处理,直接输出计算后的结果。此模块,有两种方式读取数据,即**串口 UART(TTL 电平)**或者 IIC(2 线)。串口的波特率有 9600bps 与 115200bps,可配置,有连续,询问输出两种方式,可掉电保存设置。可适应不同的工作环境,与单片机及电脑连接。
- 模块另外可以设置单独传感器芯片工作模式,作为简单传感器模块,MCU 不参与数据处理工作。
- 参考资料网站 https://www.gysensor.cn/air-gy39/
技术参数(传感器精度请参考芯片手册)
模块引脚声明
Pin1 | VCC | 电源+ (3v-5v) |
---|---|---|
Pin2 | CT | 串口UART_TX / IIC_SCL |
Pin3 | DR | 串口UART_RX / IIC_SDA |
Pin4 | GND | 电源地 |
Pin5 | NC | 保留,不要连接 |
Pin6 | INT | max44009光强芯片中断 S1=0(接GND时启用) |
Pin7 | SDA | 芯片数据线S1=0(接GND时启用) |
Pin8 | SCL | 芯片时钟线S1=0(接GND时启用) |
PinA | S0 | 串口/MCU_IIC模式选择,接地为MCU_IIC模式 |
PinB | S1 | 仅使用传感器芯片选择 |
模块通信协议描述
-
串口通信
(1) 串口通信参数(默认波特率值 9600bps,可通过软件设定)
波特率:9600 bps 校验位:N 数据位:8 停止位:1
波特率:115200 bps 校验位:N 数据位:8 停止位:1
(2) 模块输出格式,每帧包含 8-13 个字节(十六进制):
① .Byte0: 0x5A 帧头标志
②. Byte1: 0x5A 帧头标志
③. Byte2: 0x15 本帧数据类型(参考含义说明)
④. Byte3: 0x04 数据量
⑤. Byte4: 0x00~0xFF 数据前高 8 位
⑤. Byte5: 0x00~0xFF 数据前低 8 位
⑥. Byte6: 0x00~0xFF 数据后高 8 位
⑦. Byte7: 0x00~0xFF 数据后低 8 位
⑧. Byte8: 0x00~0xFF 校验和(前面数据累加和,仅留低 8 位)(3) Byte2 代表的含义说明
Byte2 0x15 0x45 0x55 说明 光照强度 温度,湿度,气压,海拔 IIC地址 (4) 数据计算方法
①光照强度计算方法(当 Byte2=0x15 时,数据:Byte4~Byte7) :
Lux=(前高8位<<24) | (前低8位<<16) | (后高8位<<8) | 后低8位 单位lux
例:一帧数据<5A- 5A- 15 -04- 00 -00- FE- 40- 0B >
Lux=(0x00<<24)|(0x00<<16)|(0xFE<<8)|0x40
Lux=Lux/100 =650.88 (lux)
②温度、气压、湿度、海拔,计算方法(当 Byte2=0x45 时):
温度:Byte4~Byte5
T=(高 8 位<<8)|低 8 位
T=T/100 单位℃
气压:Byte6~Byte9
P=(前高 8 位<<24) | (前低 8 位<<16) | (后高 8 位<<8) | 后低 8 位
P=P/100 单位 pa
湿度:Byte10~Byte11
Hum=(高 8 位<<8)|低 8 位
Hum=Hum/100 百分制
海拔:Byte12~Byte13
H=(高 8 位<<8)|低 8 位 单位 m
例:一帧数据< 5A -5A -45 -0A -0B -2D -00 -97 -C4 -3F -12- 77 -00- 9C- FA >
T=(0x0B<<8)|0x2D=2861
温度 T=2861/100=28.61 (℃)
P=(0x00<<24)|(0x97<<16)|(C4<<8)|3F=9946175
气压 P=9946175/100=99461.75 (pa)
Hum=(0x12<<8)| 77=4727
湿度 Hum=4727/100=47.27 (%)
海拔 H=(0x00<<8)|0x9c=156 (m)
(5) 命令字节,由外部控制器发送至 GY-39 模块(十六进制)
2. IIC通信
硬件测试
接线示意图
代码
#include "main.h"void I2C_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* 使能与 I2C有关的时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE ); /* PC3-I2C_SCL、PC5-I2C_SDA*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6| GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); SCL_H;SDA_H;
}
void delay_1us(uint8_t x)//粗略延时,iic_40K
{uint8_t i=20;x=i*x;while(x--);
}
IIC起始函数//
/*
IIC起始:当SCL处于高电平期间,SDA由高电平变成低电平出现一个下降沿,然后SCL拉低
*/
uint8_t I2C_Start(void)
{SDA_H; delay_1us(5); //延时保证时钟频率低于40K,以便从机识别SCL_H;delay_1us(5);//延时保证时钟频率低于40K,以便从机识别if(!SDA_read) return 0;//SDA线为低电平则总线忙,退出SDA_L; //SCL处于高电平的时候,SDA拉低delay_1us(5);if(SDA_read) return 0;//SDA线为高电平则总线出错,退出SCL_L;delay_1us(5);return 1;
}
//**************************************
//IIC停止信号
/*
IIC停止:当SCL处于高电平期间,SDA由低电平变成高电平出现一个上升沿
*/
//**************************************
void I2C_Stop(void)
{SDA_L;SCL_L;delay_1us(5);SCL_H;delay_1us(5);SDA_H;//当SCL处于高电平期间,SDA由低电平变成高电平 //延时
}
//**************************************
//IIC发送应答信号
//入口参数:ack (0:ACK 1:NAK)
/*
应答:当从机接收到数据后,向主机发送一个低电平信号
先准备好SDA电平状态,在SCL高电平时,主机采样SDA
*/
//**************************************
void I2C_SendACK(uint8_t i)
{if(1==i)SDA_H; //准备好SDA电平状态,不应答else SDA_L; //准备好SDA电平状态,应答 SCL_H; //拉高时钟线delay_1us(5); //延时SCL_L ; //拉低时钟线delay_1us(5);
}
///等待从机应答
/*
当本机(主机)发送了一个数据后,等待从机应答
先释放SDA,让从机使用,然后采集SDA状态
*/
/
uint8_t I2C_WaitAck(void) //返回为:=1有ACK,=0无ACK
{uint16_t i=0;SDA_H; //释放SDASCL_H; //SCL拉高进行采样while(SDA_read)//等待SDA拉低{i++; //等待计数if(i==500)//超时跳出循环break;}if(SDA_read)//再次判断SDA是否拉低{SCL_L; return RESET;//从机应答失败,返回0}delay_1us(5);//延时保证时钟频率低于40K,SCL_L;delay_1us(5); //延时保证时钟频率低于40K,return SET;//从机应答成功,返回1
}
//**************************************
//向IIC总线发送一个字节数据
/*
一个字节8bit,当SCL低电平时,准备好SDA,SCL高电平时,从机采样SDA
*/
//**************************************
void I2C_SendByte(uint8_t dat)
{uint8_t i;SCL_L;//SCL拉低,给SDA准备for (i=0; i<8; i++) //8位计数器{if(dat&0x80)//SDA准备SDA_H; else SDA_L;SCL_H; //拉高时钟,给从机采样delay_1us(5); //延时保持IIC时钟频率,也是给从机采样有充足时间SCL_L; //拉低时钟,给SDA准备delay_1us(5); //延时保持IIC时钟频率dat <<= 1; //移出数据的最高位 }
}
//**************************************
//从IIC总线接收一个字节数据
//**************************************
uint8_t I2C_RecvByte()
{uint8_t i;uint8_t dat = 0;SDA_H;//释放SDA,给从机使用delay_1us(1); //延时给从机准备SDA时间 for (i=0; i<8; i++) //8位计数器{ dat <<= 1;SCL_H; //拉高时钟线,采样从机SDAif(SDA_read) //读数据 dat |=0x01; delay_1us(5); //延时保持IIC时钟频率 SCL_L; //拉低时钟线,处理接收到的数据delay_1us(5); //延时给从机准备SDA时间} return dat;
}
//**************************************
//向IIC设备写入一个字节数据
//**************************************
uint8_t Single_WriteI2C_byte(uint8_t Slave_Address,uint8_t REG_Address,uint8_t data)
{if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address); //发送设备地址+写信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_SendByte(REG_Address); //内部寄存器地址,if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_SendByte(data); //内部寄存器数据,if(!I2C_WaitAck()){I2C_Stop(); return RESET;}I2C_Stop(); //发送停止信号return SET;
}
//**************************************
//从IIC设备读取一个字节数据
//**************************************
uint8_t Single_ReadI2C(uint8_t Slave_Address,uint8_t REG_Address,uint8_t *REG_data,uint8_t length)
{if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address); //发送设备地址+写信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;} I2C_SendByte(REG_Address); //发送存储单元地址if(!I2C_WaitAck()){I2C_Stop(); return RESET;} if(I2C_Start()==0) //起始信号{I2C_Stop(); return RESET;} I2C_SendByte(Slave_Address+1); //发送设备地址+读信号if(!I2C_WaitAck()){I2C_Stop(); return RESET;}while(length-1){*REG_data++=I2C_RecvByte(); //读出寄存器数据I2C_SendACK(0); //应答length--;}*REG_data=I2C_RecvByte(); I2C_SendACK(1); //发送停止传输信号I2C_Stop(); //停止信号return SET;
}
#ifndef _GY39_H
#define _GY39_H
#include "main.h"#define SCL_H GPIOB->BSRR = GPIO_Pin_6
#define SCL_L GPIOB->BRR = GPIO_Pin_6#define SDA_H GPIOB->BSRR = GPIO_Pin_7
#define SDA_L GPIOB->BRR = GPIO_Pin_7#define SCL_read GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
#define SDA_read GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)void I2C_GPIO_Config(void);
void I2C_Stop(void);
uint8_t Single_WriteI2C_byte(uint8_t Slave_Address,uint8_t REG_Address,uint8_t data);
uint8_t Single_ReadI2C(uint8_t Slave_Address,uint8_t REG_Address,uint8_t *REG_data,uint8_t length);#endif
/*************************************** Copyright ****************************** * * FileName : main.c * Version : v5.0 * Author : dele * Date : 2023-07-19
*******************************************************************************/
/*
STLink 连接 SWDIO PA13 SWCLK PA14 不要使用这两个端口
*/
//--------------------------------------------------------------------------------------------------
// 包含头文件 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
#include "main.h"//--------------------------------------------------------------------------------------------------
// 定义引用变量 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
float gy_temp,gy_hum;
float aht_temp,aht_hum;//温湿度 //==================================================================================================
// 实现功能: 硬件初始化配置
// 函数说明: Hareware_Iint
// 函数备注:
//--------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
void Hareware_Iint(void)
{delay_init();//延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级USART1_Config(115200);//串口初始化波特率为115200 USART2_Config(115200);//ESP8266 通讯串口2 波特率为115200 printf("串口1配置 [OK] \r\n");printf("串口2配置 [OK] \r\n");LED_Init();//LED端口初始化printf("LED_Init [OK] \r\n");Beep_Init();//蜂鸣器初始化printf("Beep_Init [OK] \r\n");KEY_Init(); //按键初始化printf("KEY_Init [OK] \r\n");LCD_Init(); //液晶屏初始化printf("LCD_Init [OK] \r\n");I2C_GPIO_Config();AHT_I2C_UserConfig();printf("AHT_I2C_UserConfig [OK] \r\n");//TIM1_Int_Init(1999,35999);//定时1s//printf("TIM1_Int_Init [OK] \r\n");W25QXX_Init();//W25QXX初始化printf("W25QXX初始化 [OK] \r\n");POINT_COLOR=RED; GBK_Lib_Init(); printf("硬件GBK字库初始化 [OK] \r\n"); //硬件GBK字库初始化--(如果使用不带字库的液晶屏版本,此处可以屏蔽,不做字库初始化) tp_dev.init();//触摸屏初始化printf("触摸屏初始化 [OK] \r\n");}
//==================================================================================================
// 实现功能: 传感器数据采集
// 函数说明: SensorGet_Data
// 函数备注:
//--------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
typedef struct
{uint32_t Press;uint16_t Temperature;uint16_t Humidity;uint16_t Altitude;
} gy_module;gy_module Bme={0,0,0,0};
//==================================================================================================
// 实现功能: 主函数
// 函数说明: main
// 函数备注:
//--------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
int main(void)
{ Hareware_Iint();printf("Hareware_Iint [OK] \r\n");uint8_t raw_data[13]={0};uint16_t data_16[2]={0};uint32_t Lux;while(1){if(Single_ReadI2C(0xb6,0x04,raw_data,10)){Bme.Temperature=(raw_data[0]<<8)|raw_data[1];data_16[0]=(((uint16_t)raw_data[2])<<8)|raw_data[3];data_16[1]=(((uint16_t)raw_data[4])<<8)|raw_data[5];Bme.Press=(((uint32_t)data_16[0])<<16)|data_16[1];Bme.Humidity=(raw_data[6]<<8)|raw_data[7];Bme.Altitude=(raw_data[8]<<8)|raw_data[9];}if(Single_ReadI2C(0xb6,0x00,raw_data,4))data_16[0]=(((uint16_t)raw_data[0])<<8)|raw_data[1];data_16[1]=(((uint16_t)raw_data[2])<<8)|raw_data[3];Lux=(((uint32_t)data_16[0])<<16)|data_16[1];gy_temp = Bme.Temperature/100;gy_hum = Bme.Humidity/100;printf("Temperature:%.2fDegC \r\n ",(float)Bme.Temperature/100);printf("Prees:%.2fPa \r\n",(float)Bme.Press/100);printf("Humidity: %.2f \r\n ",(float)Bme.Humidity/100);printf("Altitude: %.2f m \r\n ",(float)Bme.Altitude);printf("LightLux: %.2f lux \r\n ",(float)Lux/100); delay_ms(200);}}
测试效果
代码参考链接