RA4M2开发IOT(10)----集成LPS22DF气压计
RA4M2开发IOT.10--集成LPS22DF气压计
- 概述
- 视频教学
- 样品申请
- 硬件准备
- 参考程序
- 产品特性
- 通信模式
- 速率
- IIC属性配置
- CS片选配置
- SA0地址设置
- 配置中断
- 中断回调函数
- 使能中断
- 管脚初始化
- 参考程序
- LPS22DF初始化
- 界面初始化
- 中断回调函数
- 气压数据显示
概述
本篇文章将延续现有 “动态显示 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->1R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_05_PIN_00, BSP_IO_LEVEL_HIGH); //LPS22DF SA0->0R_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, ®, 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 / SPIlps22df_bus_mode_set(&dev_ctx_LPS22DF, &bus_mode);/* 7. 设置测量输出速率 & 滤波/平均参数 --------------------------------- */md.odr = LPS22DF_10Hz; // ODR = 10 Hzmd.avg = LPS22DF_256_AVG; // 256 次平均,降低噪声md.lpf = LPS22DF_LPF_ODR_DIV_9; // 低通截止 ≈ 1.1 Hzlps22df_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);elseOLED_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();// 显示气压、温度