MCU SDK 移植

更新时间:2024-04-08 08:18:04下载pdf

MCU SDK 是根据涂鸦 IoT 开发平台定义的产品功能自动生成的 MCU 开发包,能够快速协助开发者完成 MCU 程序的开发。为了减少使用涂鸦 Wi-Fi 通用串口协议的对接成本,MCU SDK 已搭建通讯及协议解析架构,只需做简单的串口适配即可实现与无线通讯模组通讯,快速实现产品智能化。
本文将介绍 MCU SDK 的移植流程以及开发注意事项。

资源要求

MCU SDK 对 资源要求如下:

  • 内存:4KB。

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

  • 函数嵌套级数:9 级。

    如果当前项目的 MCU 资源不足,可自行对接 串口协议,SDK 中的函数可以作为使用参考。

文件结构

下载 MCU SDK 的步骤,请参考 MCU 低代码开发。下载后,MCU SDK 由以下文件组成:

文件 说明
mcu_api.c
包含可供调用的函数。
mcu_api.h mcu_api.c 中的函数声明。
protocol.c 包含协议数据体的内容处理函数。请根据项目需求,在相应函数内添加代码,获取涂鸦无线模组向 MCU 发送的数据。
protocol.h protocol.h 包含以下信息:
  • MCU 要发送至涂鸦无线模组初始化所需的参数。
  • SDK 裁剪所定义的宏。用户可根据需要的功能打开相应的宏定义。
  • protocol.c 中的函数声明。
system.c 串口通讯协议解析的具体实现。
system.h system.h 包含以下信息:
  • 协议命令字的定义。
  • 部分全局变量的定义。
  • system.c 中的函数声明。
wifi.h 包含相关宏定义。

针对部分特色产品品类的 mcu_sdk,宏定义文件名会略有不同。

移植流程

  1. 编写 MCU 基础程序,移植 SDK 文件。
  2. 根据当前项目需要的功能,确认 protocol.h 宏定义。
  3. 移植 protocol.c 文件及函数调用。
  4. 添加 DP 上报下发函数调用。
  5. 添加配网功能及指示灯函数。
  6. (可选)添加产测功能。
  7. (可选)添加 MCU OTA 功能。
  8. (可选)添加扩展功能。

第一步:编写 MCU 程序和移植 SDK

  1. 在原项目工程中,完成 MCU 相关外设初始化,包括串口、外部中断(按键)、定时器(指示灯闪烁)等。

  2. 将 MCU SDK 文件夹中的 .c.h 文件添加至相应头文件引用路径。

    MCU SDK 移植

第二步:确认 protocol.h 宏定义

确认产品信息

  1. 定义 PID。PRODUCT_KEY 为产品 PID 宏定义。PID 即产品 ID,为每个产品的唯一标识,可在 涂鸦 IoT 开发平台 的产品详情页面查询。

    #define PRODUCT_KEY "ax23rawjo4np****"
    

    如果 PRODUCT_KEY 和产品 PID 不一致,请在 硬件开发 > 下载资料 下载最新 SDK 开发包后重试。

  2. 定义版本号。MCU_VER 为软件版本,默认为 1.0.0。若 MCU 需要 OTA 功能,每次升级 MCU 固件需要更改新的 MCU 版本号。

    #define MCU_VER "1.0.0"
    

  3. 定义 Wi-Fi 配网模式。CONFIG_MODE 为配网方式,支持默认模式、安全模式、防误触模式。建议选择 防误触 模式。

    三种配网模式说明:

    • 默认模式:热点和快连配网模式互相切换。如果模组未配网或移除配网,上电后会处于待配网状态并一直保持。

      #define CONFIG_MODE     CONFIG_MODE_DEFAULT
      
    • 安全模式:模组出厂上电后处于非配网状态,需要 MCU 发送重置命令才会进入相应的配网模式。当设备处于配网状态一段时间(默认 3 分钟)后,未被用户配走,模组便重新进入非配网状态,需要重新收到重置命令才会重新进入配网模式。

      #define CONFIG_MODE     CONFIG_MODE_LOWPOWER
      
    • 防误触模式:模组被用户配网成功后,本地(MCU 发送重置命令)重置后,设备处于待配网状态,一段时间(默认 3 分钟)后没有被重新配网,模组会自动恢复重置前的用户网络连接。本地重置后设备中途异常断电,上电也会自动恢复重置前的用户网络连接。这种模式下,只有当设备被用户从 App 端移除,设备才不会记录上次用户的网络连接并重连。

      #define CONFIG_MODE     CONFIG_MODE_SPECIAL
      
    • 定义配网模式时长。当模组工作模式选择了 安全模式防误触模式,可以打开 CONFIG_MODE_DELAY_TIME 宏定义,设置配网模式的保持时间,设置范围为 3~10 分钟。默认值为 3 分钟。

      #define CONFIG_MODE_DELAY_TIME    10
      
  4. 选择固定配网模式。可以通过 CONFIG_MODE_CHOOSE 设置,使热点和快连配网模式同时存在或者仅保存热点模式。

    • 0:配网方式为热点和快连模式共存,此时无需切换配网方式。

      #define CONFIG_MODE_CHOOSE        0
      
    • 1:配网方式只有热点模式。

      #define CONFIG_MODE_CHOOSE        1
      
  5. 设置功耗模式。可以通过 LONG_CONN_LOWPOWER 开启模组低功耗模式,默认关闭。

    //#define LONG_CONN_LOWPOWER        0         //关闭低功耗模式
    //#define LONG_CONN_LOWPOWER        1         //打开低功耗模式
    

