MCU SDK 移植

更新时间:2026-01-14 07:04:32下载pdf

前言

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

代码框架

开发者需要着重关注下图中的红色部分,需自行实现。

MCU SDK 移植
MCU SDK 移植

文件结构

在移植 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() 函数。

void USART1_IRQHandler(void)
{
    uint8_t data;

    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        data = USART_ReceiveData(USART1);
        void uart_servive_rx_store(unsigned char value);
        uart_servive_rx_store(data);
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

int main(void)
{
    app_uart_init();
    MAIN_DEBUG(" mcu sdk example ");
    
    while(1)
    {
        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() 定义中实现串口单字节发送功能。

void uart_send_byte(unsigned char value)
{
    void app_uart_send_byte(unsigned char data);
    app_uart_send_byte(value);
}

void uart_send_bytes(unsigned char *data, unsigned short data_len)
{
    if ((NULL == data)) {
        return;
    }

    for (unsigned short i = 0; i < data_len; i++) {
        uart_send_byte(data[i]);
    }
}

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;
}

支持与帮助

  • 如需详细了解通用对接串口协议,可参考 串口协议
  • 这里展示了一个zigbee mcu sdk移植后的STM32的例程,开发者可以下载参考下。
  • 在开发过程遇到问题,您可以登录 TuyaOS 开发者论坛 MCU SDK 开发版块 进行咨询。