RA4M2开发IOT(10)----集成LPS22DF气压计

发布于:2025-06-26 ⋅ 阅读:(15) ⋅ 点赞:(0)

概述

本篇文章将延续现有 “动态显示 MEMS 数据” 的框架,在同一条 I²C 总线上新增 LPS22DF 数字气压计。
项目将具备 惯性 + 气压 的完整环境感知能力,并且借助涂鸦平台可快速把本地大气数据同步到云端,为室内气候监测、爬山/无人机高度预警等场景奠定基础。

最近在瑞萨RA的课程,需要样片的可以加qun申请:925643491。

视频教学

https://www.bilibili.com/video/BV1DjNmzyEuV

RA4M2开发IOT(10)----集成LPS22DF气压计

样品申请

https://www.wjx.top/vm/rCrkUrz.aspx

硬件准备

首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。
主控为R7FA4M2AD3CFL#AA0

在这里插入图片描述
同时添加RA4M2_IOT扩展版。

在这里插入图片描述

参考程序

https://github.com/CoreMaker-lab/RA4M2_IOT

https://gitee.com/CoreMaker/RA4M2_IOT

产品特性

LPS22DF是一款超紧凑型压阻绝对压力传感器,可用作数字输出气压计。LPS22DF相比前代产品具有更低的功耗和更小的压力噪声。

该器件包含传感元件和IC接口,该接口通过I²C、MIPI I3CSM或SPI接口实现传感元件与应用的通信,同时该器件也支持用于数据接口的广泛Vdd IO。检测绝对压力的传感元件由悬浮膜组成,采用ST开发的专门工艺进行制造。

LPS22DF采用全压塑孔LGA封装(HLGA)。可保证在-40 °C到+85 °C的温度范围都能工作。封装上有开孔,以便外部压力到达传感元件。

260-1260 hPa 的绝对压力范围,适用于多种气压应用。 最低电流消耗可达 1.7 μA,适合低功耗设备。 压力精度达 0.2 hPa,并具备 0.34 Pa 的低噪声和 0.45 Pa/°C 的温度补偿偏移。

通信模式

对于LPS22DF,可以使用IIC进行通讯。
最小系统图如下所示。

在这里插入图片描述

本文使用的板子原理图如下所示。

在这里插入图片描述

接入IOT板PCB如下所示。

在这里插入图片描述

在这里插入图片描述

速率

该模块支持的I2C速度为快速模式1M。

在这里插入图片描述

IIC属性配置

查看手册,可以得知LPS22DF的IIC地址为“1011100” 或者 “1011101”,即0x5C或0x5D。

在这里插入图片描述

CS片选配置

LPS22DF可以通过CS管脚进行IIC或者SPI通讯切换

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

SA0地址设置

通过设置SA0管脚的高低电平可以改变模块的地址。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

配置中断

这里的中断口对应P402。

在这里插入图片描述
在这里插入图片描述
在“New Stack”下选择Input > External IRQ (r_icu)。

在这里插入图片描述

模块配置如下所示。
● Name:g_external_irq4,这是该外部中断的名称。
● Channel:选择了4通道。
● Trigger:触发方式设置为Rising(上升沿触发),即信号上升时触发中断。
● Digital Filtering:未启用数字滤波(Not Supported)。
● Digital Filtering Sample Clock:由于数字滤波未启用,因此该项也未支持。
● Callback:指定了回调函数external_irq4_callback。当中断触发时,将调用此函数处理具体逻辑。
● Pin Interrupt Priority:设置为Priority 2,表示该中断的优先级为2。
● IRQ06:映射到引脚P402,即该中断信号通过引脚P000触发。

在这里插入图片描述

配置中断脚。

在这里插入图片描述

中断回调函数

● external_irq4_callback函数是外部中断的回调函数,当中断触发时,icu_irq_isr中断服务程序会调用此函数。
● lps22df_irq_flag变量在每次中断时赋值为1。

bool lps22df_irq_flag =0;
/* Called from icu_irq_isr */
void external_irq4_callback (external_irq_callback_args_t * p_args)
{
    (void) p_args;
    lps22df_irq_flag = 1;
}

