RA4M2開發涂鴉模塊CBU.6--RA4M2驅動涂鴉CBU模組
- 概述
- 視頻教學
- 樣品申請
- 參考程序
- 硬件準備
- 接口
- 生成UART
- UART屬性配置
- R_SCI_UART_Open()函數原型
- 回調函數user_uart_callback0 ()
- 變量定義
- 按鍵回調
- 更新按鍵狀態
- DP-LED 同步
- 長按進入配網
- 涂鴉協議解析
- 主循環任務調度
概述
本方案基于瑞薩 RA4M2 MCU 與涂鴉 CBU Wi-Fi & BLE 模組的無縫對接,旨在快速構建智能傳感與控制終端。系統架構由 RA4M2 負責業務邏輯和外設驅動,CBU 模組提供網絡接入與云端交互能力,兩者通過標準串口通協議 (Tuya 通用協議) 完整實現產品功能上報與命令下發。
最近在瑞薩RA的課程,需要樣片的可以加qun申請:925643491。
視頻教學
https://www.bilibili.com/video/BV1ETMBzrEVt
RA4M2開發涂鴉模塊CBU(6)----RA4M2驅動涂鴉CBU模組
樣品申請
https://www.wjx.top/vm/rCrkUrz.aspx
參考程序
https://github.com/CoreMaker-lab/RA4M2_TUYA_CBU
https://gitee.com/CoreMaker/RA4M2_TUYA_CBU
硬件準備
首先需要準備一個開發板,這里我準備的是自己繪制的開發板,需要的可以進行申請。
主控為R7FA4M2AD3CFL#AA0
這里使用的無線硬件是涂鴉CBU模組。
接口
模組跳線接線方式如下。
接入RA4M2開發板。
涂鴉模組原理圖如下所示。
這里的涂鴉可以接入RA4M2的P100和P101。
生成UART
點擊Stacks->New Stack->Connectivity -> UART(r_sci_uart)。
UART屬性配置
R_SCI_UART_Open()函數原型
故可以用 R_SCI_UART_Open()函數進行配置,開啟和初始化UART。
故可以用 R_SCI_UART_Open()函數進行配置,開啟和初始化UART。
/* Open the transfer instance with initial configuration. */err = 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;/* CMD = 0x06(DP 下發)需根據數據點判斷長度*/if(TUYA_wifi_buffer[3]==0x06){/* DP-ID 0x65:亮度,數據長度 0x0004 -> 總長 ≥ 15 */if(TUYA_wifi_buffer[6]==0x65 && UART0_TUYA_LENGTH>=15)//亮度值UART0_TUYA_flag=1;/* DP-ID 0x66:開關,數據長度 0x0001 -> 總長 ≥ 12 */else if(TUYA_wifi_buffer[6]==0x66 && UART0_TUYA_LENGTH>=12)//開關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":")
//0x78, 0x68, 0x6E, 0x7A, 0x74, 0x79, 0x64, 0x67, 0x77, 0x64, 0x63, 0x67, 0x6B, 0x71, 0x78, 0x64 (xhnztydgwdcgkqxd)
//0x22, 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(})
//0xC(校驗碼)
uint8_t g_tuya_product_info[49]={0x55, 0xAA, 0x03, 0x01,0x00, 0x2A,0x7B, 0x22, 0x70, 0x22, 0x3A, 0x22,0x78, 0x68, 0x6E, 0x7A, 0x74, 0x79, 0x64, 0x67, 0x77, 0x64, 0x63, 0x67, 0x6B, 0x71, 0x78, 0x64,0x22, 0x2C, 0x22, 0x76, 0x22, 0x3A, 0x22,0x31, 0x2E, 0x30, 0x2E, 0x30,0x22, 0x2C, 0x22, 0x6D, 0x22, 0x3A,0x30,0x7D,0xCC};//接收模塊發送的查詢產品信息請求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 wifi_Rx=0;//wifi接收指令標志位
uint32_t LED_PWM_num=10;//亮度值
/* DP-ID 0x65:亮度值 */
uint8_t g_tuya_dp_pwm[15]={0x55,0xAA,0x03,0x07,0x00,0x08,0x65,0x02,0x00,0x04,0x00,0x00,0x00,0x0A,0x86};//MCU亮度
bool LED_open=0; /* LED 開關:0=OFF,1=ON */
/* DP-ID 0x66:開關 */
uint8_t g_tuya_dp_switch[12]={0x55,0xAA,0x03,0x07,0x00,0x05,0x66,0x01,0x00,0x01,0x00,0x76};//MCU上報開關狀態
按鍵回調
更新按鍵中斷external_irq4_callback,當按下的時候需要告知無線模塊,無線模塊進行上報,這里定義當按鍵按下時候button_flag為1,讓無線模組進行上報。
/** @brief 由中斷回調函數切換的外部中斷標志 */
static volatile bool s_ext_irq_flag = false;
bool button_flag =0;
/*** @brief 外部中斷 IRQ4 回調函數** 當 ICU 外部中斷發生時調用,切換外部中斷標志。** @param[in] p_args 中斷回調參數(未使用)*/
void external_irq4_callback(external_irq_callback_args_t *p_args)
{(void)p_args;s_ext_irq_flag = !s_ext_irq_flag;button_flag=1;LED_open=!LED_open;
}
更新按鍵狀態
在原有基礎上增加wifi_Update變量,這樣每次使用按鍵之后都可以上報到云端。
/*** @brief 根據中斷標志更新 LED 輸出** 根據 s_ext_irq_flag 狀態設置指定 IOPORT 引腳高低電平。*/
static inline void led_update(void)
{
// R_IOPORT_PinWrite(&g_ioport_ctrl,
// BSP_IO_PORT_01_PIN_04,
// s_ext_irq_flag ? BSP_IO_LEVEL_HIGH : BSP_IO_LEVEL_LOW);if(button_flag){button_flag=0;wifi_Update=1;R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_01_PIN_04, LED_open);}
}
DP-LED 同步
該函數先檢查 wifi_Update 標志,若需上報,則:
- 本地硬件同步
○ 調整 GPT2 定時器占空比以設置 LED 呼吸燈亮度。 - 構建并發送涂鴉 DP-亮度幀
○ 填充 g_tuya_dp_pwm 數組中的亮度字段并校驗。
○ 通過 UART0 向涂鴉模塊下發亮度變化數據點。 - 構建并發送涂鴉 DP-開關幀
○ 更新 g_tuya_dp_switch 中的開關字段并校驗。
○ 通過 UART0 向涂鴉模塊下發 LED 開關狀態。
整個流程實現了“本地 LED 狀態 → MCU 構造涂鴉數據幀 → Wi-Fi 模塊上報至云端”的閉環同步。
/******************************************************************************************************************** @brief 向涂鴉模塊上報最新 DP 數據 & 本地硬件同步*******************************************************************************************************************/
void tuya_wifi_Update(void)
{if(wifi_Update){printf("wifi_Update\n");wifi_Update=0;/*更新 GPT 占空比 ---------------------------------------*//* 計數器周期在 FSP 配置為 9000(舉例),因此 1% 亮度 = 9000 / 1000 = 9 個計數。 */fsp_err_t err = R_GPT_DutyCycleSet(&g_timer2_ctrl,9000-LED_PWM_num * 9, // 新比較值GPT_IO_PIN_GTIOCA); // 使用 A 通道assert(FSP_SUCCESS == err);/* 更新 DP 幀 ------------------------------------------------------*/g_tuya_dp_pwm[12]=LED_PWM_num>>8;g_tuya_dp_pwm[13]=LED_PWM_num;g_tuya_dp_pwm[14]=0x86-0x0A+g_tuya_dp_pwm[12]+g_tuya_dp_pwm[13];/*發送 DP-亮度 */printf("[MCU] -> TUYA DP_PWM = %d\r\n",LED_PWM_num);err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_dp_pwm, 15);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;/*發送 DP-開關 */printf("[MCU] -> TUYA DP_SW=%d\r\n",LED_open);g_tuya_dp_switch[10]=LED_open;g_tuya_dp_switch[11]=0x76+g_tuya_dp_switch[10];//上報LED開關err = R_SCI_UART_Write(&g_uart0_ctrl, g_tuya_dp_switch, 12);if(FSP_SUCCESS != err) __BKPT();while(uart_wifi_TX_flag == false){}uart_wifi_TX_flag = false;}}
長按進入配網
該函數檢測按鍵引腳狀態,累計長按時間。當按鍵持續按下達到 3 秒(3000 次 1 ms 延時)時,觸發一次配網模式:
- 打印日志 [BTN] wifi_ap_mode
- 通過 UART0 向涂鴉模塊發送配網指令幀 g_tuya_wifi_cfg(0x05 命令)。
短按或松開時則重置計時,避免誤觸發。
/******************************************************************************************************************** @brief 長按按鍵 3 s 進入配網模式 (發送 0x05 命令)*******************************************************************************************************************/
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;
○ 0x06:DP 下發,打印 [TUYA] <DP-switch=ON/OFF> 或 [TUYA] <DP-PWM=值>,更新本地 LED 狀態與亮度,并置 wifi_Update。 - 清空接收緩存,為下一幀解析復位。
/******************************************************************************************************************** @brief 解析完成后,根據 CMD 打印清晰友好的日志*******************************************************************************************************************/
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)//報告設備聯網狀態{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跟新標志位}}else if(TUYA_wifi_buffer[3]==0x06)//接收模塊DP下發信息{if(TUYA_wifi_buffer[6]==0x66)//LED開關{wifi_Update=1;//wifi跟新標志位LED_open=TUYA_wifi_buffer[10];button_flag=1;printf("[TUYA] <DP-switch=%s\r\n", LED_open ? "ON" : "OFF");}else if(TUYA_wifi_buffer[6]==0x65)//LED亮度值{wifi_Update=1;//wifi跟新標志位LED_PWM_num=0;LED_PWM_num+=TUYA_wifi_buffer[13];LED_PWM_num+=TUYA_wifi_buffer[12]*256;printf("[TUYA] <DP-PWM=%ld\r\n", LED_PWM_num);}}}// 清除數組memset(TUYA_wifi_buffer, 0, UART0_LENGTH);// 同時把當前有效長度歸零UART0_TUYA_LENGTH = 0;}
}
主循環任務調度
在主循環中依次執行以下任務:
- LED 更新(led_update())— 處理普通按鍵翻轉 LED;
- 涂鴉數據上報(tuya_wifi_Update())— 若有變更則同步 DP 數據;
- 配網按鍵檢測(button_wifi_ap())— 長按 3 s 觸發 Wi-Fi 配網命令;
- 涂鴉協議解析(uart0_tuya())— 解析并響應下行命令;
/* 主循環 */while (true){led_update(); /* 切換普通 LED */
// pwm_breathe_update(); /* 呼吸燈效果 */tuya_wifi_Update(); /* 涂鴉數據上報 */button_wifi_ap(); /* 配網按鍵檢測 */uart0_tuya(); /* 涂鴉協議解析 */R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);}