MCU SDK 移植

更新时间:2025-12-24 03:06:24下载pdf

前言

MCU SDK 是根据涂鸦开发者平台定义的产品功能自动生成的 MCU 代码,能够协助您快速完成 MCU 程序的开发。本文为您介绍 Zigbee 通用方案移植 MCU SDK 的流程以及注意事项。本文将介绍 MCU SDK 的移植流程以及开发注意事项。

资源要求

MCU SDK 对资源要求如下:

  • FLASH:4 KB
  • RAM:与设备功能的 DP 数据长度有关,约为 100 字节。如果使用 MCU OTA 升级功能,RAM 需大于 260 字节。
  • 函数嵌套级数:9 级

文件结构

在移植 MCU SDK 前,您需要下载对应的 SDK 源码,具体请参考 MCU 低代码开发。下载后,MCU SDK 由以下文件组成:

文件 说明
zigbee.h 提供通用宏的定义
system.h system.h 包含以下信息:
  • 函数声明:通用功能函数、队列处理函数、组帧函数
system.c system.c 包含以下信息:
  • 函数声明:队列处理函数
  • 函数定义:通用功能函数、队列处理函数、组帧函数
protocol.h protocol.h 包含以下信息:
  • DP ID 宏定义
  • 协议信息:帧字段、命令字等
  • 命令字相关信息:DP 数据类型、Zigbee 模组网络状态、网络参数等
  • 函数声明:帧接收函数、DP 处理函数
protocol.c protocol.c 包含以下信息:
  • 协议、命令字相关定义
  • 函数声明:帧接收处理函数(完整流程)、DP 消息处理函数
  • 函数定义:DP 处理函数、帧接收处理函数(完整流程)、帧发送函数、DP 消息处理函数
mcu_api.h mcu_api.h 包含以下信息:
  • 配置项:设备信息、调试信息打印开关、串口及队列缓冲区尺寸、命令字相关信息等
  • 函数声明:串口服务函数、串口数据发送函数、MCU 唤醒 Zigbee 函数、帧接收处理函数(用户接口)、帧发送函数
mcu_api.c mcu_api.c 包含以下信息:
  • 函数定义:串口服务函数、串口数据发送函数、帧接收处理函数(待用户实现)、唤醒相关函数

开发时,要重点关注 mcu_api.hmcu_api.c 文件,需要手动修改的代码部分都在这两个接口文件中。此外,还有涂鸦开发者平台配置相关的 protocol.hprotocol.c 文件,需要根据实际需求进行检查和修改。

移植流程

MCU SDK 移植流程如下:

  1. 实现 MCU 平台外设功能
  2. 移植 SDK 源文件
  3. 修改接口文件
  4. 参数配置项
  5. 修改协议文件

第一步:实现 MCU 平台外设功能

在正式移植 MCU SDK 前,需要您在自己的 MCU 平台上实现至少以下外设功能:

  • UART 串口:发送和接收和模组的通讯数据功能。
  • 定时器:计时功能。
  • GPIO:控制模组唤醒和自身被唤醒功能。

第二步:移植 SDK 源文件

将 MCU SDK 文件夹中的 .c.h 文件分别添加到项目工程的源文件和头文件引用路径下。

第三步:修改接口文件

修改接口文件 mcu_api.hmcu_api.c,主要分为四个部分:

Part 1:串口接收数据处理

需要把 MCU 串口接收到的数据以单字节的形式传入 uart_servive_rx_store(),然后周期性调用(主循环调用或定时器定时调用)uart_service_parse() 函数。

Part 2:唤醒方法确认

需要确认自身开发设备是否是低功耗设备,接口文件相关配置宏如下:

///< <USER MUST NOT CHANGE>
#define ROUTER_DEVICE                           1
#define SLEEP_END_DEVICE                        2
#define SCENE_SWITCH_DEVICE                     3

#define LOW_LEVEL_WAKE_UP                       1
#define LOW_PULSE_WAKE_UP                       2
///< END <USER MUST NOT CHANGE>

///< <USER MUST CHECK AND CHANGE>
#define MCU_VER                                 "1.0.0"
#define DEVICE_TYPE                             ROUTER_DEVICE
#if (DEVICE_TYPE == SLEEP_END_DEVICE)
#define MCU_WAKEUP_MODULE_METHOD                LOW_LEVEL_WAKE_UP
#endif
///< END <USER MUST CHECK AND CHANGE>