在这里插入图片描述

使能中断

在app_peripheral_init添加开启中断和初始化。

    // 打开外部中断通道(用于接收 LPS22DF 的中断输出)
    fsp_err_t err = R_ICU_ExternalIrqOpen(&g_external_irq4_ctrl, &g_external_irq4_cfg);
    assert(FSP_SUCCESS == err);

    err = R_ICU_ExternalIrqEnable(&g_external_irq4_ctrl);
    assert(FSP_SUCCESS == err);

在这里插入图片描述

管脚初始化

使能SA0为低电平,配置模块地址。

在这里插入图片描述

在app_peripheral_init中添加LPS22DF的初始化。

    //LPS22DF CS->1
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_05_PIN_00, BSP_IO_LEVEL_HIGH);    
    //LPS22DF SA0->0
    R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_13, BSP_IO_LEVEL_LOW);   

在这里插入图片描述

参考程序

https://github.com/STMicroelectronics/lps22df-pid/

把对应驱动包导入到src文件夹。

在这里插入图片描述

添加头文件。

#include "lps22df_reg.h"

在这里插入图片描述

添加预设定义。

/* Extern variables ----------------------------------------------------------*/

/* Private functions ---------------------------------------------------------*/
/*
 *   WARNING:
 *   Functions declare in this section are defined at the end of this file
 *   and are strictly related to the hardware platform used.
 *
 */
static int32_t platform_write_lps22df(void *handle, uint8_t reg, const uint8_t *bufp,
                              uint16_t len);
static int32_t platform_read_lps22df(void *handle, uint8_t reg, uint8_t *bufp,
                             uint16_t len);
static void tx_com_lps22df( uint8_t *tx_buffer, uint16_t len );
static void platform_delay_lps22df(uint32_t ms);
static void platform_init_lps22df(void);
stmdev_ctx_t            dev_ctx_LPS22DF; // 设备上下文

在这里插入图片描述
在最下方添加LPS22DF的IIC读写函数。

/*
 * @brief  Write generic device register (platform dependent)
 *
 * @param  handle    customizable argument. In this examples is used in
 *                   order to select the correct sensor bus handler.
 * @param  reg       register to write
 * @param  bufp      pointer to data to write in register reg
 * @param  len       number of consecutive register to write
 *
 */
static int32_t platform_write_lps22df(void *handle, uint8_t reg, const uint8_t *bufp,uint16_t len)
{
    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, 0x5C, I2C_MASTER_ADDR_MODE_7BIT);
    assert(FSP_SUCCESS == err);
    // 创建一个足够大的缓冲区来包含寄存器地址和数据
    uint8_t data[len + 1];
    data[0] = reg; // 将寄存器地址放在数据的开始
    memcpy(&data[1], bufp, len); // 复制数据到缓冲区

    err = R_SCI_I2C_Write(&g_i2c2_ctrl, data, len+1, true);
    assert(FSP_SUCCESS == err);
    /* Since there is nothing else to do, block until Callback triggers*/
    //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
    while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
    {
        R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
        timeout_ms--;
        }
    if (I2C_MASTER_EVENT_ABORTED == i2c_event)
    {
        __BKPT(0);
    }
    /* Read data back from the I2C slave */
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    timeout_ms           = 100000;
    return 0;
}


/*
 * @brief  Read generic device register (platform dependent)
 *
 * @param  handle    customizable argument. In this examples is used in
 *                   order to select the correct sensor bus handler.
 * @param  reg       register to read
 * @param  bufp      pointer to buffer that store the data read
 * @param  len       number of consecutive register to read
 *
 */