定义收发缓存

  • 串口接收缓存:大小受到串口数据处理被调用的频率影响。如果 MCU 对串口数据的处理较快,串口接收缓存大小可适当减小。

  • 串口发送缓存:大小须大于数据最长的 DP 数据长度。

  • 串口数据处理缓存:大小须大于数据最长的 DP 数据长度,还要根据是否需要 OTA 功能以及是否需要天气服务、天气服务类型数量和天数来调整大小,需要大于最大数据量的大小。

    /******************************************************************************
                            3:定义收发缓存
                        如当前使用 MCU 的 RAM 不够,可修改为 24
    ******************************************************************************/
    #ifndef SUPPORT_MCU_FIRM_UPDATE
    #define WIFI_UART_RECV_BUF_LMT          16              //串口数据接收缓存区大小,如 MCU 的 RAM 不够,可缩小
    #define WIFI_DATA_PROCESS_LMT           24              //串口数据处理缓存区大小,根据用户 DP 数据大小量定,必须大于 24
    #else
    #define WIFI_UART_RECV_BUF_LMT          128             //串口数据接收缓存区大小,如 MCU 的 RAM 不够,可缩小
    
    //请在此处选择合适的串口数据处理缓存大小(根据上面 MCU 固件升级包选择的大小和是否开启天气服务,选择开启多大的缓存)
    #define WIFI_DATA_PROCESS_LMT           300             //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 256,则缓存必须大于 260。若开启天气服务,则需要更大
    //#define WIFI_DATA_PROCESS_LMT           600             //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 512,则缓存必须大于 520。若开启天气服务,则需要更大
    //#define WIFI_DATA_PROCESS_LMT           1200            //串口数据处理缓存大小,如需 MCU 固件升级,若单包大小选择 1024,则缓存必须大于 1030。若开启天气服务,则需要更大
    
    #endif
    
    #define WIFIR_UART_SEND_BUF_LMT         48              //根据用户 DP 数据大小量确定,必须大于 48
    

定义模组工作方式

  • 如配网按键和 LED 接在 MCU 端,即选择 模组和 MCU 配合处理 工作模式(常用),保持 WIFI_CONTROL_SELF_MODE 宏定义处于被注释状态。

    //#define         WIFI_CONTROL_SELF_MODE                       //Wi-Fi 自处理按键及 LED 指示灯,如为 MCU 外接按键/LED 指示灯请关闭该宏
    
  • 如配网指示灯和按键是接在 Wi-Fi 模组上的,即选择 模组自处理 工作模式,开启 WIFI_CONTROL_SELF_MODE 宏定义,然后根据实际的硬件连接,将指示灯和按键所连接的 GPIO 脚位填入下面两个宏定义。

    #ifdef          WIFI_CONTROL_SELF_MODE                        //模组自处理
        #define     WF_STATE_KEY            14                    //Wi-Fi 模组状态指示按键,请根据实际 GPIO 管脚设置
        #define     WF_RESERT_KEY           0                     //Wi-Fi 模组重置按键,请根据实际 GPIO 管脚设置
    #endif
    

