【STM32】RTT-Studio中HAL库开发教程六:IIC通信--GZP6877D气压传感器

news2024/9/25 19:22:52

文章目录

    • 一、简介
    • 二、寄存器操作
    • 三、硬件IIC通信
    • 四、模拟IIC通信
    • 五、测试验证

一、简介

  GZP6877D 型压力传感器采用 SOP6 封装形式,倒钩管的设计可以保证安装的密封性。内有封装的压力传感器与信号调理芯片,对传感器的偏移、灵敏度、温漂和非线性进行数字补偿。采用 24 位 ADC,并且调理芯片内置温度传感器,可以输出高精度的压力值和温度值。同时提供 IIC 通讯协议接口,抗干扰能力强。所有测量数据都经过充分校准和温度补偿。

  • 测量范围-100kPa…0~0.5kPa…1000kPa
  • 表压型、气嘴带防脱结构
  • 电源电压: 2.5V~5.5V
  • 适用于无腐蚀性的气体
  • IIC 输出

  I2C 总线使用 SCL 和 SDA 作为信号线,这两根线都通过上拉电阻(典型值 4.7K)连接到 VDD,不通信时都保持为高电平。I2C 设备地址为 0x6D。
  GZP6877D芯片数据手册:https://atta.szlcsc.com/upload/public/pdf


二、寄存器操作

1.寄存器描述
(1)Reg0x06—Reg0x08:压力数据寄存器

(2)Reg0x09—Reg0x0A:温度数据寄存器

(3)Reg0x30:测量命令寄存器
  Bit:<2:0>
   000:单次温度采集模式。
   001:单次传感器压力信号采集模式。(使用此模式之前需要先读取温度,以获取温度校准系数,否则读数不准)
   010:组合采集模式(一次温度采集后立即进行一次传感器压力信号采集)。
   011,休眠工作模式(定期的执行一次组合采集模式,间隔时间由‘sleep_time’决定)
  Sco:<3>:数据采集完成标志位。1–开始数据采集;0–采集结束(休眠工作模式除外)。
  Sleep_time<7:4>:0001:62.5ms, 0010:125ms … 1111: 1s, 0000:无意义。(仅在休眠工作模式下有效)

(4)Reg0xA5:
  Aout_config<7:4>:模拟输出配置(建议保留默认配置)
  LDO_config:内部 LDO 配置。0,配置成 1.8V;1,配置成 3.6V
  Unipolar:0,ADC 原始数据以有符号数格式输出;1: ADC 原始数据以无符号格式输出。
  Data_out_control:0,输出校准数据;1,输出 ADC 原始数据(默认配置为 0)
  Diag_on:0,关闭诊断功能;1,开启诊断功能(默认开启)

(5)Reg0xA6
  Input Swap:在传感器内部交换差分信号极性。
  Gain_P<5:3>:采集传感器信号时 PGA 增益,000:增益=1X。001:增益=2X。010:增益=4X。011:增益=8X。100: 增益=16X。101:增益=32X。110: 增益=64X。111:增益=128X。
  OSR_P<2:0>:采集传感器信号时的过采样,000:1024X, 001:2048X, 010:4096X, 011:8192X,100:256X, 101:512X,110:16384X, 111:32768X。
在这里插入图片描述

2.工作模式说明
(1)组合数据采集模式:设置‘measurement_control’=010 和‘sco’=1 进入组合数据采集模式。
  芯片上电后先后进行一次温度数据采集和一次传感器数据采集,完成后回到待机模式,并自动将‘sco’置 0。在组合采集模式下,“Data_out_control”寄存器必须设置为 0,校准后的温度数据储存在 0x09~0x0A 寄存器,压力数据储存在 0x06~0x08 寄存器。

组合模式读取数据按照如下指令顺序进行操作:

  • 发送指令 0x0A 到 0x30 寄存器进行一次温度采集,一次压力数据采集。
  • 读取 0x30 寄存器地址,若Sco位为0代表采集结束,可以读取数据。或等待延迟10ms。
  • 读取 0x06、0x07、0x08 三个寄存器地址数据构成 24 位 AD 值(压力数据 AD 值),读取 0x09、0x0A 两个寄存器地址数据构成 16 位 AD 值(温度数据 AD 值)
  • 按以下公式换算成实际压力、温度值。

(2)休眠数据采集模式
  设置‘measurement_control’=011 和‘sco’=1 进入休眠数据采集模式。芯片上电后,以一定的时间间隔进行一次温度数据采集和一次传感器数据采集,间隔时间由’sleep_time’ 设置,范围为 62.5ms 到 1s。除非手动将‘sco’置 0,不然不会停止采集。在休眠数据采集模式下‘Data_out_control’必须设置为 0,校准后的温度数据储存在 0x09~0x0A 寄存器,压力数据储存在 0x06~0x08 寄存器。