static int32_t platform_read_lps22df(void *handle, uint8_t reg, uint8_t *bufp,uint16_t len)
{
    R_SCI_I2C_SlaveAddressSet(&g_i2c2_ctrl, 0x5C, I2C_MASTER_ADDR_MODE_7BIT);
    assert(FSP_SUCCESS == err);
    err = R_SCI_I2C_Write(&g_i2c2_ctrl, &reg, 1, true);
    assert(FSP_SUCCESS == err);
    /* Since there is nothing else to do, block until Callback triggers*/
    //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
    while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
    {
        R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
        timeout_ms--;
        }
    if (I2C_MASTER_EVENT_ABORTED == i2c_event)
    {
        __BKPT(0);
        }
    /* Read data back from the I2C slave */
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    timeout_ms           = 100000;

    /* Read data from I2C slave */
    err = R_SCI_I2C_Read(&g_i2c2_ctrl, bufp, len, false);
    assert(FSP_SUCCESS == err);
    while ((I2C_MASTER_EVENT_RX_COMPLETE != i2c_event) && timeout_ms)
    {
        R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
        timeout_ms--;
    }
    if (I2C_MASTER_EVENT_ABORTED == i2c_event)
    {
        __BKPT(0);
    }

    i2c_event = I2C_MASTER_EVENT_ABORTED;
    timeout_ms           = 100000;
  return 0;
}


/*
 * @brief  platform specific delay (platform dependent)
 *
 * @param  ms        delay in ms
 *
 */
static void platform_delay_lps22df(uint32_t ms)
{
    R_BSP_SoftwareDelay(ms, BSP_DELAY_UNITS_MILLISECONDS);
}

在这里插入图片描述

LPS22DF初始化

sensor_lps22df_init函数负责把 ST LPS22DF 气压/温度传感器接入到现有 I²C 总线,并配置为低噪声测量模式 。

/* ---------------------------------------------------------------------------
 * @brief  初始化 LPS22DF 数字气压计(气压 + 片内温度)
 *         - 共用 I²C 总线(SENSOR_BUS)
 *         - 配置为 10 Hz / 256 次平均 / ODR÷9 低通
 *         - 打开 DRDY 中断,MCU 可用外部中断捕获新数据
 * --------------------------------------------------------------------------*/
static void sensor_lps22df_init(void)
{
    /* 1. 局部缓冲/结构体 -------------------------------------------------- */
    lps22df_pin_int_route_t int_route;     // 中断路由寄存器镜像
    lps22df_bus_mode_t      bus_mode;      // 接口&滤波选项
    lps22df_id_t            id;            // WHO_AM_I 读取结果
    lps22df_md_t            md;            // 模式配置
    int                     ret;           // ST 驱动函数返回值

    /* 2. 绑定底层读/写/延时 ------------------------------------------------ */
    dev_ctx_LPS22DF.write_reg = platform_write_lps22df;  // I²C 写函数
    dev_ctx_LPS22DF.read_reg  = platform_read_lps22df;   // I²C 读函数
    dev_ctx_LPS22DF.mdelay    = platform_delay_lps22df;  // 毫秒延时
    dev_ctx_LPS22DF.handle    = &SENSOR_BUS;             // I²C 句柄

    /* 3. 读取并校验设备 ID ------------------------------------------------- */
    lps22df_id_get(&dev_ctx_LPS22DF, &id);
    printf("LPS22DF_ID=0x%x, whoamI=0x%x\r\n", LPS22DF_ID, id.whoami);
    if (id.whoami != LPS22DF_ID)   // 若连线或地址错误,停在此处
        while (1);

    /* 4. 先 Boot -> 再软复位 ------------------------------------------------ */
    ret = lps22df_init_set(&dev_ctx_LPS22DF, LPS22DF_BOOT);   if (ret) while (1);
    ret = lps22df_init_set(&dev_ctx_LPS22DF, LPS22DF_RESET);  if (ret) while (1);

    /* 5. 启用驱动推荐配置:BDU=1 / IF_INC=1 ------------------------------- */
    lps22df_init_set(&dev_ctx_LPS22DF, LPS22DF_DRV_RDY);

    /* 6. 选择接口 + 滤波模式 ---------------------------------------------- */
    bus_mode.filter    = LPS22DF_FILTER_AUTO;  // 写寄存器自动关 LPF,之后再恢复
    bus_mode.interface = LPS22DF_SEL_BY_HW;    // 由 SEL 脚决定 I²C / SPI
    lps22df_bus_mode_set(&dev_ctx_LPS22DF, &bus_mode);

    /* 7. 设置测量输出速率 & 滤波/平均参数 --------------------------------- */
    md.odr = LPS22DF_10Hz;            // ODR = 10 Hz
    md.avg = LPS22DF_256_AVG;         // 256 次平均,降低噪声
    md.lpf = LPS22DF_LPF_ODR_DIV_9;   // 低通截止 ≈ 1.1 Hz
    lps22df_mode_set(&dev_ctx_LPS22DF, &md);

    /* 8. 使能气压/温度 DRDY 中断 ------------------------------------------- */
    lps22df_pin_int_route_get(&dev_ctx_LPS22DF, &int_route); // 读默认
    int_route.drdy_pres = PROPERTY_ENABLE;   // 数据就绪 → INT/DRDY 脚
    lps22df_pin_int_route_set(&dev_ctx_LPS22DF, &int_route);

    /* 现在 LPS22DF 已在 10 Hz 低噪声模式开始工作,可在主循环
       通过中断或轮询读取气压 (hPa) 与温度 (°C)。*/
  }