第三步:移植 protocol.c 文件及函数调用

  1. wifi.h 文件保存至存放 Wi-Fi 相关文件的文件夹中,例如 main.c

  2. 在 MCU 串口及其他外设初始化后,调用 mcu_api.c 文件中的 wifi_protocol_init() 函数。

  3. 将 MCU 串口单字节发送函数填入 protocol.c 文件中的 uart_transmit_output() 函数内,并删除 #error。示例如下:

    /**
        * @brief 串口发送数据
        * @param[in] {value} 串口要发送的 1 字节数据
        * @return Null
        */
    void uart_transmit_output(unsigned char value)
    {
        //#error "请将 MCU 串口发送函数填入该函数,并删除该行"
        UART3_SendByte(value);
    /*
        //Example:
        extern void Uart_PutChar(unsigned char value);
        Uart_PutChar(value);                                    //串口发送函数
    */
    }
    
  4. 在串口接收中断服务函数里面调用 mcu_api.c 文件内的 uart_receive_input() 函数,并将接收到的字符作为参数传入。示例如下:

    void USART3_IRQHandler(void)
    {
        unsigned char Res=0;
    
        if((USART3->SR&UART_FLAG_RXNE) != 0)
        {
            Res=USART3->DR;
            uart_receive_input(Res);
        }
    }
    
  5. MCU 进入 while(1) 循环后调用 mcu_api.c 文件内的 wifi_uart_service() 函数。main.c 中示例代码结构如下:

    #include "wifi.h"
    ...
    void main(void)
    {
        wifi_protocol_init();
        ...
        while(1)
        {
            wifi_uart_service();
            ...
        }
        ...
    }
    
    • MCU 必须在 while(1) 中直接调用 mcu_api.c 内的 wifi_uart_service() 函数。
    • 程序正常初始化完成后,建议不进行关中断,如必须关中断,关中断时间必须短。关中断会引起串口数据包丢失,请勿在中断内调用上报函数。

第四步:调用 DP 数据上报和下发函数

设备功能(Data Point,DP)主要有 6 种数据类型:

类型 说明
bool 通常为开关类的 DP,例如开关、ECO 和屏显。
enum 通常用作有多种状态的 DP,例如工作模式、风速和风摆位置。
value 通常用作数值类型的 DP,例如设定温度值、当前温度值和电量。
fault 通常用于故障的上报,数据常用 bitmap 格式显示。
string 通常用作字符串类型的 DP。需要以字符串形式传输的 DP 可以使用此类型。部分不便于使用 bool、enum、value 或 fault 类型的 DP,也可用此类型。
raw 通常用作需要透传但对数据格式无要求(明文或者加密)的数据。发送端和接收端对数据的格式、组包和解析方式需要统一。

情景一:所有 DP 数据上报