3.温度和压力值换算公式
(1)最高位为“0”代表正压/正温度:
  压力:Pressure = Pressure_ ADC / k
  温度:Temperature = Temp_ ADC / 256

(2)最高位为“1”代表负压/负温度:
  压力:Pressure = (Pressure_ADC - 16777216) / k
  温度:Temperature = (Temp_ADC - 65536) / 256

PS:传感器校准后的输出可视为当前实际压力值(±1%Span);传感器校准后的输出:单位 Pa(默认),若要显示其他单位,可在换算公式里输入相应的系数进行换算;
关于上述压力 ADC 换算公式中 k 值的选取可参照下表:
在这里插入图片描述
  量程 P 为测量区间的最大值,比如,测量-30~80kpa,P=80kpa,k 值为 64。


三、硬件IIC通信

一般硬件IIC的通信速率更快,更简单,大部分可以选择使用硬件IIC,如果由于硬件IIC不够使用,可以使用软件IIC进行替代。

1.初始化配置
(1)需要先打开图形界面配置的I2C驱动:
在这里插入图片描述

(2)在board.h中添加一下代码进行初始化。

/** if you want to use i2c bus(soft simulate) you can use the following instructions.
 *
 * STEP 1, open i2c driver framework(soft simulate) support in the RT-Thread Settings file
 *
 * STEP 2, define macro related to the i2c bus
 *                 such as     #define BSP_USING_I2C1
 *
 * STEP 3, according to the corresponding pin of i2c port, modify the related i2c port and pin information
 *                 such as     #define BSP_I2C1_SCL_PIN    GET_PIN(port, pin)   ->   GET_PIN(C, 11)
 *                             #define BSP_I2C1_SDA_PIN    GET_PIN(port, pin)   ->   GET_PIN(C, 12)
 */

/*#define BSP_USING_I2C1*/
#ifdef BSP_USING_I2C1
#define BSP_I2C1_SCL_PIN    GET_PIN(port, pin)
#define BSP_I2C1_SDA_PIN    GET_PIN(port, pin)
#endif

#define BSP_USING_I2C2
#ifdef BSP_USING_I2C2
#define BSP_I2C2_SCL_PIN    GET_PIN(F, 1)
#define BSP_I2C2_SDA_PIN    GET_PIN(F, 0)
#endif

(3)在board.c中添加:void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)来初始化IIC。

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (hi2c->Instance == I2C2)
    {
        __HAL_RCC_GPIOF_CLK_ENABLE();
        /**I2C2 GPIO Configuration
         PF0     ------> I2C2_SDA
         PF1     ------> I2C2_SCL
         */
        GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
        HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

        /* Peripheral clock enable */
        __HAL_RCC_I2C2_CLK_ENABLE();
    }
}

(4)在是他们2fxxx_hal_conf.h中添加如下的宏定义
在这里插入图片描述

(5)GZP6877.h文件

#ifndef APPLICATIONS_GZP6877_H_
#define APPLICATIONS_GZP6877_H_

#include <drv_common.h>
#include <stdio.h>

/**====================================================###### 宏定义 ######==================================================*/
#define GZP6877_WRITE_BIT           0x00        // 硬件I2C写标志
#define GZP6877_READ_BIT            0x01        // 硬件I2C读标志

#define GZP6877_SLAVE_ADDR          0x6D        // 设备地址
#define GZP6877_WRITE_ADDR          ((GZP6877_SLAVE_ADDR << 1) | GZP6877_WRITE_BIT)
#define GZP6877_READ_ADDR           ((GZP6877_SLAVE_ADDR << 1) | GZP6877_READ_BIT)

#define GZP6877_DATA_MSB_ADDR       0x06        // 气压高地址
#define GZP6877_DATA_CSB_ADDR       0x07        // 气压中地址
#define GZP6877_DATA_LSB_ADDR       0x08        // 气压低地址
#define GZP6877_TEMP_MSB_ADDR       0x09        // 温度高地址
#define GZP6877_TEMP_LSB_ADDR       0x0A        // 温度低地址
#define GZP6877_CMD_ADDR            0x30        // 测量命令寄存器

#define GZP6877_ONE_TEMP_CMD        0x08        // 单次温度采集模式
#define GZP6877_ONE_PRESS_CMD       0x09        // 单次采集传感器压力信号
#define GZP6877_COMBINED_COM        0x0A        // 组合采集模式