在这里插入图片描述
在主程序中添加初始化。

    // 初始化 LPS22DF 传感器,配置速率和模式
    sensor_lps22df_init();

在这里插入图片描述

界面初始化

在OLED_switch添加LPS22DF界面初始化。

static void OLED_switch(void)
{
    if (g_oled_clear)                     // ← 仅当需切屏时进入
    {
        g_oled_clear = 0;                 // 标记已完成清屏
        OLED_Clear();                     // ① 清显存并刷新,黑场一次

        switch (g_oled_page)              // ② 根据当前页写“标签”
        {
            /* ---------- Page-0 : Tuya 模组 ---------- */
            case 0:
                OLED_ShowString(0,  0, (u8 *)"TUYA",       16,1);
                OLED_ShowString(0, 16, (u8 *)"WIFI MODE:", 16,1);
                OLED_ShowString(0, 32, (u8 *)"AP MODE:",   16,1);
                break;

            /* ---------- Page-1 : LSM6DSV16X ---------- */
            case 1:
                OLED_ShowString(0,  0, (u8 *)"MEMS_LSM6DSV16X", 12,1);
                OLED_ShowString(0, 12, (u8 *)"TAP:",   12,1);
                OLED_ShowString(60,12, (u8 *)"TEMP:",  12,1);
                OLED_ShowString(0, 24, (u8 *)"X(mg):", 12,1);
                OLED_ShowString(0, 36, (u8 *)"Y(mg):", 12,1);
                OLED_ShowString(0, 48, (u8 *)"Z(mg):", 12,1);
                break;

            /* ---------- Page-2 : LPS22DF 气压计 ------- */
            case 2:
                OLED_ShowString(0,  0, (u8 *)"MEMS_LPS22DF", 16,1);
                OLED_ShowString(0, 16, (u8 *)"Pressure:", 16,1);
                OLED_ShowString(0, 32, (u8 *)"Temp:",   16,1);
                break;
        }
        OLED_Refresh();                   // ③ 推送骨架到屏幕
    }
}

在这里插入图片描述

中断回调函数

lps22df_read_data_drdy函数是气压计 LPS22DF 的 DRDY 中断读取函数。
由于没有不需要上报涂鸦,所以2中的wifi_Update和g_tuya_up_data注释掉了。
两个变量推送到 OLED 第 2 页或打包成 Tuya DP 上报云端。

/******************************************************************************
 * @brief  通过 DRDY 中断读取 LPS22DF 气压计数据
 *         - 由外部中断服务程序置位 lps22df_irq_flag
 *         - 进入后读取最新气压 / 温度(若数据已就绪)
 *         - 同时触发涂鸦 DP 上报计时器
 ******************************************************************************/