如果您开发的是非低功耗设备(DEVICE_TYPE = ROUTER_DEVICE 或者 DEVICE_TYPE = SCENE_SWITCH_DEVICE),可以直接跳过这一部分修改。

如果您开发的是低功耗设备(DEVICE_TYPE = SLEEP_END_DEVICE),需要选择唤醒方式,实现基本流程如下(细节部分请参考 串口协议章节:低功耗唤醒部分),这一部分需要结合自身 MCU 平台定时器、GPIO 等外设实现一些等待和超时的逻辑。

#if (DEVICE_TYPE == SLEEP_END_DEVICE)
/* 
 * "g_zg_wait_mcu_time" depends on the user's chip platform, users should design it by themselves,
 * users can set the "g_zg_wait_mcu_time" through the function "mcu_tx_set_zg_wait_mcu_time()".
 */
#if (MCU_WAKEUP_MODULE_METHOD == LOW_LEVEL_WAKE_UP)
void mcu_wakeup_zg_level_method(unsigned char *data, unsigned short data_len)
{
    /*
    *< USER TODO: wakeup zigbee module by low-level method end send data;
    *sequence chart:
    *                         ____                                      _____
    * 
    *  mcu wakeup module pin:     |<---------------t1----------------->|
    *                             |                                    |
    *                             |____________________________________|
    * 
    *  mcu uart tx pin      :     |<-t0->|<-send()->|                  |
    *                             |______|__________|__________________|
    * 
    *  mcu uart rx pin      :     |                 |<-recv()->|       |
    *                             |_________________|__________|_______|
    * 
    *  t0: "g_mcu_wait_zg_time" mcu wait module wakeup time, 1-10ms, ZT module >=10ms;
    * 
    *  t1: "g_wakeup_stay_time" mcu wakeup module pin stay time, t1 must < 120s;
    * 
    *  1. pull down the mcu wakeup module pin;
    * 
    *  2. wait t0;
    * 
    *  3. uart_send_bytes() send data;
    * 
    *  4. wait uart_servive_rx_store() finish receiving data and uart_service_parse() 
    * 
    *     finish analysing data;
    * 
    *  6. pull up the mcu wakeup module pin; then the module will enter sleep mode after x ms;
    */    

}
#elif (MCU_WAKEUP_MODULE_METHOD == LOW_PULSE_WAKE_UP)
void mcu_wakeup_zg_pulse_method(unsigned char *data, unsigned short data_len)
{
    /*
    *< USER TODO: wakeup zigbee module by low-level pulse method end send data;
    *sequence chart:
    *                         ____        ________________________________
    * 
    *  mcu wakeup module pin:     |<-t0->|                             |
    *                             |______|                             |
    * 
    *  mcu uart tx pin      :     |      |<-send()->|                  |
    *                             |______|__________|__________________|
    * 
    *  mcu uart rx pin      :     |                 |<-recv()->|       |
    *                             |_________________|__________|_______|
    * 
    *  t0: "g_mcu_wait_zg_time" mcu wakeup module pin pull down stay time;ZS module [1, 5]ms, ZT module >=10ms;
    *
    *  1. mcu wakeup module pin generate a low-level pulse lasting for t0 ms;
    * 
    *  2. uart_send_bytes() send data;
    * 
    *  3. wait uart_servive_rx_store() finish receiving data and uart_service_parse() 
    *     finish analysing data;
    */  
}
#endif
#endif

Part 3:串口发送数据

需要在函数 uart_send_bytes() 定义中实现串口单字节发送功能。

Part 4:接收帧解析回调实现

MCU SDK 已预留了协议解析后需要对特定命令进行一些处理的回调接口,可以根据自己的需求来填充这些函数,具体接口展示如下:

void mcu_recv_factory_recovery_cb(void)
{
    //< USER TODO
}
  . . .
  . . .

  省略部分

  . . .
  . . .
void mcu_recv_gw_nwk_status_cb(unsigned char nwk_status)
{
    //< USER TODO
}

第四步:参数配置项

另外,还有一些可选择修改的宏定义,具体介绍如下:

宏定义 说明 注意事项
MCU_VER MCU 固件版本 MCU 软件版本,默认 1.0.0;若 MCU 需要 OTA 功能,每次升级 MCU 固件需要更改新的 MCU 版本号
DEVICE_TYPE 设备类型 必须选择以下中的一项:
  • ROUTER_DEVICE
    标准功耗(非场景开关)
  • SLEEP_END_DEVICE:低功耗
  • SCENE_SWITCH_DEVICE
    标准功耗(场景开关)
