更新时间:2024-07-29 06:50:27下载pdf
MCU SDK 是根据涂鸦 IoT 开发平台定义的产品功能,自动生成的 MCU 代码,能够协助您快速完成 MCU 程序的开发。本文为您介绍移植 NB-IoT 的 MCU SDK 的流程以及注意事项。
MCU SDK 是根据涂鸦 IoT 开发平台定义的产品功能,自动生成的 MCU 代码。为了减少您使用涂鸦 NB-IoT 通用串口协议的对接成本,MCU SDK 已搭建通讯及协议解析架构。将 MCU SDK 添加至工程并配置相关信息后,就可以快速完成 MCU 程序开发。
涂鸦 SDK 包对 MCU 的要求如下。资源不足的用户,可自行对接串口协议,SDK 包中的函数可以作为使用参考。
文件 | 说明 |
---|---|
mcu_api.c |
包含可供调用的函数。 |
mcu_api.h |
mcu_api.c 中的函数声明。 |
protocol.c |
包含协议数据体的内容处理函数。您可以根据项目需求,在相应函数内添加代码,获取 NB-IoT 模组向 MCU 发送的数据。 |
protocol.h |
protocol.h 包含以下信息:MCU 要发送至 NB-IoT 模组初始化所需的参数,SDK 裁剪所定义的宏,用户可根据需要的功能打开相应的宏定义,protocol.c 中的函数声明。 |
system.c |
串口通讯协议解析的具体实现。 |
system.h |
system.h 包含以下信息:协议命令字的定义,部分全局变量的定义,system.c 中的函数声明。 |
nbiot.h |
包含 NB-IoT 相关宏定义。 |
nbiot.h
中的说明,配置产品工作模式与连云方式。protocol.h
宏定义。protocol.c
文件及函数调用。在原项目工程中,完成 MCU 相关外设初始化,包括串口和 GPIO(配置 NB-IoT 唤醒源)等。
将 MCU SDK 文件夹中的 .c
和 .h
文件添加至相应头文件引用路径。
SIM_MODE
为产品工作模式,可选项为 psm
、drx
和 edrx
。该项务必与 涂鸦 IoT 开发平台 上创建产品时的工作模式一致。CONNECT_MODE
为产品云连接方式,可选项为 isp
和 tuya
,该项务必与涂鸦 IoT 开发平台上创建产品时的连云方式一致。
isp
为先连接运营商云进而连接云端。tuya
为直连云端。目前,仅中国移动卡支持该功能。SUPPORT_MCU_FIRM_UPDATE
为 MCU 升级或文件下载功能开关,若需要该功能可开启此宏。定义 PID:PRODUCT_KEY
为产品 PID 宏定义。PID 即产品 ID,为每个产品的唯一标识,可在涂鸦 IoT 开发平台的 产品管理页面 获取。
如果 PRODUCT_KEY
和产品 PID 不一致,请在 硬件开发 > 下载资料 下载最新 SDK 开发包后重试。
#define PRODUCT_KEY "ny18le8hpsb3z97g"
定义版本号:MCU_VER
为软件版本,默认为 1.0.0
。若 MCU 需要 OTA 功能,需要添加新的 MCU 版本号。
#define MCU_VER "1.0.0"
配置校时功能:SUPPORT_MCU_RTC_CHECK
为校时功能开关,若产品对象需要严格的时间准确性,可以为 NB-IoT 模组校时,以便保证 NB-IoT 模组 RTC 的准确性。如需要,可以开启该宏,并且在 protocol.c
文件 mcu_write_rtctime
内实现代码。
串口接收缓存:大小受到串口数据处理被调用的频率影响。如果 MCU 对串口数据的处理较快,串口接收缓存大小可适当减小。
串口发送缓存:大小要大于数据最长的 DP 数据长度。
串口数据处理缓存:大小需要大于数据最长的 DP 数据长度,还要根据是否需要 OTA 功能来调整大小。
/******************************************************************************
3:定义收发缓存:
如当前使用 MCU 的 RAM 不够,可修改为 24
******************************************************************************/
#ifndef SUPPORT_MCU_FIRM_UPDATE
#define NBIOT_UART_QUEUE_LMT 16 //数据接收队列大小,如 MCU 的 RAM 不够,可缩小
#define NBIOT_UART_RECV_BUF_LMT 24 //根据用户 DP 数据量大小确定,必须大于 24
#else
#define NBIOT_UART_QUEUE_LMT 128 //数据接收队列大小,如 MCU 的 RAM 不够,可缩小
#define NBIOT_UART_RECV_BUF_LMT 300 //根据用户 DP 数据量大小确定,必须大于 24
#endif
#define NBIOT_UART_SEND_BUF_LMT 64 //根据用户 DP 数据量大小确定,必须大于 24
/******************************************************************************
将 nbiot.h
文件保存至 NB-IoT 相关文件的文件夹中,例如 main.c
文件夹。
在 MCU 串口及其他外设初始化后,调用 mcu_api.c
文件中的 nbiot_protocol_init()
函数。
将 MCU 串口单字节发送函数填入 protocol.c
文件中 uart_transmit_output
函数内,并删除 #error
。示例如下:
void uart_transmit_output(unsigned char value)
{
extern void Uart_PutChar(unsigned char value);
Uart_PutChar(value); //串口发送函数
}
在串口接收中断服务函数里,调用 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);
}
}
MCU 进入 while(1)
循环后,调用 mcu_api.c
文件内的 cellular_uart_service()
函数。main.c
中示例代码结构如下:
#include "nbiot.h"
...
void main(void)
{
nbiot_protocol_init();
...
while(1)
{
nbiot_uart_service();
...
}
...
}
MCU 必须在 while(1)
中直接调用 mcu_api.c
内的 nbiot_uart_service()
函数。程序正常初始化完成后,建议不进行关中断,如必须关中断,关中断时间必须短。关中断会引起串口数据包丢失,请勿在中断内调用上报函数。
NB-IoT 固件在空闲状态(没有数据交互)的情况下,会主动进入休眠以节省功耗。
如果 MCU 需要处理较多数据或者长时间不需要模组进入休眠,则可以发送睡眠锁命令,开启睡眠锁定。待数据处理完成,需要 NB-IoT 睡眠时再发送命令解锁进入睡眠。
如果需要 NB-IoT 模组在整个产品工作期间不进入休眠,需在收到 NB-IoT 模组查询产品信息后,立即发送睡眠锁命令。
NB-IoT 具备连接多、成本低、功耗低的特点,但是其窄带宽的特性也已经限制了可以并发的数量。
针对安装密集、数量较多的产品,请酌情使用离散功能,使设备启动与数据交互离散开来,优化数据拥堵问题。
MCU 端也要针对离散后的效果进行实际验证,以便达到最稳定可靠的效果。
离散功能仅针对上电启动有效,数据上报的离散则需要 MCU 单独维护。如果产品需要频繁给 NB-IoT 断电,请不要开启离散功能。
/*****************************************************************************
函数名称:mcu_set_discrete_info
功能描述:设置 NB-IoT 模组首次上电注网离散信息
输入参数:en:0 关闭离散功能,1 使能离散功能
duration:随机离散时间最大值,范围为 120-1800,例如当设置值为 150 时,表示随机范围为 0-150
step:离散范围步进,在相同 duration 情况下,step 越小,离散的越散
返回参数:无
使用说明:
*****************************************************************************/
void mcu_set_discrete_info(unsigned char en, unsigned int duration, DISCRETE_STEP step)
{
unsigned int du = 0;
unsigned int length = 0;
unsigned char uart_buff[128] = {0};
du = duration;
if (duration < 120){
du = 120;
}
if (duration > 1800){
du = 1800;
}
length = snprintf(uart_buff, sizeof(uart_buff), "{\"enable\":%d,\"duration\":%d,\"step\":%d}",\
(unsigned int)en, du, (unsigned int)step);
length = set_nbiot_uart_buffer(0, uart_buff, length);
nbiot_uart_write_frame(SET_DISCRETE_INFO_CMD, length);
}
详情请参考 Wi-Fi 通用对接中的 MCU SDK 移植中 DP 数据上报与下发 章节。
MCU 与 NB-IoT 模组在进行 OTA 数据交互时,数据包交互的数据量比较多,会出现一些异常情况。本小节介绍 NB-IoT 模组的处理逻辑。
文件下载的逻辑和 OTA 实质上一样。可以将本章节中的 OTA 替换成 文件下载,作为文件下载逻辑的介绍。
OTA 过程 | 说明 |
---|---|
OTA 启动 | NB-IoT 模组在检测到有 MCU OTA 时,会发送启动升级包。
|
OTA 数据传输 | NB-IoT 模组在发送一包 OTA 数据之后,如果 5 秒内没有收到 MCU 的回复,会进行重发。若重发 3 次之后 MCU 依旧没有回复,就认为 OTA 失败。
|
OTA 传输完成 | 支持以下两种方式判断 OTA 传输是否完成。
|
在 MCU OTA 启动的时候以及固件更新的时候,需要重点处理重复下载的情况。
在 MCU OTA 启动的时候,为了避免重复下载,需进行固件 CRC(Cyclic Redundancy Check)校验比对。
case MCU_OTA_START_CMD:
...
//回复模组
#error "请完成传输包大小/断点地址的回复,完成后请删除该行"
/*
1.计算当前正在运行 app 固件的 crc 值 crc32_app
2.与升级开始时给出的固件 firm_crc32 比较
if (firm_crc32 == crc32_app) {
//固件已存在,无需再次下载,直接回复文件长度即可
mcu_firm_update_start_ack(unsigned char unit_len, firm_length);
}
else {
//可根据实际情况回复断点偏移地址(从头下载为 0,断点下载则传入偏移地址)
mcu_firm_update_start_ack(unsigned char unit_len, unsigned long received_len);
}
*/
...
break;
在 MCU 固件下载完成进入固件更新的时候,如果存在重复进入的情况则需要过滤处理,可通过对比固件 CRC 来确定。
/*****************************************************************************
函数名称:mcu_firm_update_handle
功能描述:MCU 进入固件升级模式
输入参数:value:固件缓冲区
position:当前数据包在于固件位置
length:当前固件包长度(固件包长度为 0 时,表示固件包发送完成)
返回参数:无
使用说明:MCU 需要自行实现该功能
*****************************************************************************/
unsigned char mcu_firm_update_handle(const unsigned char value[],unsigned long position,unsigned short length)
{
#error "请自行完成 MCU 固件升级代码,完成后请删除该行"
if(length == 0)
{
//固件数据发送完成
#error "请按照以下步骤完成 crc32 校验,结果返回 0 或者 1,完成后请删除该行"
/*
1.计算已经下载完成固件的 crc 值 crc32_dl
2.计算当前正在运行 app 固件的 crc 值 crc32_app
3.与升级开始时给出的固件 crc32 比较
if (crc32 == crc32_dl) { //当前固件下载成功,即将跳转运行最新固件
mcu_firm_update_data_ack(1, 0); //crc 校准成功
//按照应用需求执行程序跳转
}
else if (crc32 == crc32_app){ //当前运行的固件为最新固件,仅需回复响应
mcu_firm_update_data_ack(1, 0); //crc 校准成功
}
else {
mcu_firm_update_data_ack(1, 1); //crc 校准失败
}
*/
}
else
{
//mcu_firm_update_data_ack(0, 0);
//固件数据处理
}
return SUCCESS;
}
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