#define GZP6877_PRE_LIMIT           8388606     // 气压界限
#define GZP6877_PRE_ADJUST          16777216    // 气压校准值
#define GZP6877_K_VALUE             8           // 气压K值
#define GZP6877_TEMP_LIMIT          32768       // 温度界限
#define GZP6877_TEMP_ADJUST         65536       // 温度校准值
#define GZP6877_TEMP_K              256         // 温度K值

I2C_HandleTypeDef hi2c2;

extern void GZP6877_I2C2_Init(void);
extern void GZP6859D_ReadSingleModePressureData(uint32_t *pPressure);
extern void GZP6859D_ReadSingleModeTempData(float *pTemperature);
extern void GZP6859D_ReadCombinedModeData(float *pTemperature, uint32_t *pPressure);
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_GZP6877_H_ */

(6)GZP6877.c文件

#include "GZP6877.h"

/**
 * @breif GZP6877的I2C初始化
 */
void GZP6877_I2C2_Init(void)
{
    hi2c2.Instance = I2C2;
    hi2c2.Init.ClockSpeed = 100000;
    hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c2.Init.OwnAddress1 = 0;
    hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c2.Init.OwnAddress2 = 0;
    hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    if (HAL_I2C_Init(&hi2c2) != HAL_OK)
    {
        Error_Handler();
    }
}

/**
 * @brief 单次模式读取压力数据
 * @param pPressure -[out] 压力值,单位为Pa
 */
void GZP6859D_ReadSingleModePressureData(uint32_t *pPressure)
{
    uint8_t cmd = GZP6877_ONE_PRESS_CMD;
    uint8_t result = 0;
    uint8_t pressArr[3] = {0};
    uint32_t press = 0;

    // 进行单次传感器压力信号采集模式
    HAL_I2C_Mem_Write(&hi2c2, GZP6877_WRITE_ADDR, GZP6877_CMD_ADDR, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 10);

    // 采集结束
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_CMD_ADDR, I2C_MEMADD_SIZE_8BIT, &result, 1, 10);
    rt_kprintf("result:0x%02X  ", result);

    // 获取压力数据AD值
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_MSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[0], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[0]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_CSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[1], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[1]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_LSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[2], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[2]);

    // 压力计算公式
    press = ((pressArr[0] << 16) + (pressArr[1] << 8) + pressArr[2]);
    if (press > GZP6877_PRE_LIMIT)
    {
        press = press - GZP6877_PRE_ADJUST;
    }
    // 单位为Pa
    press = press / GZP6877_K_VALUE;
    *pPressure = press;
    rt_kprintf("press:%d Pa\r\n", press);
}

/**
 * @brief 单次模式读取温度数据
 * @param pTemperature -[out] 温度值,单位为℃
 */
void GZP6859D_ReadSingleModeTempData(float *pTemperature)
{
    uint8_t cmd = GZP6877_ONE_TEMP_CMD;
    uint8_t result = 0;
    uint8_t tempArr[3] = {0};
    float temp = 0;

    // 进行单次传感器温度信号采集模式
    HAL_I2C_Mem_Write(&hi2c2, GZP6877_WRITE_ADDR, GZP6877_CMD_ADDR, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 10);

    // 采集结束
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_CMD_ADDR, I2C_MEMADD_SIZE_8BIT, &result, 1, 10);
    rt_kprintf("result:0x%02X  ", result);

    // 获取温度数据AD值
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_TEMP_MSB_ADDR, I2C_MEMADD_SIZE_8BIT, &tempArr[0], 1, 10);
    rt_kprintf("0x%02X  ", tempArr[0]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_TEMP_LSB_ADDR, I2C_MEMADD_SIZE_8BIT, &tempArr[1], 1, 10);
    rt_kprintf("0x%02X  ", tempArr[1]);

    // 温度计算公式
    temp = ((tempArr[0] << 8) + tempArr[1]);
    if (temp > GZP6877_TEMP_LIMIT)
    {
        temp = temp - GZP6877_TEMP_ADJUST;
    }
    // 单位为℃
    temp = temp / GZP6877_TEMP_K;
    *pTemperature = temp;
    printf("temp:%.2f C\r\n", temp);
}

/**
 * @brief 组合模式读取数据
 * @param pTemperature -[out] 温度值
 * @param pPressure -[out] 压力值
 */