MCU_WAKEUP_MODULE_METHOD 低功耗设备 MCU 唤醒方式 必须从下面选择一项:
  • LOW_LEVEL_WAKE_UP
    低电平唤醒
  • LOW_PULSE_WAKE_UP
    低电平脉冲唤醒
SUPPORT_RECEIVE_BROADCAST_DATA MCU 是否接收广播消息 默认:0x01
  • 0x00:不接收
  • 0x01:接收
ZG_WAIT_MCU_TIME_DEFAULT Zigbee 设备(低功耗设备)唤醒 MCU 后,等待 MCU 串口通讯功能就绪的时间 默认:10 ms
MCU_WAIT_ZG_TIME_DEFAULT MCU 唤醒 Zigbee 设备(低功耗设备)后,等待 Zigbee 设备(低功耗设备)串口通讯功能就绪的时间 默认:10 ms
UART_RX_BUF_LEN_LMT 串口接收缓冲区数组大小 默认:256
可以根据自身需求减小
UART_TX_BUF_LEN_LMT 串口发送缓冲区数组大小 默认:256
可以根据自身需求减小
UART_QUEUE_BUF_LEN_LMT 串口接收队列大小 默认:512
可以根据自身需求减小
OTA_PACKET_SIZE 单包 OTA 包大小 默认:0x30
最大 0x30,可以根据自身需求减小
MCU_SDK_DEBUG
PRINT_DEBUG(fmt, …)
日志相关接口 默认:关闭
可以根据自身需求配置打印函数后打开

第五步:修改协议文件

协议相关文件 protocol.hprotocol.c 是通过在涂鸦开发者平台配置 DP 自动生成的,需要核查几处是否和自己预期的 DP 信息一致(可以在代码中全局搜索 USER_CHECK_MSG),以下为具体代码示例(仅供参考,实际情况中需根据您的配置决定):

产品 PID 确认

//< USER_CHECK_MSG 
///<product key(pid)  the same as yours in the Tuya Developer Platform

#define PRODUCT_KEY "xn7xzluu"    //开发平台创建产品后生成的 16 位字符的产品唯一标识

//< USER_CHECK_MSG END

DP ID 宏定义确认

//< USER_CHECK_MSG 
///< dp id define
//开关 1(可下发可上报)
//备注:
#define DPID_SWITCH_1 1

. . .

省略部分

. . .

//随机定时(可下发可上报)
//备注:/#1 协议版本
///#2 节点长度
///#3 通道号(bit0:开关;bit1-bit7:通道号)
///#4 星期
///#5#6 起始时间(min)
///#7#8 结束时间(min)
#define DPID_RANDOM_TIMING 210

//< USER_CHECK_MSG END

DP ID 命令表确认

//< USER_CHECK_MSG
/*----------------------------------------------------------
 *                        DP list
 * 1. please check the ID and type of each DP.
 *--------------------------------------------------------*/
const DOWNLOAD_CMD_S download_cmd[] =
{
  {DPID_SWITCH_1, DP_TYPE_BOOL},
  {DPID_SWITCH_2, DP_TYPE_BOOL},
  {DPID_SWITCH_3, DP_TYPE_BOOL},
  {DPID_SWITCH_4, DP_TYPE_BOOL},
  {DPID_SWITCH_5, DP_TYPE_BOOL},
  {DPID_SWITCH_6, DP_TYPE_BOOL},
  {DPID_COUNTDOWN_1, DP_TYPE_VALUE},
  {DPID_COUNTDOWN_2, DP_TYPE_VALUE},
  {DPID_COUNTDOWN_3, DP_TYPE_VALUE},
  {DPID_COUNTDOWN_4, DP_TYPE_VALUE},
  {DPID_COUNTDOWN_5, DP_TYPE_VALUE},
  {DPID_COUNTDOWN_6, DP_TYPE_VALUE},
  {DPID_SWITCH_ALL, DP_TYPE_BOOL},
  {DPID_RELAY_STATUS, DP_TYPE_ENUM},
  {DPID_LIGHT_MODE, DP_TYPE_ENUM},
  {DPID_BACKLIGHT_SWITCH, DP_TYPE_BOOL},
  {DPID_SWITCH_INCHING, DP_TYPE_STRING},
  {DPID_ADD_ELE, DP_TYPE_VALUE},
  {DPID_CUR_CURRENT, DP_TYPE_VALUE},
  {DPID_CUR_POWER, DP_TYPE_VALUE},
  {DPID_CUR_VOLTAGE, DP_TYPE_VALUE},
  {DPID_TEST_BIT, DP_TYPE_VALUE},
  {DPID_VOLTAGE_COE, DP_TYPE_VALUE},
  {DPID_ELECTRIC_COE, DP_TYPE_VALUE},
  {DPID_POWER_COE, DP_TYPE_VALUE},
  {DPID_ELECTRICITY_COE, DP_TYPE_VALUE},
  {DPID_RELAY_STATUS_1, DP_TYPE_ENUM},
  {DPID_RELAY_STATUS_2, DP_TYPE_ENUM},
  {DPID_RELAY_STATUS_3, DP_TYPE_ENUM},
  {DPID_RELAY_STATUS_4, DP_TYPE_ENUM},
  {DPID_RELAY_STATUS_5, DP_TYPE_ENUM},
  {DPID_RELAY_STATUS_6, DP_TYPE_ENUM},
  {DPID_CYCLE_TIMING, DP_TYPE_RAW},
  {DPID_RANDOM_TIMING, DP_TYPE_RAW},
};