在模组重启或者重新配网后,Wi-Fi 模组主动下发状态查询指令,此时需要 MCU 上报设备所有 DP 状态给 Wi-Fi 模组进行同步。

  1. 打开 protocol.c 找到函数 all_data_update(void)

  2. 把所有需要上报的 DP 初值填入相应上报函数,为 App 提供开机显示初值。

    请勿随意调用 all_data_update() 函数,该函数会在特定时间主动调用。

    /**
    * @brief 系统所有 DP 信息上传,实现 App 和 MCU 数据同步
    * @param Null
    * @return Null
    * @note   此函数 SDK 内部需调用,MCU 必须实现该函数内数据上报功能,包括只上报和可上报可下发型数据
    */
    void all_data_update(void)
    {
        //#error "请在此处理可下发可上报数据及只上报数据示例,处理完成后删除该行"
    
        /*
        //此代码为平台自动生成,请按照实际数据修改每个可下发可上报函数和只上报函数
        mcu_dp_bool_update(DPID_SWITCH,当前开关); //BOOL 型数据上报;
        mcu_dp_value_update(DPID_TEMP_SET,当前温度设置); //VALUE 型数据上报;
        mcu_dp_value_update(DPID_TEMP_CURRENT,当前温度); //VALUE 型数据上报;
        mcu_dp_enum_update(DPID_MODE,当前工作模式); //枚举型数据上报;
        mcu_dp_enum_update(DPID_FAN_SPEED_ENUM,当前风速); //枚举型数据上报;
        mcu_dp_enum_update(DPID_STATUS,当前状态); //枚举型数据上报;
        mcu_dp_bool_update(DPID_ECO,当前 ECO 模式); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_DRYING,当前干燥模式); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_VENTILATION,当前换气模式); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_HEAT,当前辅热); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_LIGHT,当前灯光); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_CHILD_LOCK,当前童锁); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_BEEP,当前蜂鸣); //BOOL 型数据上报;
        mcu_dp_value_update(DPID_HUMIDITY_SET,当前湿度设置); //VALUE 型数据上报;
        mcu_dp_value_update(DPID_HUMIDITY_CURRENT,当前湿度); //VALUE 型数据上报;
        mcu_dp_enum_update(DPID_TEMP_UNIT_CONVERT,当前温标切换); //枚举型数据上报;
        mcu_dp_enum_update(DPID_COUNTDOWN_SET,当前倒计时); //枚举型数据上报;
        mcu_dp_value_update(DPID_COUNTDOWN_LEFT,当前倒计时剩余时间); //VALUE 型数据上报;
        mcu_dp_fault_update(DPID_FAULT,当前故障告警); //故障型数据上报;
        mcu_dp_value_update(DPID_TEMP_CURRENT_F,当前温度-℉); //VALUE 型数据上报;
        mcu_dp_value_update(DPID_TEMP_SET_F,当前温度设置-℉); //VALUE 型数据上报;
        mcu_dp_bool_update(DPID_SLEEP,当前睡眠功能); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_CLEANING,当前自清洁); //BOOL 型数据上报;
        mcu_dp_bool_update(DPID_SWITCH_VERTICAL,当前上下摆风); //BOOL 型数据上报;
        mcu_dp_enum_update(DPID_GEAR_VERTICAL,当前上下摆风档位); //枚举型数据上报;
        mcu_dp_value_update(DPID_ANGLE_VERTICAL,当前上下摆风角度); //VALUE 型数据上报;
        mcu_dp_bool_update(DPID_SWITCH_HORIZONTAL,当前左右摆风); //BOOL 型数据上报;
        mcu_dp_enum_update(DPID_GEAR_HORIZONTAL,当前左右摆风档位); //枚举型数据上报;
        mcu_dp_value_update(DPID_ANGLE_HORIZONTAL,当前左右摆风角度); //VALUE 型数据上报;
        mcu_dp_bool_update(DPID_DISPLAY,当前屏显开关); //BOOL 型数据上报;
        */
    }
    

情景二:单个 DP 数据上报

在单个 DP 状态发生变化时,MCU 需要主动上报,App 更新显示内容。上报格式为 mcu_dp_xxxx_updata(DPID_X,n),其中 DPID_X 为状态改变的 DP。all_data_update() 内的函数,均可单独调用。例如:

mcu_dp_bool_update(DPID_SWITCH,1); //BOOL 型数据上报
mcu_dp_value_update(DPID_TEMPER_SET,25); //VALUE 型数据上报
mcu_dp_string_update(DPID_DAY,"1234",4); //STRING 型数据上报

情景三:DP 数据下发处理函数

protocol.c 文件中,每个可下发的 DP 都有一个单独下发数据处理函数。格式为 dp_download_xxx_handle(),其中 xxx 为可下发 DP。函数解析设备功能之后,MCU 需在相应位置完成逻辑控制。以接收到开关 DP 数据为例:

/*****************************************************************************
函数名称:dp_download_switch_handle
功能描述:针对 DPID_SWITCH 的处理函数
输入参数:value:数据源数据
        length:数据长度
返回参数:成功返回:SUCCESS/失败返回:ERROR
使用说明:可下发可上报类型,需要在处理完数据后上报处理结果至 App
*****************************************************************************/
static unsigned char dp_download_switch_handle(const unsigned char value[], unsigned short length)
{
    //示例:当前 DP 类型为 BOOL
    unsigned char ret;
    //0:关状态/1:开状态
    unsigned char switch1;

    switch1 = mcu_get_dp_download_bool(value,length);
    if(switch1 == 0)
    {
        //开关的关状态
        MCU_OFF_switch1();
    }
    else
    {
        //开关的开状态
        MCU_ON_switch1();
    }

    //处理完 DP 数据后的反馈
    ret = mcu_dp_bool_update(DPID_SWITCH,switch1);
    if(ret == SUCCESS)
        return SUCCESS;
    else
        return ERROR;
}