void GZP6859D_ReadCombinedModeData(float *pTemperature, uint32_t *pPressure)
{
    uint8_t cmd = GZP6877_COMBINED_COM;
    uint8_t result = 0;
    uint8_t tempArr[4] = {0};
    float temp = 0;
    uint8_t pressArr[4] = {0};
    uint32_t press = 0;

    // 进行组合模式读取数据
    HAL_I2C_Mem_Write(&hi2c2, GZP6877_WRITE_ADDR, GZP6877_SLAVE_ADDR, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 10);

    // 采集结束
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_SLAVE_ADDR, I2C_MEMADD_SIZE_8BIT, &result, 1, 10);
    rt_kprintf("0x%02X  ", result);

    // 获取温度数据AD值
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_TEMP_MSB_ADDR, I2C_MEMADD_SIZE_8BIT, &tempArr[0], 1, 10);
    rt_kprintf("0x%02X  ", tempArr[0]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_TEMP_LSB_ADDR, I2C_MEMADD_SIZE_8BIT, &tempArr[1], 1, 10);
    rt_kprintf("0x%02X  ", tempArr[1]);

    // 温度计算公式
    temp = ((tempArr[0] << 8) + tempArr[1]);
    if (temp > GZP6877_TEMP_LIMIT)
    {
        temp = temp - GZP6877_TEMP_ADJUST;
    }
    // 单位为℃
    temp = temp / GZP6877_TEMP_K;
    *pTemperature = temp;
    printf("temp:%.2f C\r\n", temp);

    // 获取压力数据AD值
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_MSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[0], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[0]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_CSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[1], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[1]);
    HAL_I2C_Mem_Read(&hi2c2, GZP6877_READ_ADDR, GZP6877_DATA_LSB_ADDR, I2C_MEMADD_SIZE_8BIT, &pressArr[2], 1, 10);
    rt_kprintf("0x%02X  ", pressArr[2]);

    // 压力计算公式
    press = ((pressArr[0] << 16) + (pressArr[1] << 8) + pressArr[2]);
    if (press > GZP6877_PRE_LIMIT)
    {
        press = press - GZP6877_PRE_ADJUST;
    }
    // 单位为Pa
    press = press / GZP6877_K_VALUE;
    *pPressure = press;
    rt_kprintf("press:%d Pa\r\n", press);
}

(7)main.c

#include <rtthread.h>
#include <drv_common.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "GZP6877.h"

