RA4M2开发IOT(7)----RA4M2驱动涂鸦CBU模组
RA4M2开发IOT.7--RA4M2驱动涂鸦CBU模组
- 概述
- 视频教学
- 样品申请
- 硬件准备
- 参考程序
- 初始化 LSM6DSV16X 传感器
- 初始化
- 单双击识别
- 主程序
- 接口
- RA4M2接口
- 生成UART
- UART属性配置
- R_SCI_UART_Open()函数原型
- 回调函数user_uart_callback0 ()
- 变量定义
- 更新敲击状态
- DP同步
- 长按进入配网
- 涂鸦协议解析
- 定时上报
- 主循环任务调度
概述
本方案利用 Renesas RA4M2 MCU 与涂鸦智能预认证 Wi-Fi 模组的深度结合,实现多种传感器数据的云端采集与可视化管理。RA4M2 负责对传感器进行高速采样,并通过 UART接口将整合后的数据流推送给内置涂鸦协议栈的 Wi-Fi 模组。模组通过 TLS 加密通道与涂鸦云平台双向通信,既可将上传的数据点(DP)实时同步至云端,也可接收来自 App 或自建后台的控制指令。
借助涂鸚 IoT 平台,开发者只需在控制台一次性定义数据模型(DP)、下载 MCU 二次开发包并完成少量底层 HAL 实现,即可在几天内完成从硬件到移动端的端到端 Demo,在涂鸦智能 App 或基于 MQTT/HTTP 的自建后台中,可实时监控历史趋势并下发命令。
通过本方案,您可以在 1–2 天内完成硬件、固件与云端的一体化部署,实现任意传感器数据的可视化管理和智能联动。
最近在瑞萨RA的课程,需要样片的可以加qun申请:925643491。
视频教学
https://www.bilibili.com/video/BV14bMmz7ETD
RA4M2开发IOT(7)----RA4M2驱动涂鸦CBU模组
样品申请
https://www.wjx.top/vm/rCrkUrz.aspx
硬件准备
首先需要准备一个开发板,这里我准备的是自己绘制的开发板,需要的可以进行申请。
主控为R7FA4M2AD3CFL#AA0
同时添加RA4M2_IOT扩展版。
参考程序
https://github.com/CoreMaker-lab/RA4M2_IOT
https://gitee.com/CoreMaker/RA4M2_IOT
初始化 LSM6DSV16X 传感器
优化代码结构,函数 sensor_lsm6dsv16x_tap_init() 的主要作用是初始化 LSM6DSV16X 传感器以实现单击(Single Tap)和双击(Double Tap)事件检测功能,并将这些事件输出到中断引脚(INT1)用于主控MCU响应。
static void sensor_lsm6dsv16x_tap_init(void)
{// 配置传感器设备操作函数(读、写、延时),并指定 I2C 句柄dev_ctx.write_reg = platform_write;dev_ctx.read_reg = platform_read;dev_ctx.mdelay = platform_delay;dev_ctx.handle = &SENSOR_BUS;// 上电后延时,等待传感器稳定platform_delay(BOOT_TIME);// 读取芯片 ID,并打印,用于识别传感器是否连接正确lsm6dsv16x_device_id_get(&dev_ctx, &whoamI);printf("LSM6DSV16X_ID=0x%x, whoamI=0x%x\n", LSM6DSV16X_ID, whoamI);if (whoamI != LSM6DSV16X_ID) while (1);// 若芯片 ID 不匹配,则停留在此处// 若芯片 ID 不匹配,则停留在此处lsm6dsv16x_reset_set(&dev_ctx, LSM6DSV16X_RESTORE_CTRL_REGS);// 等待复位完成do {lsm6dsv16x_reset_get(&dev_ctx, &rst);} while (rst != LSM6DSV16X_READY);// 使能 Block Data Update,防止数据在读取时被更新lsm6dsv16x_block_data_update_set(&dev_ctx, PROPERTY_ENABLE);// 配置将“单击”和“双击”事件路由至 INT1 引脚pin_int.double_tap = PROPERTY_ENABLE;pin_int.single_tap = PROPERTY_ENABLE;lsm6dsv16x_pin_int1_route_set(&dev_ctx, &pin_int);// 配置中断输出方式:使能功能中断,关闭锁存模式(脉冲输出)irq.enable = 1;irq.lir = 0;lsm6dsv16x_interrupt_enable_set(&dev_ctx, irq);// 启用 Z 轴 Tap 检测(仅检测 Z 轴)tap.tap_z_en = 1;lsm6dsv16x_tap_detection_set(&dev_ctx, tap);// 设置 Tap 阈值,单位为 LSB(3 LSB ≈ 186mg @ ±8g)tap_ths.z = 3;lsm6dsv16x_tap_thresholds_set(&dev_ctx, tap_ths);// 设置 Tap 时间窗口(用于识别单击/双击时的时间控制)tap_win.tap_gap = 7;tap_win.shock = 3;tap_win.quiet = 3;lsm6dsv16x_tap_time_windows_set(&dev_ctx, tap_win);// 启用双击和单击检测模式lsm6dsv16x_tap_mode_set(&dev_ctx, LSM6DSV16X_BOTH_SINGLE_DOUBLE);// 设置加速度计工作参数:480Hz 采样率lsm6dsv16x_xl_data_rate_set(&dev_ctx, LSM6DSV16X_ODR_AT_480Hz);// 设置加速度计量程为 ±8g(灵敏度和范围的折中)lsm6dsv16x_xl_full_scale_set(&dev_ctx, LSM6DSV16X_8g);
}
初始化
app_peripheral_init() 函数用于初始化开发板的外围设备,包括 I/O 引脚、电源状态、串口通信、I2C 总线和外部中断设置,是整个系统正常运行的基础准备工作。
static void app_peripheral_init(void)
{// 点亮板载 LED(用于上电指示)R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_04, BSP_IO_LEVEL_HIGH);//LIS2MDL CS2->1R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH);//LSM6DSV16X CS1->1R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_02, BSP_IO_LEVEL_HIGH);// 初始化串口 UART9err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);assert(FSP_SUCCESS == err);// 发送初始字符串用于串口测试char *msg = "RA E2STUDIO";err = R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)msg, strlen(msg));if (FSP_SUCCESS != err) __BKPT();while (!uart_send_complete_flag) {}uart_send_complete_flag = false;// 打印欢迎信息printf("\nhello world!\r\n");// 初始化 I2C 控制器err = R_SCI_I2C_Open(&g_i2c2_ctrl, &g_i2c2_cfg);assert(FSP_SUCCESS == err);// 打开外部中断通道(用于接收 LSM6DSV16X 的中断输出)err = R_ICU_ExternalIrqOpen(&g_external_irq6_ctrl, &g_external_irq6_cfg);assert(FSP_SUCCESS == err);// 使能外部中断通道err = R_ICU_ExternalIrqEnable(&g_external_irq6_ctrl);assert(FSP_SUCCESS == err);
}
单双击识别
处理 LSM6DSV16X 传感器的单击和双击中断事件,并在串口打印事件类型。
// Tap 检测主循环函数:用于持续检测并打印单击/双击事件
static void sensor_lsm6dsv16x_tap_loop(void)
{// 若外部中断触发,读取传感器中断源if (irq_flag){lsm6dsv16x_all_sources_get(&dev_ctx, &status);irq_flag = false;if (status.single_tap)stap_event_catched = 1;if (status.double_tap)dtap_event_catched = 1;}// 若捕获到单击事件,处理并打印if (stap_event_catched){stap_event_catched = 0;printf("Single TAP\r\n");}// 若捕获到双击事件,处理并打印if (dtap_event_catched){dtap_event_catched = 0;printf("Double TAP\r\n");}
}
主程序
void hal_entry(void)
{/* TODO: add your own code here */// 初始化外设(如 UART、I2C、中断引脚、GPIO 等)app_peripheral_init();// 初始化 LSM6DSV16X 传感器,配置单击/双击 Tap 检测功能sensor_lsm6dsv16x_tap_init();while (1){// 判断单双击sensor_lsm6dsv16x_tap_loop();R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);}#if BSP_TZ_SECURE_BUILD/* Enter non-secure code */R_BSP_NonSecureEnter();
#endif
}
接口
模组跳线接线方式如下。
接入扩展版。
RA4M2接口
涂鸦模组原理图如下所示。
生成UART
点击Stacks->New Stack->Connectivity -> UART(r_sci_uart)。
UART属性配置
R_SCI_UART_Open()函数原型
故可以用 R_SCI_UART_Open()函数进行配置,开启和初始化UART。
// 初始化串口 UART0err = R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);assert(FSP_SUCCESS == err);
添加到app_peripheral_init函数中。
static void app_peripheral_init(void)
{// 点亮板载 LED(用于上电指示)R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_04, BSP_IO_LEVEL_HIGH);//LIS2MDL CS2->1R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_02, BSP_IO_LEVEL_HIGH);//LSM6DSV16X CS1->1R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_02, BSP_IO_LEVEL_HIGH);// 初始化串口 UART9err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);assert(FSP_SUCCESS == err);// 发送初始字符串用于串口测试char *msg = "RA E2STUDIO";err = R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)msg, strlen(msg));if (FSP_SUCCESS != err) __BKPT();while (!uart_send_complete_flag) {}uart_send_complete_flag = false;// 打印欢迎信息printf("\nhello world!\r\n");// 初始化 I2C 控制器err = R_SCI_I2C_Open(&g_i2c2_ctrl, &g_i2c2_cfg);assert(FSP_SUCCESS == err);// 打开外部中断通道(用于接收 LSM6DSV16X 的中断输出)err = R_ICU_ExternalIrqOpen(&g_external_irq6_ctrl, &g_external_irq6_cfg);assert(FSP_SUCCESS == err);// 使能外部中断通道err = R_ICU_ExternalIrqEnable(&g_external_irq6_ctrl);assert(FSP_SUCCESS == err);// 初始化串口 UART0err = R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);assert(FSP_SUCCESS == err);
}
回调函数user_uart_callback0 ()
当数据发送的时候,可以查看UART_EVENT_TX_COMPLETE来判断是否发送完毕。
当数据接收的时候,可以查看UART_EVENT_RX_COMPLETE来判断是否发送完毕。
UART_EVENT_RX_CHAR:字符接收事件,当接收到一个字符时触发。
可以检查检查 “p_args” 结构体中的 “event” 字段的值是否等于 “UART_EVENT_TX_COMPLETE"或者"UART_EVENT_RX_COMPLETE"或者"UART_EVENT_RX_CHAR”。
/* UART0——涂鸦模块:单字节接收完成标志 */
volatile bool uart_wifi_RX_flag = false;
/* UART0——涂鸦模块:发送完成标志*/
volatile bool uart_wifi_TX_flag = false;
/* UART0 接收缓存大小*/
#define UART0_LENGTH 255
/* UART0 接收环形缓冲区*/
uint8_t TUYA_wifi_buffer[UART0_LENGTH];
/* 已接收字节计数(环形缓存写指针)*/
uint32_t UART0_TUYA_LENGTH = 0;
/* 解析到一帧有效数据后置位,主循环里解析后需清零 */
uint32_t UART0_TUYA_flag = 0;/****************************************************************** UART0 回调:RX/TX/单字符事件处理*****************************************************************/
void user_uart_callback0(uart_callback_args_t *p_args)
{/* 整帧接收完成(需在 R_SCI_UART_Receive 调用后才会产生)*/if(p_args->event == UART_EVENT_RX_COMPLETE){uart_wifi_RX_flag = true;}/* 发送完成 */else if(p_args->event == UART_EVENT_TX_COMPLETE){uart_wifi_TX_flag = true;}/* 接收到 1 个字符*/else if(p_args->event == UART_EVENT_RX_CHAR){/* 缓冲未满则写入*/if (sizeof(TUYA_wifi_buffer) > UART0_TUYA_LENGTH){/* 仅在数据位 ≥ 8bit 时写入 1 字节 */if (UART_DATA_BITS_8 >= g_uart0_cfg.data_bits){TUYA_wifi_buffer[UART0_TUYA_LENGTH++] = (uint8_t) p_args->data;}/* —— 以下为简单的涂鸦帧快速判定逻辑,可改为状态机 —— *//* 帧头非 0x55 则丢弃当前字节(回溯 1 位)*/if(TUYA_wifi_buffer[00]!=0x55)UART0_TUYA_LENGTH--;/* 普通指令帧长度 ≥ 7 且 CMD ≠ 0x07 时,即可认为一帧完 */if(UART0_TUYA_LENGTH>=7 && TUYA_wifi_buffer[3]!=0x06)UART0_TUYA_flag=1;}}
}
变量定义
/****************************************************************** 与涂鸦协议相关的工作变量/固定协议帧* *****************************************************************/uint8_t wifi_first =0;/* 0:第一次心跳;1:第二次心跳 */
uint32_t wifi_num =0;//如果心跳频繁发送,可能是触发了复位,需要重新发送buff1,这里2秒内多次发送心跳指令则认为重启
/* ---- 固定格式下行帧:MCU 主动发送给涂鸦模块 ----------------- */
const uint8_t g_tuya_heartbeat1[8]={0x55,0xAA,0x03,0x00,0x00,0x01,0x00,0x03};//心跳检测,第1次 0x55 aa 00 00 00 01 00 03
const uint8_t g_tuya_heartbeat2[8]={0x55,0xAA,0x03,0x00,0x00,0x01,0x01,0x04};//心跳检测,第2次 0x55 aa 00 00 00 01 01 04
const uint8_t g_tuya_wifi_cfg[8]={0x55,0xAA,0x03,0x05,0x00,0x01,0x01,0x09};//WIFI配网
//0x55, 0xAA, 0x03, 0x01 (帧头)
//0x00, 0x2A (长度)
//0x7B, 0x22, 0x70, 0x22, 0x3A, 0x22 ({"p":")
//0x36, 0x33, 0x70, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x72, 0x73, 0x6C, 0x78, 0x74, 0x75, 0x72, 0x38 (63pnfirmrslxtur8)
//20x22, 0x2C, 0x22, 0x76, 0x22, 0x3A, 0x22 (","v":")
//0x31, 0x2E, 0x30, 0x2E, 0x30 (1.0.0)
//0x22, 0x2C, 0x22, 0x6D, 0x22, 0x3A (","m":)
//0x30 (0)
//0x7D(})
//0x40(校验码)
uint8_t g_tuya_product_info[49]={
0x55, 0xAA, 0x03, 0x01,
0x00, 0x2A,
0x7B, 0x22, 0x70, 0x22, 0x3A, 0x22,
0x36, 0x33, 0x70, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x72, 0x73, 0x6C, 0x78, 0x74, 0x75, 0x72, 0x38,
0x22, 0x2C, 0x22, 0x76, 0x22, 0x3A, 0x22,
0x31, 0x2E, 0x30, 0x2E, 0x30,
0x22, 0x2C, 0x22, 0x6D, 0x22, 0x3A,
0x30,
0x7D,
0x40};//接收模块发送的查询产品信息请求const uint8_t g_tuya_query_mode[8]={0x55,0xaa,0x03,0x02,0x00,0x00,0x04};//查询工作模式
uint32_t wifi_ap_num =0;//wifi重置计时
const uint8_t g_tuya_rpt_net[8]={0x55,0xaa,0x03,0x03,0x00,0x00,0x05};//报告设备联网状态
uint32_t wifi_Update=0;//wifi发送标志位
uint32_t g_tuya_up_data=0;//定时上报计时
uint8_t g_tuya_mode_flag=0;//wifi模式,4的时候链接上网
uint8_t bat=0;//电池电量
uint8_t g_tuya_dp_bat[15]={0x55,0xAA,0x03,0x07,0x00,0x08,0x03,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x1A};//MCU上报电量uint8_t up_down=0;//震动
uint8_t g_tuya_dp_tap[12]={0x55,0xAA,0x03,0x07,0x00,0x05,0x0A,0x04,0x00,0x01,0x00,0x1D};//MCU上报状态uint16_t temp_tuya=0;//温度
uint8_t g_tuya_dp_temp[15]={0x55,0xAA,0x03,0x07,0x00,0x08,0x08,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x1F};//MCU上报稳定
更新敲击状态
在原有基础上增加wifi_Update变量,这样每次使用按键之后都可以上报到云端,同时将单击双击事件up_down。
// Tap 检测主循环函数:用于持续检测并打印单击/双击事件
static void sensor_lsm6dsv16x_tap_loop(void)
{// 若外部中断触发,读取传感器中断源if (irq_flag){wifi_Update=1;g_tuya_up_data=2000;lsm6dsv16x_all_sources_get(&dev_ctx, &status);irq_flag = false;if (status.single_tap)stap_event_catched = 1;if (status.double_tap)dtap_event_catched = 1;// 若捕获到单击事件,处理并打印if (stap_event_catched){stap_event_catched = 0;up_down=1;printf("Single TAP\r\n");}// 若捕获到双击事件,处理并打印if (dtap_event_catched){dtap_event_catched = 0;up_down=2;printf("Double TAP\r\n");}}
}
DP同步
该函数先检查 wifi_Update 标志,若需上报,则:
当 wifi_Update 标志被置位时,将当前的 tap 状态通过 UART 发送给涂鸦模块,用于通知涂鸦 App 有状态变化。
整个流程实现了“本地 LED 状态 → MCU 构造涂鸦数据帧 → Wi-Fi 模块上报至云端”的闭环同步。
/******************************************************************************************************************** @brief 向涂鸦模块上报最新 DP 数据 & 本地硬件同步*******************************************************************************************************************/
static void tuya_wifi_Update(void)
{if(wifi_Update){printf("wifi_Update\n");wifi_Update=0;g_tuya_dp_tap[10]=up_down;//状态g_tuya_dp_tap[11]=0;//校验和for(int i=0;i<=10;i++)g_tuya_dp_tap[11]+=g_tuya_dp_tap[i];err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_dp_tap, 12);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}}
长按进入配网
使用P111为重置wifi按钮。
配置P111为输入端口。
该函数检测按键引脚状态,累计长按时间。当按键持续按下达到 3 秒(3000 次 1 ms 延时)时,触发一次配网模式:
- 打印日志 [BTN] wifi_ap_mode
- 通过 UART0 向涂鸦模块发送配网指令帧 g_tuya_wifi_cfg(0x05 命令)。
短按或松开时则重置计时,避免误触发。
/******************************************************************************************************************** @brief 长按按键 3 s 进入配网模式 (发送 0x05 命令)*******************************************************************************************************************/
static void button_wifi_ap(void)
{// wifi_ap_numbsp_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){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;}}
}
涂鸦协议解析
该函数在接收到完整一帧涂鸦数据后,
- 校验帧头与版本,确认有效帧;
- 根据 CMD 字段(frame[3])分支:
○ 0x00:心跳请求,打印 [TUYA] 并交替发送两种心跳应答;
○ 0x01:查询产品信息,打印 [TUYA] 并发送产品信息帧;
○ 0x02:查询工作模式,打印 [TUYA] 并发送工作模式帧;
○ 0x03:网络状态上报,打印 [TUYA] <WIFI_MODE=XX> 并回复网络状态帧,同时在已连云时置 wifi_Update;
○ 0x04:已连上路由器且连接到云端,打印 [TUYA] <WIFI_MODE=XX> 并回复网络状态帧,同时在已连云时置 wifi_Update; - 清空接收缓存,为下一帧解析复位。
/******************************************************************************************************************** @brief 解析完成后,根据 CMD 打印清晰友好的日志*******************************************************************************************************************/
static void uart0_tuya(void)
{if(UART0_TUYA_flag ==1)//接收完成标志{fsp_err_t err = FSP_SUCCESS;UART0_TUYA_flag=0;if(TUYA_wifi_buffer[0]==0x55&&TUYA_wifi_buffer[1]==0xAA)//判断帧头和版本{if(TUYA_wifi_buffer[3]==0x00)//判断是否为心跳检测{printf("[TUYA] <heartbeat(SEQ=%u)\r\n", wifi_first);
// if(wifi_num<2000&&wifi_first==1)//频繁发送心跳指令,认为重启
// {
// wifi_first=0;
// }
// wifi_num=0;if(wifi_first==0)//第一次发送心跳数据{wifi_first=1;//心跳检测,向涂鸦模块发送err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_heartbeat1, 8);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}else{//心跳检测,向涂鸦模块发送err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_heartbeat2, 8);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}}else if(TUYA_wifi_buffer[3]==0x01)//接收模块发送的查询产品信息请求{printf("[TUYA] <Query Product Information\r\n");//接收模块发送的查询产品信息请求,向涂鸦模块发送err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_product_info, 49);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}else if(TUYA_wifi_buffer[3]==0x02)//查询工作模式{printf("[TUYA] <Query Work Mode\r\n");//查询工作模式,向涂鸦模块发送err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_query_mode, 8);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}else if(TUYA_wifi_buffer[3]==0x03)//报告设备联网状态{g_tuya_mode_flag=TUYA_wifi_buffer[6];//联网状态printf("[TUYA] <WIFI_MODE=%02X\r\n", TUYA_wifi_buffer[6]);//查询工作模式,向涂鸦模块发送err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_rpt_net, 8);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;if(TUYA_wifi_buffer[6]==0x04)//已连上路由器且连接到云端{wifi_Update=1;//wifi跟新标志位}}}// 清除数组memset(TUYA_wifi_buffer, 0, UART0_LENGTH);// 同时把当前有效长度归零UART0_TUYA_LENGTH = 0;}
}
定时上报
tuya_up_data() 是用于定时上传 电量 和 温度数据 给涂鸦 Wi-Fi 模块的函数。当 g_tuya_mode_flag == 4(表示处于“设备成功连接到云端”)且 g_tuya_up_data 计时达到上限时,它会打包数据帧,通过 UART 发送给涂鸦模块,并进行校验和处理。
// 涂鸦数据上传函数:每 2000ms 上传一次电量和温度信息给涂鸦模块
static void tuya_up_data(void)
{// 当处于涂鸦工作模式 4(表示允许上传数据)if(g_tuya_mode_flag==4){// g_tuya_up_data 每加一次表示延时 1ms(由定时器或主循环每 ms 调用此函数)if(g_tuya_up_data<2000)g_tuya_up_data++;// 计时增加else{g_tuya_up_data=0;// 超过 2000ms,开始上传数据/* ---------------- 电量数据上传 ---------------- */bat = 87; // 模拟电量百分比(实际项目中建议从 ADC 获取真实电压)g_tuya_dp_bat[13] = bat; // 将电量值填入数据帧第14字节g_tuya_dp_bat[14] = 0; // 清空校验和// 计算校验和(累加前13个字节)for(int i=0;i<=13;i++)g_tuya_dp_bat[14]+=g_tuya_dp_bat[i];// 串口打印电量信息,供调试查看printf("[MCU] -> TUYA DP_BAT = %d\r\n",bat);// 通过 UART 发送电量数据给涂鸦 Wi-Fi 模块err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_dp_bat, 15);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;/* ---------------- 温度数据上传 ---------------- */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);// 扩大10倍为整数(单位 0.1°C)方便打包进两个字节temp_tuya = temperature_degC * 10;g_tuya_dp_temp[12] = temp_tuya / 255; // 高字节g_tuya_dp_temp[13] = temp_tuya % 255; // 低字节g_tuya_dp_temp[14] = 0; // 清空校验和// 计算校验和for(int i=0;i<=13;i++)g_tuya_dp_temp[14]+=g_tuya_dp_temp[i];// 串口打印温度信息printf("[MCU] -> TUYA DP_tEMP = %6.2f\r\n",temperature_degC);// 通过 UART 发送温度数据给涂鸦模块err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_dp_temp, 15);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;// printf("Temperature [degC]:%6.2f\r\n", temperature_degC);}}
}
主循环任务调度
● Tap 事件检测
调用 sensor_lsm6dsv16x_tap_loop(),判断是否发生了单击或双击事件,并通过串口打印相应信息。
● 涂鸦模块通信状态更新
调用 tuya_wifi_Update(),维护与涂鸦 Wi-Fi 模块的通信,例如处理心跳包、状态同步等。
● 用户配网按键检测
调用 button_wifi_ap(),检查用户是否通过按键进入 Wi-Fi 配网模式,用于与 App 建立连接。
● 解析涂鸦串口命令
调用 uart0_tuya(),解析从涂鸦模块串口收到的数据帧,比如用户通过 App 控制的指令。
● 周期性上传数据
调用 tuya_up_data(),每 2000ms 上传一次电量和温度数据到涂鸦云平台,供用户实时查看。
● 软件延时 1ms
使用 R_BSP_SoftwareDelay() 进行延时,控制主循环运行频率,避免占用过多 CPU 资源。
void hal_entry(void)
{/* TODO: add your own code here */// 初始化外设(如 UART、I2C、中断引脚、GPIO 等)app_peripheral_init();// 初始化 LSM6DSV16X 传感器,配置单击/双击 Tap 检测功能sensor_lsm6dsv16x_tap_init();while (1){// 处理 LSM6DSV16X 的 Tap 检测事件(单击/双击),并串口打印sensor_lsm6dsv16x_tap_loop();// 处理涂鸦协议状态更新(例如心跳、连接状态等)tuya_wifi_Update(); // 检查按键是否触发 Wi-Fi 配网模式(例如长按按键进入配网)button_wifi_ap(); // 解析 UART 接收的涂鸦协议数据帧(如收到APP指令等)uart0_tuya(); // 每 2000ms 上传一次传感器数据(电量、温度)到涂鸦模块tuya_up_data();// 软件延时 1ms,控制循环节奏(相当于每 1ms 执行一次)R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);}
#if BSP_TZ_SECURE_BUILD/* Enter non-secure code */R_BSP_NonSecureEnter();
#endif
}
相关文章:
RA4M2开发IOT(7)----RA4M2驱动涂鸦CBU模组
RA4M2开发IOT.7--RA4M2驱动涂鸦CBU模组 概述视频教学样品申请硬件准备参考程序初始化 LSM6DSV16X 传感器初始化单双击识别主程序接口RA4M2接口生成UARTUART属性配置R_SCI_UART_Open()函数原型回调函数user_uart_callback0 ()变量定义更新敲击状态DP同步长按进入配网涂鸦协议解析…...
华为公布《鸿蒙编程语言白皮书》V1.0 版:解读适用场景
6 月 22 日消息,华为现已在其开发者网站上架《鸿蒙编程语言白皮书》V1.0 版本,主要围绕鸿蒙 HarmonyOS 整体框架、适用场景、演进策略、未来愿景四大角度进行阐述,文档访问地址(https://developer.huawei.com/consumer/cn/doc/gui…...
多源异构数据接入与实时分析:衡石科技的技术突破
在数字化转型的浪潮中,企业每天产生的数据量呈指数级增长。这些数据来自CRM系统、IoT设备、日志文件、社交媒体、交易平台等众多源头,格式各异、结构混乱、流速不一。传统的数据处理方式如同在无数孤立的岛屿间划着小船传递信息,效率低下且无…...
多设备Obsidian笔记同步:WebDAV与内网穿透技术高效实现教程
文章目录 前言1. Windows开启Webdav服务2. 客户端测试3. 安装Cpolar内网穿透实现公网访问Webdav4. 同步PC端笔记至WebDav4.1 首先需要在IIS中添加md的格式4.2 在Obsidian中安装第三方插件 5. 同步手机端笔记至WebDav 前言 各位好!在数字化浪潮席卷的当下࿰…...
Linux->进程概念(精讲)
引入:本文会讲到的东西有哪些? 注:要讲就讲清楚,所以从0到懂,目录在右侧 一:冯诺依曼体系结构 1:人物介绍 冯诺依曼是一个伟大的人,他提出了一个体系结构,被命名冯诺依…...
【舞蹈】PC-Dance:姿势可控的音乐驱动舞蹈合成
PC-Dance:姿势可控的音乐驱动舞蹈合成 自监督节奏对齐学习音乐到舞蹈的对齐嵌入-PC-Syn 中,依然怒了一种用于 自适应运动图构建(AMGC)的高效方案,可以基于图的优化效率并保持动作的多样性。 舞蹈合成 整体情况 我们的系统主要由音乐到舞蹈对齐嵌 入网络(M2D-Align)和姿势…...
uni-app项目实战笔记22--图片预览和切换
需求描述: 1、图片预览时,通常需要知道,当前预览的是第几张,总共有多少张图片; 2、当用户左右滑动切换预览图片时,当前预览索引需要随着进行切换。 下面简单介绍下实现过程: 1、在图片列表页…...
[特殊字符] AIGC工具深度实战:GPT与通义灵码如何彻底重构企业开发流程
🔍 第一模块:理念颠覆——为什么AIGC不是“玩具”而是“效能倍增器”? ▍企业开发的核心痛点图谱(2025版) 研发效能瓶颈:需求膨胀与交付时限矛盾持续尖锐,传统敏捷方法论已触天花板…...
华为OD机考-用户调度问题-DP(JAVA 2025B卷)
import java.util.Scanner;public class UserScheduling {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt(); // 用户个数int[][] costs new int[n][3]; // 存储每个用户使用A/B/C策略的系统消耗for (int i 0; i …...
【论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测】
论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测 1.摘要&&引言2.方法2.1 预备知识2.2 Fusion-Mamba2.2.1 架构特征提取与多模态融合(FMB模块)FMB的应用与输出2.2.2 关键组件3.2.2.1 SSCS 模块:浅层跨模态特征交互…...
Python 数据分析与可视化 Day 4 - Pandas 数据筛选与排序操作
🎯 今日目标 掌握 Pandas 中 groupby() 的使用方式学会使用 agg() 方法进行多个聚合掌握 pivot_table() 构建透视表结合分组与排序进行更深入的分析 🧮 一、基本分组统计(groupby) ✅ 分组 单列聚合 df.groupby("性别&qu…...
基于Vue.js的图书管理系统前端界面设计
一、系统前端界面设计要求与效果 (一)系统功能结构图 设计一个基于Vue.js的图书管理系统前端界面。要充分体现Vue的核心特性和应用场景,同时结合信息管理专业的知识。要求系统分为仪表盘、图书管理、借阅管理和用户管理四个主要模块&#x…...
FPGA故障注入测试软件使用指南
有数字芯片之母别称的FPGA,是国内在半导体行业率先取得重大突破的细分赛道,正迎来技术和市场形成共振的黄金发展期。 国内拥有最多的应用设计工程师与新兴从业人员,但到目前为止,还没有一款位流级别的专用EDA软件,服务用户日常应用开发所需的调试验证工作。 第一大厂商赛…...
Oracle 数据库查询:单表查询
作者:IvanCodes 日期:2025年6月22日 专栏:Oracle教程 在 Oracle 数据库操作中,查询数据是最频繁、最核心的操作之一。单表查询,即仅从一个表中检索信息,是所有复杂查询的基础。本笔记将系统梳理单表查询的关…...
【DDD】——带你领略领域驱动设计的独特魅力
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
阿里云CentOS系统搭建全攻略:开启云端技术之旅
前期准备:开启云端征程前的必备事项 在当今数字化时代,云计算已成为企业和开发者构建应用和服务的重要基础设施。阿里云作为全球领先的云计算服务提供商,提供了丰富的云计算产品和服务,其中 CentOS 系统在阿里云上的应用非常广泛…...
Flink图之间流转解析:从逻辑构建到物理执行的深度剖析
在Flink强大的数据处理体系中,Table Connectors实现了与外部结构化数据的高效交互,而Flink作业从代码到实际执行的背后,是各类图结构之间的流转与转换。这些图结构承载着作业的逻辑定义、任务划分与资源调度等关键信息,其流转过程…...
详解Redis数据库和缓存不一致的情况及解决方案
数据库与缓存不一致是分布式系统中常见问题,本质是数据在缓存层和存储层出现版本差异。 一、并发写操作导致不一致(最常见) 场景描述 线程A更新数据库 → 线程B更新数据库 → 线程B更新缓存 → 线程A更新缓存 结果:缓存中存储的…...
【CSS】CSS3媒体查询全攻略
媒体查询教程 媒体查询(Media Queries)是CSS3中引入的强大功能,允许内容根据设备特性(如屏幕尺寸、分辨率、方向等)进行自适应调整。以下是媒体查询的详细教程: 基本语法 media mediatype and (media feature) {/* CSS规则 */ }常用媒体类型 all - 所…...
深入理解Spring的ResponseBodyAdvice接口
什么是ResponseBodyAdvice? ResponseBodyAdvice是Spring框架4.2版本引入的一个非常有用的接口,它允许我们在控制器方法执行后、响应体写入前对响应进行统一处理。这个接口为开发者提供了对返回数据进行统一拦截和修改的能力,是Spring MVC响应处理流程中…...
C++法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。
C法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。 在 C 中,法则5指的是:当函数参数是非引用类型(即按值传递)时,传递给函数的实参会进行拷贝初始化(copy initializ…...
Python 使用 Requests 模块进行爬虫
目录 一、请求数据二、获取并解析数据四、保存数据1. 保存为 CSV 文件2. 保存为 Excel 文件打开网页图片并将其插入到 Excel 文件中 五、加密参数逆向分析1. 定位加密位置2. 断点调试分析3. 复制相关 js 加密代码,在本地进行调试(难)4. 获取 …...
day039-nginx配置补充
文章目录 0. 老男孩思想-如何提升能力?1. nginx登录认证功能1.1 创建密码文件1.2 修改子配置文件1.3 重启服务 2. nginx处理请求流程3. 配置默认站点4. location 命令5. 案例1-搭建大型直播购物网站5.1 配置本地hosts解析5.2 编写子配置文件5.3 创建相关目录/文件并…...
K8s入门指南:架构解析浓缩版与服务间调用实战演示
目录 前言一、k8s概念理解1、k8s整体架构(1) Master 主节点(2) Node 工作节点(3) Etcd 键值存储数据库 2、Pod被视为最小的部署单元3、k8s的五种控制器类型(1)…...
如何用AI开发完整的小程序<10>—总结
通过之前9节的学习。 如何用Ai制作一款简单小程序的内容就已经都介绍完了。 总结起来就以下几点: 1、搭建开发制作环境 2、创建页面(需要手动) 3、在页面上制作UI效果(让Ai搞,自己懂了后可以自己调) 4…...
Javaweb - 3 CSS
CSS 层叠样式表(Cascading Style Sheets),能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。 简单来说,HTML 搭建一个毛坯房,C…...
【算法】【优选算法】优先级队列
目录 一、1046.最后一块石头的重量二、703. 数据流中的第 K 大元素三、692. 前 K 个⾼频单词四、295. 数据流的中位数 一、1046.最后一块石头的重量 题目链接:1046.最后一块石头的重量 题目描述: 题目解析: 题意就是让我们拿出提供的数组…...
PaddleOCR + Flask 构建 Web OCR 服务实战
1、前言 随着图像识别技术的发展,OCR(光学字符识别)已经成为很多应用场景中的基础能力。PaddleOCR 是百度开源的一个高性能 OCR 工具库,支持中英文、多语言、轻量级部署等特性。 而 Flask 是一个轻量级的 Python Web 框架,非常适合快速构建 RESTful API 或小型 Web 应用…...
openapi-generator-maven-plugin自动生成HTTP远程调用客户端
Java开发中调用http接口的时候,有很多可选的技术方案,比如:HttpURLConnection、RestTemplate、WebClient、Feign、Retrofit、Okhttp等,今天我们来看一个更优的技术方案OpenAPI Generator(http://openapi-generator.tech/) OpenAP…...
ms-swift 部分命令行参数说明
参考链接 命令行参数 — swift 3.6.0.dev0 文档 Qwen Chat num_train_epochs 训练的epoch数,默认为3 假设你有 1000 条训练样本,并且设置了: num_train_epochs 3 这意味着: 模型会完整地遍历这 1000 条数据 3 次。每一次…...
【学习笔记】深入理解Java虚拟机学习笔记——第10章 前端编译与优化
第10章 前端编译与优化 10.1 概述 1>前端编译器:Javac命令。 【.java文件->.class文件】 2>即时编译器:Hotspot.C1.C2 【.class文件->机器码】 3>提前编译器:JDK的Jaotc等【.java->机器码】 10.2 Javac 编译器 10.2.1 …...
删除node并且重装然后重装vue
参考第一篇文章 node.js卸载与安装超详细教程_node卸载重装-CSDN博客 第二篇文章安装vue Vue安装与配置教程(非常详细)_安装vue-CSDN博客...
Flink源码阅读环境准备全攻略:搭建高效探索的基石
想要深入探索Flink的底层原理,搭建一套完整且适配的源码阅读环境是必经之路。这不仅能让我们更清晰地剖析代码逻辑,还能在调试过程中精准定位关键环节。接下来,结合有道云笔记内容,从开发工具安装、源码获取导入到调试配置&#x…...
【破局痛点,赋能未来】领码 SPARK:铸就企业业务永续进化的智慧引擎—— 深度剖析持续演进之道,引领数字化新范式
摘要 在瞬息万变的数字时代,企业对业务连续性、敏捷创新及高效运营的需求日益迫切。领码 SPARK 融合平台,秉持“持续演进”这一核心理念,以 iPaaS 与 aPaaS 为双擎驱动,深度融合元数据驱动、智能端口调度、自动化灰度切换、AI 智…...
Flink SQL Connector Kafka 核心参数全解析与实战指南
Flink SQL Connector Kafka 是连接Flink SQL与Kafka的核心组件,通过将Kafka主题抽象为表结构,允许用户使用标准SQL语句完成数据读写操作。本文基于Apache Flink官方文档(2.0版本),系统梳理从表定义、参数配置到实战调优…...
Linux 服务器运维:磁盘管理与网络配置
🤵♂️ 个人主页:布说在见 ✍🏻作者简介: 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏…...
PyTorch 入门学习笔记
目录 1 张量 1)张量的初始化和属性 2)张量操作 3)使用 NumPy 进行桥接 2 torch.autograd 1)背景 2)在 PyTorch 中的使用 3)Autograd 的微分机制 4)计算图原理 3 神经网络 1ÿ…...
9大策略深度解析MySQL多表JOIN性能优化
一、多表JOIN的现实挑战 在实际开发中,MySQL多表JOIN场景主要源于两类场景: • 历史遗留系统:老代码中未严格遵循范式设计的SQL语句• 数据库迁移:从Oracle迁移至MySQL时保留的复杂关联查询 这类操作潜藏多重风险: …...
CSS 逐帧动画
CSS 逐帧动画实现指南 逐帧动画(frame-by-frame animation)是一种通过快速连续显示一系列静态图像来创造运动效果的技术。以下是使用CSS实现逐帧动画的几种方法。 1. 使用 steps() 计时函数 这是实现逐帧动画最常用的方法,通过animation-timing-function的steps(…...
UE5 游戏模板 —— ThirdPersonGame
UE5 游戏模板 —— ThirdPersonGame 前言一、初始化旋转控制参数1.参数一2.参数二3.参数三4.参数四 二、输入系统总结 前言 有了前面的铺垫,第三人称模板简直是手到擒来了,我们只需要注意一些初始化的变量是做什么的即可,因为UE的Character …...
java中关于异步转同步的一些解决方案的对比与思考。【spring mvc堵塞式】
文章目录 1、Spring MVC堵塞式编程中的技术方案a) 最简单的方案,使用 DeferredResult 代码如下,代码解读:最终控制台输出如下。用户收到的结果 b) 上点难度,使用redis监听事件,根据事件的不同返回不同的数据…...
【数据结构与算法】数据结构核心概念系统梳理
第一章 绪论:基础概念体系 🚩算法:问题求解步骤的描述。 🚩非递归的算法效率更高。 1.1 逻辑结构 vs 存储结构 维度逻辑结构存储结构(物理结构)定义数据元素之间的逻辑关系数据结构在计算机中的实现方式分类线性/树形/图/集合顺序/链式/索引/散列独立性独立于存储结构…...
《HTTP权威指南》 第7章 缓存
带着问题学习: 缓存如何提高性能如何衡量缓存的有效性缓存置于何处作用最大HTTP如何保持缓存副本的新鲜度缓存如何与其他缓存及服务器通信 web缓存是可以自动保存常见文档副本的HTTP设备。 缓存优点 减少冗余的数据传输,节省网络费用缓解网络瓶颈问题&…...
【Zephyr 系列 28】MCU 闪存文件系统详解:LittleFS + NVS + 块设备设计实战
🧠关键词:Zephyr 文件系统、LittleFS、NVS、Flash 分区、嵌入式存储、断电保护、wear leveling 📌 1. 为什么 MCU 上需要文件系统? 在嵌入式开发中,很多开发者起初直接操作 Flash 保存参数,但随着需求增长…...
ICML 2025 | 时间序列(Time Series)论文总结
ICML 2025将在2025年7月13日至7月19日(周六)在温哥华会议中心举行,本文总结了ICML 2025有关时间序列(Time Series)相关文章,共计63篇。 时间序列Topic:预测,分类,异常检测,生成&…...
理解后端开发中的中间件(以gin框架为例)
中间件(Middleware)是后端开发中的一个核心概念,它在请求(Request)和响应(Response)之间扮演着桥梁角色。以下是关于中间件的详细解释: 基本概念 中间件是在请求到达最终处理程序之前或响应返回客户端之前执行的一系列函数或组件。它可以: 访…...
【分布式技术】Bearer Token以及MAC Token深入理解
Bearer Token以及MAC Token深入理解 **Bearer Token 详解****1. 什么是 Bearer Token?****2. Bearer Token 的构建详情****(1)生成流程****(2)Token 示例(JWT)****(3)Tok…...
WebRTC(七):媒体能力协商
目的 在 WebRTC 中,每个浏览器或终端支持的音视频编解码器、分辨率、码率、帧率等可能不同。媒体能力协商的目的就是: 确保双方能“听得懂”对方发的媒体流;明确谁发送、谁接收、怎么发送;保障连接的互操作性和兼容性。 P2P的基…...
(线性代数最小二乘问题)Normal Equation(正规方程)
Normal Equation(正规方程) 是线性代数中的一个重要概念,主要用于解决最小二乘问题(Least Squares Problem)。它通过直接求解一个线性方程组,找到线性回归模型的最优参数(如权重或系数ÿ…...
【机器学习】数学基础——标量
目录 一、标量的定义 二、标量的核心特征:无方向的纯粹量级 2.1 标量 vs 矢量 直观对比 三、 标量的数学本质:零阶张量 3.1 张量阶数金字塔 3.2 标量的数学特性 四、 现实世界的标量图谱 4.1 常见标量家族 4.2 经典案例解析 五、 标量的运算奥秘…...