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
}