double lps22df_data_pressure=0.0f;
double lps22df_data_temp=0.0f;
static void lps22df_read_data_drdy(void)
{
    /* -------- 1. 检测由 EXTI 产生的数据就绪标志 ------------------ */
    if (lps22df_irq_flag)                    // INT/DRDY 低电平到来
    {
        lps22df_irq_flag = false;            // 先清除本地标志

        /* -------- 2. 触发涂鸦数据上报计时 ----------------------- */
//        wifi_Update   = 1;                   // 下轮主循环立刻刷新 DP
//        g_tuya_up_data = 2000;               // 2 s 后再次上报 (计数器复位)

        /* -------- 3. 查询 LPS22DF 数据就绪标志 ------------------ */
        lps22df_all_sources_t all_src;
        lps22df_all_sources_get(&dev_ctx_LPS22DF, &all_src);

        /* -------- 4. 仅在有新数据时读取压强/温度 ---------------- */
        if (all_src.drdy_pres || all_src.drdy_temp)
        {
            static lps22df_data_t data;      // 静态减少栈开销
            lps22df_data_get(&dev_ctx_LPS22DF, &data);
            lps22df_data_pressure = data.pressure.hpa;
            lps22df_data_temp = data.heat.deg_c;
//            printf("pressure [hPa]: %6.2f  temperature [degC]: %6.2f\r\n",
//                   data.pressure.hpa, data.heat.deg_c);
            /* 此处可再把 data 填入 OLED/Page-2 或涂鸦 DP */
        }
    }
}

在这里插入图片描述

之后在主程序中调用。


        // 处理 LPS22DF事件,(例如气压,温度)
        lps22df_read_data_drdy();

在这里插入图片描述

气压数据显示

OLED_lps22df_MEMS() 是 OLED 第 2 页面(气压计页)的动态刷新函数。
● 通过 g_tuya_num 计数器,每 100 ms 刷新一次,既保证数据实时,又减轻 I²C 负担。
● 调用全局缓存 lps22df_data_pressure 与 lps22df_data_temp(由 DRDY 中断读取函数更新),把 气压 以 “xxxx.xx hPa”,温度 以 “±xxx.xx °C” 的格式显示在屏幕指定坐标。
● 采用分段写字符的方式(整数->小数点->小数),避免 sprintf 带来的堆栈及时间开销。
● 最后执行 OLED_Refresh() 将修改后的显存块写回驱动 IC,实现无闪烁、平滑的实时数据展示。

/* ------------ Page-2 : MEMS_lps22df -------------------------- */
static void OLED_lps22df_MEMS(void)
{
    /* ---------- 1. 节流判断:每 1 ms 主循环 +1,满 100 ≈ 100 ms ---------- */
    if(g_tuya_num<100)
    g_tuya_num++;
    else
    {
        g_tuya_num=0;
        uint32_t t100 = 0;// 小数两位 ×100 的临时变量
        OLED_ShowNum  (72, 16,(uint32_t)lps22df_data_pressure, 4, 16, 1);// 整数位
        /*   小数点 '.'       */
        OLED_ShowChar (104, 16, '.',16, 1);
        /*   小数部分:xx     */
        t100 = (uint32_t)(lps22df_data_pressure * 100);
        OLED_ShowNum  (112, 16,(uint32_t)t100%100, 2, 16, 1);

        /*   正负号 */
        if(lps22df_data_temp<0)
            OLED_ShowChar (72, 32, '-',16, 1);
        else
            OLED_ShowChar (72, 32, '+',16, 1);
        OLED_ShowNum (80, 32,(uint32_t)fabs(lps22df_data_temp),3,16, 1);// 整数位
        /*   小数点 '.'       */
        OLED_ShowChar (104, 32, '.',16, 1);
        /*   小数部分:xx     */
        t100=(uint32_t)(fabs(lps22df_data_temp)*100);
        OLED_ShowNum (112, 32,(uint32_t)t100%100,2,16, 1);
        OLED_Refresh();
    }
}

在这里插入图片描述
在主程序中添加OLED界面显示。

        if(g_oled_page==0) /* Page-0 : Tuya 状态页 */
            OLED_DrawPage_TUYA();// 显示 Wi-Fi / AP 配网信息
        else if(g_oled_page==1)/* Page-1 : LSM6DSV16X 状态页 */
            OLED_DrawPage_MEMS();// 显示单/双击、温度、XYZ 加速度
        else if(g_oled_page==2)/* Page-2 : LPS22DF 状态页 */
            OLED_lps22df_MEMS();// 显示气压、温度

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到