概述
本工程在瑞萨 RA4M2 单片机上,利用 0.96ʺ 128×64 OLED 实时展示多源信息,并把 涂鸦状态和LSM6DSV16X 六轴 IMU 的运动数据(单/双击、温度、三轴加速度)以“无闪烁”方式动态刷到屏幕。
最近在瑞萨RA的课程,需要样片的可以加qun申请:925643491。
视频教学
https://www.bilibili.com/video/BV1aENbzhEYN
RA4M2开发IOT(9)----动态显示MEMS数据
样品申请
https://www.wjx.top/vm/rCrkUrz.aspx
硬件准备
首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。
主控为R7FA4M2AD3CFL#AA0
同时添加RA4M2_IOT扩展版。
参考程序
https://github.com/CoreMaker-lab/RA4M2_IOT
https://gitee.com/CoreMaker/RA4M2_IOT
定义变量
● g_oled_clear:只在切换页面或进入特定模式时置 1,触发一次 OLED_Clear(),
之后页面内容靠覆写更新,避免闪屏。
● g_tuya_num:你用它做 100-次计数(在 1 ms 定时循环里 +1),到 100 (~100 ms) 再刷新页面一次,减少 I²C 带宽。
/* ================== OLED Page Control ================== */
static uint8_t g_oled_clear = 1; // 1=需要清屏;0=正常增量刷新
static uint8_t g_oled_page = 0; // 当前页索引:0-Tuya 1-MEMS 2-Baro
static uint8_t g_tuya_num = 0; // 100ms 级计数器,用来节流页面刷新
AC
/* —— Tuya(涂鸦)配网状态 —— */
static uint8_t g_tuya_ap_mode = 0; // 1=处于 AP 快配;0=普通
static uint32_t g_tuya_ap_mode_num = 0; // AP 模式剩余倒计时 (ms)
轻触切屏
在button_wifi_ap长按配网添加轻触切屏
/*******************************************************************************************************************
* @brief 长按按键 3 s 进入配网模式 (发送 0x05 命令)
*******************************************************************************************************************/
static void button_wifi_ap(void)
{
// wifi_ap_num
bsp_io_level_t p_port_value_pin_111;
R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_11, &p_port_value_pin_111);
if(p_port_value_pin_111)/* 松开 */
wifi_ap_num=0;
else//长按3s复位wifi
{
if(wifi_ap_num<3000)
wifi_ap_num++;
else if(wifi_ap_num==3000)
{
g_oled_clear=1;// 切屏前清屏
g_oled_page=0;// 0-Tuya 1-MEMS 2-Baro
g_tuya_ap_mode_num=60*1*1000;//1分钟AP配网
wifi_ap_num++;
printf("[BTN] wifi_ap_mode\r\n");
fsp_err_t err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_wifi_cfg, 8);
if(FSP_SUCCESS != err) __BKPT();
while(uart_wifi_TX_flag == false){}
uart_wifi_TX_flag = false;
}
if(wifi_ap_num==100)//轻触切换OLED显示
{
g_oled_clear=1;// 下一帧先清屏
if(g_oled_page<2)// 0-Tuya 1-MEMS 2-Baro
g_oled_page++;
else
g_oled_page=0;
}
}
}
静态标签
在检测到 g_oled_page 变化时——由 button_wifi_ap() 轻触切换或上电初始化——一次性绘制对应页面的静态标签(页标题、固定字段名)。
只要 g_oled_clear == 1 就清屏并绘骨架;随后动态数值由各 OLED_DrawPage_* 覆写,不闪烁。
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", 12,1);
break;
}
OLED_Refresh(); // ③ 推送骨架到屏幕
}
}
涂鸦界面
在 Page-0 下周期性(每 100×1 ms ≈ 100 ms)刷新两行动态信息:
● AP MODE 行:显示 AP 或 normal
● WIFI MODE 行:显示 g_tuya_mode_flag(0~4)。
同时根据 g_tuya_mode_flag 控制 60 s AP 配网倒计时,使文字在倒计时结束后回到 normal。
/* ------------ Page-0 : TUYA 模组信息 -------------------------------- */
static void OLED_DrawPage_TUYA(void)
{
/* ① 管理倒计时 ── 在线时清零;否则每 1 ms 递减 */
if (g_tuya_mode_flag == 4) // 4 = 已连云
g_tuya_ap_mode_num = 0;
else if (g_tuya_ap_mode_num > 0)
g_tuya_ap_mode_num--;
/* ② 100 ms 节流:g_tuya_num 递增至 100 再刷新一次 */
if (++g_tuya_num >= 100)
{
g_tuya_num = 0;
/* --- 行1: AP/normal --- */
if (g_tuya_ap_mode_num > 0) // 倒计时仍在 → AP
OLED_ShowString(80,16,(u8 *)"AP ",16,1);
else // 否则 normal
OLED_ShowString(80,16,(u8 *)"normal",16,1);
/* --- 行2: Wi-Fi work-mode 数字标志 --- */
OLED_ShowNum(80, 32, g_tuya_mode_flag, 1, 16,1);
OLED_Refresh(); // 推送局部变更
}
}
LSM6DSV16X界面
在 Page-1 下每 100 ms 刷新:
- 单/双击状态文本 (up_down)
- 片内温度 (temperature_degC)
- X/Y/Z 三轴加速度(mg,带符号,小数点后两位)
/* ------------ Page-1 : MEMS_LSM6DSV16X -------------------------- */
static void OLED_DrawPage_MEMS(void)
{
if(g_tuya_num<100)
g_tuya_num++;
else
{
g_tuya_num=0;
/* ---------- 1. TAP 状态 ---------- */
if(up_down==0)
OLED_ShowString(24, 12, "Normal", 12, 1);//在坐标 (24,12)
else if(up_down==1)
OLED_ShowString(24, 12, "Single", 12, 1);//在坐标 (24,12)
else if(up_down==2)
OLED_ShowString(24, 12, "Double", 12, 1);//在坐标 (24,12)
/* ---------- 2. 读取并显示片内温度 ---------- */
int16_t data_raw_temperature;
double_t temperature_degC;
/* Read temperature data */
memset(&data_raw_temperature, 0x00, sizeof(int16_t));// 清空原始数据缓存
// 读取 LSM6DSV16X 的原始温度数据
lsm6dsv16x_temperature_raw_get(&dev_ctx, &data_raw_temperature);
// 将原始 LSB 数据转换为摄氏温度
temperature_degC = lsm6dsv16x_from_lsb_to_celsius(
data_raw_temperature);
OLED_ShowNum (90, 12,(uint32_t)temperature_degC, 3, 12, 1);// 整数位
OLED_ShowChar (110, 12, '.',12, 1);
uint32_t t100 = (uint32_t)(temperature_degC * 100);
OLED_ShowNum (116, 12,(uint32_t)t100%100, 2, 12, 1);// 整数位
/* ---------- 3. 读取并显示 X/Y/Z 加速度 ---------- */
int16_t data_raw_acceleration[3];
double_t acceleration_mg[3];
lsm6dsv16x_acceleration_raw_get(&dev_ctx, data_raw_acceleration);
acceleration_mg[0] =
lsm6dsv16x_from_fs8_to_mg(data_raw_acceleration[0]);
acceleration_mg[1] =
lsm6dsv16x_from_fs8_to_mg(data_raw_acceleration[1]);
acceleration_mg[2] =
lsm6dsv16x_from_fs8_to_mg(data_raw_acceleration[2]);
if(acceleration_mg[0]<0)
OLED_ShowChar (36, 24, '-',12, 1);
else
OLED_ShowChar (36, 24, '+',12, 1);
OLED_ShowNum (42, 24,(uint32_t)fabs(acceleration_mg[0]),5,12, 1);
OLED_ShowChar (72, 24, '.',12, 1);
t100=(uint32_t)(fabs(acceleration_mg[0])*100);
OLED_ShowNum (78, 24,(uint32_t)t100%100,2,12, 1);
if(acceleration_mg[1]<0)
OLED_ShowChar (36, 36, '-',12, 1);
else
OLED_ShowChar (36, 36, '+',12, 1);
OLED_ShowNum (42, 36,(uint32_t)fabs(acceleration_mg[1]),5,12, 1);
OLED_ShowChar (72, 36, '.',12, 1);
t100=(uint32_t)(fabs(acceleration_mg[1])*100);
OLED_ShowNum (78, 36,(uint32_t)t100%100,2,12, 1);
if(acceleration_mg[2]<0)
OLED_ShowChar (36, 48, '-',12, 1);
else
OLED_ShowChar (36, 48, '+',12, 1);
OLED_ShowNum (42, 48,(uint32_t)fabs(acceleration_mg[2]),5,12, 1);
OLED_ShowChar (72, 48, '.',12, 1);
t100=(uint32_t)(fabs(acceleration_mg[2])*100);
OLED_ShowNum (78, 48,(uint32_t)t100%100,2,12, 1);
OLED_Refresh();
// printf("Acceleration [mg]:%4.2f\t%4.2f\t%4.2f\r\n",
// acceleration_mg[0], acceleration_mg[1], acceleration_mg[2]);
}
}
主程序
hal_entry() 的主循环中,负责把 MCU 采集到的实时传感器/通信状态 动态绘制到 0.96ʺ OLED——并确保在不同功能页之间平滑切换、无闪烁。
//切屏完成后不再清屏,保持增量刷新,避免闪烁
OLED_switch();
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 加速度