当设备状态在非 App 控制下发生变化时,MCU 中需要调用 mcu_dp_bool_update(DPID_SWITCH_1,switch_1); 上传设备功能(开关)状态实时状态,形成反馈,也可以根据实际需要选择上报时机。

第五步:添加配网功能及指示灯函数

MCU 与模组配合处理模式 工作模式需要设置配网功能及指示灯函数。

移植协议成功后,还需添加配网指令以及指示灯功能。根据 配网方式 编写配网触发方式,根据 模组工作方式 编写网络状态指示方式。可以根据实际情况自行决定,通常为按键触发和 LED 快闪/慢闪指示。

目前支持两种以下配网模式:

  • Wi-Fi 快连模式(Smart 模式,快闪):操作简便,以指示灯快闪做指示。

  • Wi-Fi 热点模式(AP 模式,慢闪):配网可靠,以指示灯慢闪做指示。

    • 建议产品同时开发两种配网模式,以便适应不同的场景。
    • Wi-Fi & 蓝牙无线模组,以上 2 种模式默认还同时支持蓝牙配网模式。

配网指令

配网指令有 mcu_reset_wifi()mcu_set_wifi_mode() 两个函数可以实现。通常在按键触发配网后,在按键处理函数中调用。

  • mcu_reset_wifi():调用后复位 Wi-Fi 模组,复位后之前的配网信息全部清除。mcu_reset_wifi() 每调用一次,Wi-Fi 模组在热点和快连之间切换一次配网模式。

    MCU SDK 移植
  • mcu_set_wifi_mode():参数为 SMART_CONFIGAP_CONFIG。调用后清除配网信息,进入快连模式或者热点模式。

配网指示

通常在 while(1) 中调用 mcu_get_wifi_work_state() 函数,来查询 Wi-Fi 状态。根据 Wi-Fi 状态,写入相应闪灯的模式。

设备联网状态 描述 状态值 LED 显示
状态 1 快连模式配置状态 0x00 快闪,间隔 250ms
状态 2 热点模式配置状态 0x01 慢闪,间隔 1500ms
状态 3 Wi-Fi 已配置但未连上路由器 0x02 熄灭
状态 4 Wi-Fi 已配置且连上路由器,局域网可控制 0x03 常亮
状态 5 已连上路由器且连接到云端,局域网和外网均可控制 0x04 常亮
状态 6 Wi-Fi 设备处于低功耗模式 0x05 熄灭
状态 7 Wi-Fi 设备处于快连和热点模式共存配置状态 0x06 快闪,间隔 250ms

调用函数 mcu_get_wifi_work_state() 查询 Wi-Fi 连接状态,函数架构如下:

void main(void)
{
    ...
    while(1)
    {
        ...
        switch(mcu_get_wifi_work_state())
        {
            case SMART_CONFIG_STATE:
                //处于 Smart 配置状态,即 LED 快闪
            break;
            case AP_STATE:
                //处于 AP 配置状态,即 LED 慢闪
            break;
            case WIFI_NOT_CONNECTED:
                //Wi-Fi 配置完成,正在连接路由器,即 LED 常暗
            break;
            case WIFI_CONNECTED:
                //路由器连接成功,即 LED 常亮
            break;
            default:break;
        }
        ...
    }
}

第六步:(可选)添加产测功能

Wi-Fi 产测功能默认开启。为保证最终量产效率及品质,建议开启该功能。

#define         WIFI_TEST_ENABLE                //开启 Wi-Fi 产测功能(扫描指定路由)

详细产测流程,请参考 设备产测生产测试说明

第七步:(可选)添加 MCU OTA 功能

MCU SDK 提供 MCU 固件 OTA 功能,MCU 待升级固件可通过 Wi-Fi 通信模组下载,并通过串口发送至 MCU。MCU 本地存储后切换新版本固件,即可完成 MCU OTA 功能。如需支持 MCU OTA 功能,请开启 SUPPORT_MCU_FIRM_UPDATE,默认关闭。

#define         SUPPORT_MCU_FIRM_UPDATE

更多 MCU OTA 功能介绍与使用,请参考 MCU OTA 服务

第八步:(可选)添加扩展功能

Wi-Fi 和 Wi-Fi & 蓝牙通用协议接入支持多种扩展能力,可满足不同行业应用需求。关于扩展功能介绍与使用,请参考 扩展功能