//< USER_CHECK_MSG END

DP 消息下发处理函数

这部分需确认每一个下发的 DP 是否需要执行对应操作;若需自行实现该逻辑,在 dp_msg_handle () 函数中需重点关注各 Case 分支对应的处理函数 —— 例如针对 “开关 1 下发消息”,需关注 dp_download_switch_1_handle () 函数,其余场景可依此类推。

/*****************************************************************************
函数名称:dp_download_switch_1_handle
功能描述:针对 `DPID_SWITCH_1` 的处理函数
输入参数:value:数据源数据
                   length:数据长度
返回参数:成功返回:SUCCESS,失败返回:ERROR
使用说明:可下发可上报类型,需要在处理完数据后上报处理结果至 App
*****************************************************************************/
static unsigned char dp_download_switch_1_handle(const unsigned char value[], unsigned short length)
{
    //示例:当前 DP 类型为 BOOL
    unsigned char ret;
    //0:off/1:on
    unsigned char switch_1;
    
    switch_1 = mcu_get_dp_download_bool(value,length);
    if(switch_1 == 0) {
        //bool off
    }else {
        //bool on
    }
  
    //There should be a report after processing the DP
    ret = mcu_dp_bool_update(DPID_SWITCH_1,switch_1);
    if(ret == SUCCESS)
        return SUCCESS;
    else
        return ERROR;
}

//< USER_CHECK_MSG END
static unsigned char dp_msg_handle(unsigned char dp_id, const unsigned char *value, unsigned short length)
{
    unsigned char ret;
    if (NULL == value) {
        return ERROR;
    }
    
    switch (dp_id) {
        case DPID_SWITCH_1:
            //开关 1 处理函数
            ret = dp_download_switch_1_handle(value,length);
        break;

    . . .

    省略部分

    . . .

        case DPID_RANDOM_TIMING:
            //随机定时处理函数
            ret = dp_download_random_timing_handle(value,length);
        break;

        default :
            break;
    }
    return ret;
}

DP 消息上报处理函数

这一部分由于模块在某些场景需要知道 MCU DP 数据(如重新配网、重启等),所以需要用户实现 “上报所有 DP 数据” 和 “上报单个 DP 数据” 功能,分别对应函数 all_data_update()specific_dp_update() ,以此完成 App 和 MCU 数据同步。

static void all_data_update(void)
{
    #error  "Please handle the examples of data that can be issued and reported,as well as the data that can only be reported here. After processing,delete this line."
    //< USER_CHECK_MSG
    mcu_dp_bool_update(DPID_SWITCH_1, 1); //BOOL 型数据上报;

    . . .
    省略部分
    . . .

    //< USER_CHECK_MSG END
}

static void specific_dp_update(unsigned char dp_id)
{
    switch (dp_id) {
         case DPID_SWITCH_1: {
              mcu_dp_bool_update(DPID_SWITCH_1, 1); //BOOL 型数据上报;
              break;
         }

    . . .
    省略部分
    . . .

        default: {
            break;
        }
    }
}   default :
            break;
    }
    return ret;
}

支持与帮助

  • 如需详细了解通用对接串口协议,可参考 串口协议
  • 在开发过程遇到问题,您可以登录 TuyaOS 开发者论坛 MCU SDK 开发版块 进行咨询。