STM32案例学习:GY-39环境监测传感器模块详解

STM32案例学习 GY-39环境监测传感器模块

硬件平台

  1. 野火STM32F1系列开发板
  2. 正点STM32F1系列开发板
  3. STM32F103ZET6核心板
  4. GY-39环境监测传感器模块
    STM32产品

GY-39环境监测传感器模块

  1. GY-39 是一款低成本,气压,温湿度,光强度传感器模块。工作电压 3-5v,功耗小,安装方便。
  2. 其工作原理是,MCU 收集各种传感器数据,统一处理,直接输出计算后的结果。此模块,有两种方式读取数据,即**串口 UART(TTL 电平)**或者 IIC(2 线)。串口的波特率有 9600bps 与 115200bps,可配置,有连续,询问输出两种方式,可掉电保存设置。可适应不同的工作环境,与单片机及电脑连接。
  3. 模块另外可以设置单独传感器芯片工作模式,作为简单传感器模块,MCU 不参与数据处理工作
  4. 参考资料网站 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. 串口通信
    (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;	        //释放SDA
	SCL_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;                //拉高时钟线,采样从机SDA
     
		  if(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);
        
    }


}

测试效果

代码参考链接

物联沃分享整理
物联沃-IOTWORD物联网 » STM32案例学习:GY-39环境监测传感器模块详解

发表评论