int main(void)
{
    int count = 1;

    GZP6877_I2C2_Init();

    float temperature;
    uint32_t pressure;
    GZP6859D_ReadCombinedModeData(&temperature, &pressure);  //先以组合模式读取温度,以获取温度校准系数,否则读数不准

    while (count)
    {
        GZP6859D_ReadSingleModePressureData(&pressure);
        GZP6859D_ReadSingleModeTempData(&temperature);
        printf("P:%d Pa T:%.2f C\r\n", pressure, temperature);
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

四、模拟IIC通信

1.gzp6877.c文件

#include "gzp6877d.h"

/*======================================================### 静态函数调用 ###==================================================*/
/**
 * @brief IIC的us延时函数
 * @param us: 延时时间
 */
static void IIC_Delay_us(rt_uint32_t us)
{
    rt_hw_us_delay(us);
}

/**
 * @brief IIC的起始信号
 */
static void IIC_Start(void)
{
    GZP6877_SDA_OUT_MODE;
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SDA_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
}

/**
 * @brief IIC的停止信号
 */
static void IIC_Stop(void)
{
    GZP6877_SDA_OUT_MODE;
    GZP6877_SDA_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
}

/**
 * @brief  IIC等待应答
 * @return 1:等待超时   0:得到应答
 */
static uint8_t IIC_Wait_ACK(void)
{
    uint8_t outTime = 0;

    GZP6877_SDA_INPUT_MODE;
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    while (rt_pin_read(GZP6877_SDA_PIN))
    {
        outTime++;
        if (outTime >= ACK_OUT_TIME)
        {
            IIC_Stop();
            return RT_ERROR;
        }
    }

    GZP6877_SCL_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);

    return RT_EOK;
}

/**
 * @brief 没有超时时间的应答函数
 */
void IIC_ACK(void)
{
    GZP6877_SCL_LOW;
    GZP6877_SDA_OUT_MODE;
    GZP6877_SDA_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
}

/**
 * @brief IIC无应答
 */
void IIC_NACK(void)
{
    GZP6877_SCL_LOW;
    GZP6877_SDA_OUT_MODE;
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SCL_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
}

/**
 * @brief IIC发送一个字节数据
 */
static void IIC_Send_Byte(uint8_t data)
{
    GZP6877_SDA_OUT_MODE;
    GZP6877_SCL_LOW;

    for (int i = 0; i < 8; ++i)
    {
        if (data & (0x01 << (7 - i)))
        {
            GZP6877_SDA_HIGH;
        }
        else
        {
            GZP6877_SDA_LOW;
        }

        IIC_Delay_us(IIC_DELAY_TIME);
        GZP6877_SCL_HIGH;
        IIC_Delay_us(IIC_DELAY_TIME);
        GZP6877_SCL_LOW;
        IIC_Delay_us(IIC_DELAY_TIME);
    }

    GZP6877_SCL_LOW;
    IIC_Delay_us(IIC_DELAY_TIME);
    GZP6877_SDA_HIGH;
    IIC_Delay_us(IIC_DELAY_TIME);
}

/**
 * @brief 接收一个字节的数据
 * @return 返回接收到的数据
 */
static uint8_t IIC_Read_Byte(void)
{
    uint8_t recv = 0;

    GZP6877_SDA_INPUT_MODE;
    for (int i = 0; i < 8; ++i)
    {
        GZP6877_SCL_LOW;
        IIC_Delay_us(IIC_DELAY_TIME);
        GZP6877_SCL_HIGH;
        IIC_Delay_us(IIC_DELAY_TIME / 2);

        recv <<= 1;
        if (rt_pin_read(GZP6877_SDA_PIN))
        {
            recv++;
        }
        IIC_Delay_us(IIC_DELAY_TIME / 2);
    }
    GZP6877_SDA_HIGH;

    return recv;
}

/**
 * @brief GZP6877写入一个字节的数据
 * @return 1: 异常     0:读取正常
 */
static uint8_t GZP6877_Write_One_Byte(uint8_t address, uint8_t data)
{
    uint8_t ack;

    IIC_Start();
    IIC_Send_Byte(GZP6877_WRITE_ADDR);
    ack = IIC_Wait_ACK();
    if (ack == RT_ERROR)
    {
        return RT_ERROR;
    }

    IIC_Send_Byte(address);
    ack = IIC_Wait_ACK();
    if (ack == RT_ERROR)
    {
        return RT_ERROR;
    }

    IIC_Send_Byte(data);
    IIC_Wait_ACK();
    if (ack == RT_ERROR)
    {
        return RT_ERROR;
    }
    IIC_Stop();

    return RT_EOK;
}

/**
 * @brief GZP6877读一个字节的数据
 * @param address:读书数据的地址
 * @return 0:应答错误
 */
static uint8_t GZP6877_Read_One_Byte(uint8_t address)
{
    uint8_t data, ack;

    IIC_Start();
    IIC_Send_Byte(GZP6877_WRITE_ADDR);
    ack = IIC_Wait_ACK();
    if (ack != RT_EOK)
    {
        return 0;
    }

    IIC_Send_Byte(address);
    ack = IIC_Wait_ACK();
    if (ack != RT_EOK)
    {
        return 0;
    }

    IIC_Start();
    IIC_Send_Byte(GZP6877_READ_ADDR);
    ack = IIC_Wait_ACK();
    if (ack != RT_EOK)
    {
        return 0;
    }

    data = IIC_Read_Byte();
    IIC_Stop();

    return data;
}
/*=====================================================#######  END  #######=================================================*/

/*======================================================##### 外部调用 #####==================================================*/
/**
 * @brief IIC的GPIO引脚初始化
 */
void GZP6877_GPIO_Init(void)
{
    GZP6877_SDA_OUT_MODE;
    GZP6877_SCL_OUT_MODE;
    GZP6877_SDA_HIGH;
    GZP6877_SCL_HIGH;
}

/**
 * @brief GZP6877模块等待可读压力和温度
 * @return 0:可读  1:不可读
 */
uint8_t GZP6877_Wait(void)
{
    uint8_t ack, data, status;
    ack = GZP6877_Write_One_Byte(GZP6877_CMD_ADDR, GZP6877_COM);
    if (ack)
    {
        return RT_ERROR;
    }

    data = GZP6877_Read_One_Byte(GZP6877_CMD_ADDR);
    if (!data)
    {
        return RT_ERROR;
    }

    status = data & 0x08;
    if (status)
    {
        rt_thread_delay(20);
    }

    return 0;
}

/**
 * @brief GZP6877模块读取压力
 * @return 返回气压值
 */
int GZP6877_Read_Pressure(void)
{
    int pressure = 0;
    uint8_t pressure_H, pressure_M, pressure_L;

    pressure_H = GZP6877_Read_One_Byte(GZP6877_DATA_HSB_ADDR);
    pressure_M = GZP6877_Read_One_Byte(GZP6877_DATA_MSB_ADDR);
    pressure_L = GZP6877_Read_One_Byte(GZP6877_DATA_LSB_ADDR);

    pressure |= (int)pressure_H << 16;
    pressure |= (int)pressure_M << 8;
    pressure |= (int)pressure_L << 0;

    /* 超过 8388606(0x800000) 为负压值 */
    if (pressure > GZP6877_PRE_LIMIT)
    {
        pressure = (pressure - GZP6877_PRE_ADJUST) / GZP6877_PRE_K;
    }
    else
    {
        pressure /= GZP6877_PRE_K;
    }

    return pressure;
}

/**
 * @brief GZP6877模块读取温度
 * @return 返回温度值
 */
float GZP6877_Read_Temperature(void)
{
    uint8_t temp_H, temp_L;
    float temp = 0;

    temp_H = GZP6877_Read_One_Byte(GZP6877_TEMP_MSB_ADDR);
    temp_L = GZP6877_Read_One_Byte(GZP6877_TEMP_LSB_ADDR);

    temp = (float)(temp_H << 8) + (float)(temp_L << 0);

    if (temp > GZP6877_TEMP_LIMIT)
    {
        temp = (temp - GZP6877_TEMP_ADJUST) / GZP6877_TEMP_K;
    }
    else
    {
        temp /= GZP6877_TEMP_K;
    }

    return temp;
}

/**
 * @brief 打印气压和温度
 */
void GZP6877_Read_Data(void)
{
    int pres = 0;
    float temp = 0;

    pres = GZP6877_Read_Pressure();
    temp = GZP6877_Read_Temperature();

    printf("pres: %d Pa,  temp: %.3f C\n", pres, temp);
}
/*=====================================================#######  END  #######=================================================*/

2.gzp6877.h文件

#ifndef APPLICATIONS_GZP6877D_H_
#define APPLICATIONS_GZP6877D_H_

#include <rtthread.h>
#include <drv_common.h>

/**=====================================================###### 宏定义 ######==================================================*/
#define GZP6877_WRITE_BIT       0x00            // 硬件I2C写标志
#define GZP6877_READ_BIT        0x01            // 硬件I2C读标志

#define GZP6877_SLAVE_ADDR      0x6D            // 设备地址
#define GZP6877_WRITE_ADDR      ((GZP6877_SLAVE_ADDR << 1) | GZP6877_WRITE_BIT)
#define GZP6877_READ_ADDR       ((GZP6877_SLAVE_ADDR << 1) | GZP6877_READ_BIT)

#define GZP6877_COM             0x0A            // 测量一次数据指令
#define GZP6877_DATA_HSB_ADDR   0x06            // 气压高地址
#define GZP6877_DATA_MSB_ADDR   0x07            // 气压中地址
#define GZP6877_DATA_LSB_ADDR   0x08            // 气压低地址
#define GZP6877_TEMP_MSB_ADDR   0x09            // 温度高地址
#define GZP6877_TEMP_LSB_ADDR   0x0A            // 温度低地址
#define GZP6877_CMD_ADDR        0x30            // 测量命令寄存器

#define GZP6877_PRE_LIMIT       8388606         // 气压界限
#define GZP6877_PRE_ADJUST      16777216        // 气压校准值
#define GZP6877_PRE_K           8               // 气压K值
#define GZP6877_TEMP_LIMIT      32786           // 温度界限
#define GZP6877_TEMP_ADJUST     65536           // 温度校准值
#define GZP6877_TEMP_K          256             // 温度K值

#define IIC_DELAY_TIME          60              // IIC延时时间
#define ACK_OUT_TIME            250             // 应答信号超时时间

#define GZP6877_SDA_PIN         GET_PIN(F, 0)   // PF0数据总线
#define GZP6877_SCL_PIN         GET_PIN(F, 1)   // PF1时钟总线

#define GZP6877_SCL_OUT_MODE    rt_pin_mode(GZP6877_SCL_PIN, PIN_MODE_OUTPUT);  // SCL输出模式

#define GZP6877_SDA_OUT_MODE    rt_pin_mode(GZP6877_SDA_PIN, PIN_MODE_OUTPUT);  // SDA输出模式
#define GZP6877_SDA_INPUT_MODE  rt_pin_mode(GZP6877_SDA_PIN, PIN_MODE_INPUT);   // SDA输出模式

#define GZP6877_SDA_HIGH        rt_pin_write(GZP6877_SDA_PIN, PIN_HIGH);        // 设置SDA为高
#define GZP6877_SDA_LOW         rt_pin_write(GZP6877_SDA_PIN, PIN_LOW);         // 设置SDA为高

#define GZP6877_SCL_HIGH        rt_pin_write(GZP6877_SCL_PIN, PIN_HIGH);        // 设置SCL为高
#define GZP6877_SCL_LOW         rt_pin_write(GZP6877_SCL_PIN, PIN_LOW);         // 设置SCL为高
/**====================================================#######  END  #######=================================================*/

/**==================================================##### 函数及变量声明 #####===============================================*/
extern void GZP6877_GPIO_Init(void);                // IIC的GPIO引脚初始化
extern uint8_t GZP6877_Wait(void);                  // GZP6877模块等待可读压力和温度
extern int GZP6877_Read_Pressure(void);             // GZP6877模块读取压力
extern float GZP6877_Read_Temperature(void);       // GZP6877模块读取温度
extern void GZP6877_Read_Data(void);                // 打印气压和温度
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_GZP6877D_H_ */

3.main.c

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "gzp6877d.h"

int main(void)
{
    int count = 1;
    GZP6877_GPIO_Init();

    while (count)
    {
        if (GZP6877_Wait())
        {
            continue;
        }

        GZP6877_Read_Data();

        rt_thread_mdelay(500);
    }

    return RT_EOK;
}

五、测试验证

  通过软件IIC和硬件IIC进行通信,都可以实现温度和气压的读取,实现的效果如下所示:
在这里插入图片描述
在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2077659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(二十)Flink Paimon

数据湖、湖仓一体是当前大数据领域技术发展的重要趋势。近几年开源数据湖技术如 Apache Hudi、Apache Iceberg、Apache Paimon、DeltaLake 等不断涌现,基于湖仓一体架构的统一元数据管理、数据治理也越来越受到关注。从传统数仓到数据湖、湖仓一体架构,从流批一体计算到基于数…

【内网渗透】最保姆级的春秋云镜Brute4Road打靶笔记

目录 ①fcan扫外网&#xff0c;redis主从复制拿shell&#xff0c;suid提权 ②wget下载frp&#xff0c;内网穿透&#xff0c;fscan扫内网 ③wpscan扫wordpress RCE写webshell ④配置文件泄露&#xff0c;连数据库读敏感信息 ⑤fscan爆破MSSQLSERVER密码&#xff0c;MDUT连接…

MyBatis 通用 Mapper5 来了

通用 Mapper 是一个发布于 2014 年的开源项目&#xff0c;是第一个在 MyBatis 中实现了通用 DAO 的开源框架&#xff0c;发布距今接近 10 年&#xff0c;仍然有一部分开发者在使用&#xff0c;项目仍然在维护&#xff0c;主要通过开发者提交 PR 来进行维护。 基于 JDK 17 Jaka…

【Linux】05.Linux 下的编辑器——vim

vim是一个多模式的编辑器。 一、vim的模式 我们主要介绍 vim 的三种模式&#xff0c;分别是命令模式、插入模式和底行模式&#xff0c;各模式的功能区分如下&#xff1a; 命令模式&#xff08;Normal mode&#xff09; 控制屏幕光标的移动&#xff0c;字符、字或行的删除&am…

NOsql数据库Redis

关系型数据库和 NoSQL 数据库 Redis的源码安装 编译 vim /etc/redis/6379.conf bind改成* -::* 关闭protected模式 Redis 主从复制 主从同步过程 环境配置 redis-node1 master redis-node2 slave1 redis-node3 slave2 slave中配置 vim /etc/redis/6379.conf master中操…

设计模式25-访问器模式

设计模式25-访问器模式 访问者模式的动机定义与结构定义结构访问者模式概述结构图解析总结 C代码推导原理代码实例代码 优缺点应用总结 访问者模式的动机 在软件构建过程中&#xff0c;由于需求的改变&#xff0c;某些类层次结构中常常需要增加新的行为方法。此时如果直接在基…

169页PPT丨城投公司战略规划之产业投资商规划

一、规划背景与目标随着经济的不断发展和市场环境的变化&#xff0c;麦肯新城投公司致力于从传统的城市建设主体向产业投资商转型&#xff0c;以实现可持续发展和提升区域经济活力。 目标&#xff1a;在未来5年内&#xff0c;成为具有核心竞争力和广泛影响力的产业投资商&…

Hadoop: Mapreduce了解

目录 1.MapReduce概述 2.MapReduce的基本工作原理 2.1.Map阶段 2.1.1.Map源码解析 2.1.2.map端代码总结 2.2.Shuffle and Sort阶段 2.3.Reduce阶段 2.3.1.Reduce源码解析 2.3.2.Reduce端源码总结 3.数据流与任务执行 3.1.数据输入与输出格式 3.1.1.TextInputFormat…

【公共科目】简单概率问题

问&#xff1a;在人寿保险事业中&#xff0c;很重视某一年龄段的投保人的死亡率&#xff0c;计入每个投保人能活到65的概率为0.6&#xff0c;问3个投保人中有2个人活到65岁的概率是多少? A 0.126 B 0.388 C 0.432【正确答案】 D 0.534 首先看到这道题&#xff0c;很感慨能活到…

redis分布式是如何实现的(面试版)

需要结合项目中的业务进行回答&#xff0c;通常情况下&#xff0c;分布式锁使用的场景&#xff1a; 集群情况下的定时任务、抢单、幂等性场景。 下面先来看一个抢卷场景&#xff1a; 以下情况会出现超卖情况&#xff1a; 因为线程会交替执行&#xff0c;所以线程查询优惠价的…

学习C语言(18)

整理今天的学习内容 1.strcmp的使用和模拟实现 strcmp是用来比较字符串的大小的 比较方式&#xff1a;比较两个字符串中对应位置上字符ASCII码值的⼤小 第⼀个字符串大于第二个字符串&#xff0c;则返回⼤于0的数字 第⼀个字符串等于第二个字符串&#xff0c;则返回0 第⼀…

Mac M1 Max配置torch-geometric等深度学习库

前提&#xff1a;此电脑中已经安装好了Anaconda环境 &#xff08;一&#xff09;查看创建的虚拟环境中torch的版本 import torch torch.__version__&#xff08;二&#xff09;针对安装的 torch 版本&#xff0c;去官网下载torch-geometric 依赖的对应版本 torch-sparse、tor…

OpenMax算法详解:深度学习中的高效开集识别技术

OpenMax算法详解&#xff1a;深度学习中的高效开集识别技术 在深度学习领域&#xff0c;模型的识别能力往往受限于其训练数据集的范畴。传统的分类模型&#xff0c;如卷积神经网络&#xff08;CNN&#xff09;或循环神经网络&#xff08;RNN&#xff09;&#xff0c;通常被设计…

《大道平渊》· 拾捌 —— 证明自己,本身就毫无意义。

《大道平渊》 我在任何时候都会保持最轻松的状态&#xff0c;选择最舒适的态度和动作。 我在与人交谈时&#xff0c;无论何时何地&#xff0c;都会像在家里躺着和挚友交谈一样轻松。 我总是悠然自若&#xff0c;因为我深知自己的过度表现&#xff0c;只会给人留下怪异的印象。…

django学习入门系列之第十点《django的快速上手》

文章目录 快速上手app注册【settings.py】编写URL和视图的对应关系【urls.py】编写视图函数【views.py】启动django启动网页静态文件路径修改 往期回顾 快速上手 app注册【settings.py】 注意&#xff0c;创建了不代表注册了 操作&#xff0c;在settings里面添加这个 # 一…

002、架构_详解(重点)

GoldenDB 分布式数据库框架 DN和RDB增加了备节点;引入新模块CM,且GTM、MDS、PM、CM都增加备节点;MDS、PM、CM、RDB被统一在了管理节点之中;GTM和MDS间多了一条连线,因为GTM的切换由MDS把控;初步系统架构mysqld:一般称为DB节点,负责单个节点的数据处理; dbproxy:一般…

新手该如何选择与小程序定位相关的关键词

关键词的优化是提高小程序排名的关键步骤之一&#xff0c;所以如何选择与小程序定位相关的关键词是一个很重要的过程&#xff0c;需要考虑多个因素以确保关键词既符合小程序的业务特性&#xff0c;又能吸引目标用户。以下是一些具体的步骤和建议&#xff1a; 1. 深入了解小程序…

<WPF> logic tree 和 visual tree 到依赖属性和路由事件--烂尾睡觉

logic tree 逻辑树通常与 XAML 文件中的元素直接对应。The logical tree comprises the elements as they are listed in the XAML. 用途&#xff1a; 数据绑定&#xff1a;逻辑树用于数据绑定机制&#xff0c;帮助控件获取其父级的属性或数据上下文。事件处理&…

Telnet详解与应用——从原理到实战模拟

1. 引言 在现代网络管理中&#xff0c;远程访问和控制设备的能力至关重要。Telnet是一种经典的远程访问协议&#xff0c;尽管在安全性方面逐渐被SSH等更现代化的协议取代&#xff0c;但其在早期网络管理中的广泛使用使其成为网络工程师的基本技能之一。本文将深入探讨Telnet的…

Shader 透明相关

&#xff11;、设置深度写入与渲染队列 深度写入 深度写入默认是开启&#xff0c;需要通过渲染状态中的 ZWrite off 指令主动关闭深度写入 当我们把它写在Pass渲染通道中时&#xff0c;它只会影响该Pass 若我们把它写在SubShader语句块中&#xff0c;它将影响其中的所有Pass …