MN316 NB-IoT驱动代码 — STM32 UART+DMA+IDLE · MQTT物模型上报

知识库
知识库文档
/firmware/通信模块/MN316 NBIOT模块/MN316 NB-IoT驱动代码 — STM32 UART+DMA+IDLE · MQTT物模型上报.md

文档

MN316 NB-IoT驱动代码 (STM32 HAL库)

MCU: STM32F103 | UART2 DMA+IDLE中断 | MN316 AT指令 | OneNET MQTT物模型


一、GPIO与UART初始化

/* ======================== nbiot_config.h ======================== */
#ifndef NBIOT_CONFIG_H
#define NBIOT_CONFIG_H

#include "stm32f1xx_hal.h"
#include <stdint.h>

/* ---- 硬件引脚定义 (根据实际电路修改) ---- */
#define NB_PWK_PORT          GPIOB
#define NB_PWK_PIN           GPIO_PIN_8      /* PWRKEY */
#define NB_RST_PORT          GPIOB
#define NB_RST_PIN           GPIO_PIN_9      /* RESET   */
#define NB_NETLED_PORT       GPIOC
#define NB_NETLED_PIN        GPIO_PIN_6      /* NETLIGHT */

/* MN316 UART: USART2, 9600bps, 8N1, TTL 3.3V */
extern UART_HandleTypeDef huart2;

/* ---- 缓冲区 ---- */
#define NBIOT_RX_BUF_SIZE    1024
#define NBIOT_TX_BUF_SIZE    1024

/* ---- 时序参数 ---- */
#define MN316_PWRKEY_PULSE   1200    /* PWRKEY拉低时间 ms (≥1s) */
#define NBIOT_UPLOAD_INTERVAL_MS  60000  /* 上报间隔 ms */
#define NBIOT_MAX_RETRY      3

/* ---- OneNET Topic宏 ---- */
#define ONENET_TOPIC_PROP_POST  "$sys/%s/%s/thing/property/post"
#define ONENET_TOPIC_EVENT_POST "$sys/%s/%s/thing/event/post"
#define ONENET_TOPIC_CMD_RECV   "$sys/%s/%s/thing/service/+/invoke"

/* ---- 枚举 ---- */
typedef enum {
    NBIOT_STATE_OFF = 0,
    NBIOT_STATE_INIT,
    NBIOT_STATE_ATTACHED,
    NBIOT_STATE_CONNECTED,
    NBIOT_STATE_ERROR,
} NBIoT_State_t;

typedef enum {
    NBIOT_PROTO_MQTT = 0,
    NBIOT_PROTO_TCP  = 1,
} NBIoT_Protocol_t;

/* ---- 配置结构体 ---- */
typedef struct {
    char server_ip[32];
    uint16_t server_port;
    char product_id[32];
    char device_name[32];
    char token[64];
    char nb_band[8];       /* "5,8" 表示 B5+B8 */
    NBIoT_Protocol_t protocol;
} NBIoT_Config_t;

/* ---- 传感器数据结构体 ---- */
typedef struct {
    uint32_t timestamp;
    float    ph;
    float    turbidity;
    float    conductivity;
    float    tds;
    float    temperature;
    float    humidity;
} SensorData_t;

#endif /* NBIOT_CONFIG_H */
/* ======================== nbiot_hardware_init.c ======================== */
/**
 * @brief  MN316硬件初始化: GPIO + USART2 DMA
 *         调用时机: 系统启动时, 早于NBIoT_Init()
 */
#include "nbiot_config.h"
#include "stm32f1xx_hal.h"

extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;

void NBIoT_Hardware_Init(void)
{
    GPIO_InitTypeDef gpio = {0};

    /* ---- 1. 使能GPIO时钟 ---- */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    /* ---- 2. PWRKEY: 推挽输出, 默认高 ---- */
    gpio.Pin   = NB_PWK_PIN;
    gpio.Mode  = GPIO_MODE_OUTPUT_PP;
    gpio.Pull  = GPIO_PULLUP;
    gpio.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(NB_PWK_PORT, &gpio);
    HAL_GPIO_WritePin(NB_PWK_PORT, NB_PWK_PIN, GPIO_PIN_SET);

    /* ---- 3. RESET: 推挽输出, 默认高 ---- */
    gpio.Pin   = NB_RST_PIN;
    HAL_GPIO_Init(NB_RST_PORT, &gpio);
    HAL_GPIO_WritePin(NB_RST_PORT, NB_RST_PIN, GPIO_PIN_SET);

    /* ---- 4. NETLIGHT: 输入浮空 (可选, 仅用于监控) ---- */
    gpio.Pin  = NB_NETLED_PIN;
    gpio.Mode = GPIO_MODE_INPUT;
    gpio.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(NB_NETLED_PORT, &gpio);

    /* ---- 5. USART2 DMA接收初始化 (不在这里启动, SendCmd中按需启动) ---- */
    /* 前提: MX_USART2_UART_Init()已完成, huart2.Init.BaudRate=9600 */
    __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}

/**
 * @brief  在stm32f1xx_it.c的USART2_IRQHandler中调用此函数
 *         实现UART IDLE中断 + DMA接收
 *
 *  stm32f1xx_it.c 示例:
 *
 *  void USART2_IRQHandler(void)
 *  {
 *      if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
 *          NBIoT_UART_IdleCallback();  // 调用下面这个函数
 *          __HAL_UART_CLEAR_IDLEFLAG(&huart2);
 *      }
 *      HAL_UART_IRQHandler(&huart2);
 *  }
 */

static volatile uint8_t  rx_buf[1024];
static volatile uint16_t rx_len = 0;
static volatile uint8_t  rx_ready = 0;

void NBIoT_UART_IdleCallback(void)
{
    /* 停止DMA, 获取已接收字节数 */
    HAL_UART_DMAStop(&huart2);
    rx_len = NBIOT_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart2.hdmarx);
    rx_ready = 1;
}

/* 获取接收缓冲指针 (供nbiot.c使用) */
uint8_t* NBIoT_GetRxBuf(void)     { return (uint8_t*)rx_buf; }
uint16_t NBIoT_GetRxLen(void)     { return rx_len; }
uint8_t  NBIoT_IsRxReady(void)    { return rx_ready; }
void     NBIoT_ClearRxReady(void) { rx_ready = 0; }

二、AT指令收发驱动(核心)

/* ======================== nbiot.c (核心驱动) ======================== */
#include "nbiot_config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* ---- 内部状态 ---- */
static NBIoT_State_t  s_state = NBIOT_STATE_OFF;
static NBIoT_Config_t s_cfg   = {0};
static volatile uint8_t s_mqtt_connected = 0;
static volatile uint8_t s_mqtt_timeout   = 0;

/* 外部引用 */
extern UART_HandleTypeDef huart2;
extern uint8_t* NBIoT_GetRxBuf(void);
extern uint16_t NBIoT_GetRxLen(void);
extern uint8_t  NBIoT_IsRxReady(void);
extern void     NBIoT_ClearRxReady(void);

/* ---- 启动UART DMA+IDLE接收 ---- */
static void UART_StartRx(void)
{
    uint8_t *buf = NBIoT_GetRxBuf();

    HAL_UART_DMAStop(&huart2);
    NBIoT_ClearRxReady();
    memset(buf, 0, NBIOT_RX_BUF_SIZE);

    __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
    HAL_UART_Receive_DMA(&huart2, buf, NBIOT_RX_BUF_SIZE);
}

/* ---- 响应内容检查 ---- */
static uint8_t RxContains(const char *needle)
{
    if (needle == NULL || needle[0] == '\0') return 0;
    return (strstr((char*)NBIoT_GetRxBuf(), needle) != NULL) ? 1 : 0;
}

/**
 * @brief  发送AT指令并等待期望响应
 * @param  cmd        AT指令字符串 (不含\r\n)
 * @param  expected   期望响应的子串, NULL表示只等OK
 * @param  timeout_ms 超时时间(ms)
 * @return HAL_OK/HAL_TIMEOUT/HAL_ERROR
 */
HAL_StatusTypeDef NBIoT_SendCmd(const char *cmd,
                                 const char *expected,
                                 uint32_t timeout_ms)
{
    uint32_t tick = HAL_GetTick();
    HAL_StatusTypeDef status = HAL_TIMEOUT;
    uint8_t *buf = NBIoT_GetRxBuf();

    /* 启动DMA接收 */
    UART_StartRx();

    /* 发送AT指令 (末尾自动加\r\n) */
    HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 1000);
    HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, 100);

    /* 等待IDLE中断完成或超时 */
    while ((HAL_GetTick() - tick) < timeout_ms) {
        if (NBIoT_IsRxReady()) {
            uint16_t len = NBIoT_GetRxLen();
            buf[len] = '\0';

            /* 检查MQTT超时URC */
            if (strstr((char*)buf, "+MQTTTO") != NULL) {
                s_mqtt_timeout = 1;
                s_mqtt_connected = 0;
            }

            /* 匹配期望 */
            if (expected != NULL && strstr((char*)buf, expected) != NULL) {
                status = HAL_OK;
                goto exit;
            }
            if (strstr((char*)buf, "OK\r\n") != NULL) {
                status = (expected == NULL) ? HAL_OK : HAL_ERROR;
                goto exit;
            }
            if (strstr((char*)buf, "ERROR") != NULL) {
                status = HAL_ERROR;
                goto exit;
            }

            /* 未匹配, 继续拼接接收 */
            NBIoT_ClearRxReady();
            if (len < NBIOT_RX_BUF_SIZE - 1) {
                HAL_UART_Receive_DMA(&huart2, buf + len,
                                     NBIOT_RX_BUF_SIZE - len);
            }
        }
        __WFI();  /* 让出CPU, 降低功耗 */
    }

exit:
    HAL_UART_DMAStop(&huart2);
    return status;
}

/* ---- 查询网络附着 ---- */
uint8_t NBIoT_CheckAttach(void)
{
    if (NBIoT_SendCmd("AT+CGATT?", "+CGATT:1", 3000) == HAL_OK)
        return 1;
    return 0;
}

/* ---- 查询信号强度 (返回dBm) ---- */
int16_t NBIoT_GetSignal(void)
{
    if (NBIoT_SendCmd("AT+CSQ", "OK", 3000) != HAL_OK)
        return 0;

    char *p = strstr((char*)NBIoT_GetRxBuf(), "+CSQ:");
    if (p != NULL) {
        int rssi = atoi(p + 5);
        if (rssi != 99) return -113 + rssi * 2;
    }
    return 0;
}

三、MN316 开机与网络注册

/* ======================== 开机与注册流程 ======================== */

/**
 * @brief  MN316开机: PWRKEY拉低 ≥1秒
 */
static HAL_StatusTypeDef MN316_PowerOn(void)
{
    /* PWRKEY拉低 */
    HAL_GPIO_WritePin(NB_PWK_PORT, NB_PWK_PIN, GPIO_PIN_RESET);
    HAL_Delay(MN316_PWRKEY_PULSE);      /* 默认1200ms */
    HAL_GPIO_WritePin(NB_PWK_PORT, NB_PWK_PIN, GPIO_PIN_SET);
    HAL_Delay(2000);                     /* 等待模块启动 */

    /* AT探活 */
    if (NBIoT_SendCmd("AT", "OK", 5000) != HAL_OK) {
        return HAL_ERROR;
    }
    return HAL_OK;
}

/**
 * @brief  MN316网络注册完整流程
 *         频段设置 → 射频开启 → 网络附着 → PDN激活
 */
static HAL_StatusTypeDef MN316_NetworkRegister(void)
{
    char cmd[64];
    uint8_t retry = 0;

    /* 1. 关闭射频 (CFUN=0才能配置频段) */
    NBIoT_SendCmd("AT+CFUN=0", "OK", 5000);
    HAL_Delay(500);

    /* 2. 自动连接 */
    NBIoT_SendCmd("AT+NCONFIG=AUTOCONNECT,1", "OK", 3000);
    HAL_Delay(100);

    /* 3. 设置频段 (B5电信 + B8移动) */
    snprintf(cmd, sizeof(cmd), "AT+NBAND=%s", s_cfg.nb_band);
    NBIoT_SendCmd(cmd, "OK", 3000);
    HAL_Delay(100);

    /* 4. 开启射频 */
    if (NBIoT_SendCmd("AT+CFUN=1", "OK", 10000) != HAL_OK) {
        /* 失败则软复位重试 */
        NBIoT_SendCmd("AT+NRB", "REBOOTING", 5000);
        HAL_Delay(5000);
        NBIoT_SendCmd("AT", "OK", 5000);
        NBIoT_SendCmd("AT+CFUN=1", "OK", 10000);
    }
    HAL_Delay(1000);

    /* 5. 附着网络 */
    NBIoT_SendCmd("AT+CGATT=1", "OK", 10000);
    HAL_Delay(500);

    /* 6. 检查附着状态 (带重试) */
    while (retry < NBIOT_MAX_RETRY) {
        if (NBIoT_CheckAttach()) {
            s_state = NBIOT_STATE_ATTACHED;
            return HAL_OK;
        }
        retry++;
        HAL_Delay(3000);
    }

    s_state = NBIOT_STATE_ERROR;
    return HAL_ERROR;
}

四、MQTT连接OneNET

/* ======================== MQTT连接OneNET平台 ======================== */

/**
 * @brief  检查MQTT是否已配置正确的参数
 */
static uint8_t IsMQTTConfigured(void)
{
    if (NBIoT_SendCmd("AT+MQTTCFG?", "OK", 5000) != HAL_OK)
        return 0;
    if (RxContains(s_cfg.server_ip) && RxContains(s_cfg.device_name))
        return 1;
    return 0;
}

/**
 * @brief  检查MQTT是否已连接 (状态5=已连接)
 */
static uint8_t IsMQTTConnected(void)
{
    if (NBIoT_SendCmd("AT+MQTTSTAT?", "OK", 5000) != HAL_OK)
        return 0;
    return RxContains("+MQTTSTAT:5");
}

/**
 * @brief  MN316 MQTT连接OneNET完整流程
 *         MQTTCFG → MQTTOPEN → 订阅reply/command/event
 */
static HAL_StatusTypeDef MN316_OneNET_MQTT_Connect(void)
{
    char cmd[512];
    uint8_t poll = 0;

    /* 先释放旧连接 */
    NBIoT_SendCmd("AT+MQTTDISC", NULL, 3000);
    NBIoT_SendCmd("AT+MQTTDEL", NULL, 2000);
    HAL_Delay(500);

    /* 1. 配置MQTT参数 */
    snprintf(cmd, sizeof(cmd),
        "AT+MQTTCFG=\"%s\",%d,\"%s\",60,\"%s\",\"%s\",1,0",
        s_cfg.server_ip, s_cfg.server_port,
        s_cfg.device_name, s_cfg.product_id, s_cfg.token);

    if (NBIoT_SendCmd(cmd, "OK", 12000) != HAL_OK) {
        /* MQTTCFG偶发不回OK,补查配置结果 */
        HAL_Delay(1500);
        if (!IsMQTTConfigured()) {
            return HAL_ERROR;
        }
    }
    HAL_Delay(500);

    /* 2. 打开MQTT连接 */
    NBIoT_SendCmd("AT+MQTTOPEN=1,1,0,0,0,\"\",\"\"", "OK", 15000);
    HAL_Delay(3000);

    /* 3. 轮询等待连接建立 */
    while (poll < 5) {
        if (IsMQTTConnected()) break;
        poll++;
        HAL_Delay(2000);
    }

    if (!IsMQTTConnected()) {
        s_mqtt_connected = 0;
        return HAL_ERROR;
    }
    s_mqtt_connected = 1;
    s_mqtt_timeout = 0;
    HAL_Delay(500);

    /* 4. 订阅属性回复Topic */
    snprintf(cmd, sizeof(cmd),
        "AT+MQTTSUB=\"$sys/%s/%s/thing/property/post/reply\",0",
        s_cfg.product_id, s_cfg.device_name);
    NBIoT_SendCmd(cmd, "OK", 5000);

    /* 5. 订阅命令下发Topic */
    snprintf(cmd, sizeof(cmd),
        "AT+MQTTSUB=\"$sys/%s/%s/thing/service/+/invoke\",0",
        s_cfg.product_id, s_cfg.device_name);
    NBIoT_SendCmd(cmd, "OK", 5000);

    /* 6. 订阅事件回复Topic */
    snprintf(cmd, sizeof(cmd),
        "AT+MQTTSUB=\"$sys/%s/%s/thing/event/post/reply\",0",
        s_cfg.product_id, s_cfg.device_name);
    NBIoT_SendCmd(cmd, "OK", 5000);

    return HAL_OK;
}

五、完整初始化流程

/* ======================== 模块总初始化 ======================== */

/**
 * @brief  MN316模块完整初始化
 *         开机 → 查询信息 → 关闭PSM/eDRX → 网络注册 → MQTT连接
 */
HAL_StatusTypeDef NBIoT_Init(const NBIoT_Config_t *config)
{
    memcpy(&s_cfg, config, sizeof(NBIoT_Config_t));
    s_state = NBIOT_STATE_INIT;

    /* 1. 硬件初始化 (GPIO已在别处完成, 此处仅确保UART就绪) */
    /* 2. 开机 */
    if (MN316_PowerOn() != HAL_OK) {
        s_state = NBIOT_STATE_ERROR;
        return HAL_ERROR;
    }
    HAL_Delay(500);

    /* 3. 查询模块信息 */
    NBIoT_SendCmd("AT+CGMM", "OK", 2000);   /* 型号 */
    HAL_Delay(100);
    NBIoT_SendCmd("AT+CGSN=1", "OK", 2000); /* IMEI */
    HAL_Delay(100);
    NBIoT_SendCmd("AT+CIMI", "OK", 2000);   /* IMSI */
    HAL_Delay(100);

    /* 4. 关闭低功耗 (调试阶段保持常在线) */
    NBIoT_SendCmd("AT+CPSMS=0", "OK", 2000);
    HAL_Delay(100);
    NBIoT_SendCmd("AT+CEDRXS=0", "OK", 2000);
    HAL_Delay(100);

    /* 5. 网络注册 */
    if (MN316_NetworkRegister() != HAL_OK) {
        return HAL_ERROR;
    }

    /* 6. MQTT连接 */
    if (s_cfg.protocol == NBIOT_PROTO_MQTT) {
        if (MN316_OneNET_MQTT_Connect() == HAL_OK) {
            s_state = NBIOT_STATE_CONNECTED;
        } else {
            s_state = NBIOT_STATE_ERROR;
            return HAL_ERROR;
        }
    }

    return HAL_OK;
}

六、物模型数据上报(完整可运行示例)

/* ======================== OneNET物模型属性上报 ======================== */

/**
 * @brief  OneNET物模型属性上报
 *         Topic: $sys/{pid}/{device}/thing/property/post
 *         Payload: JSON → HEX (MN316 MQTTPUB要求hex编码)
 *
 * @param  data      传感器数据
 * @param  alarm_mask 报警位掩码
 * @return HAL_OK / HAL_ERROR
 */
HAL_StatusTypeDef NBIoT_MQTT_PropertyReport(const SensorData_t *data,
                                             uint8_t alarm_mask)
{
    /* 连接断开则重连 */
    if (!s_mqtt_connected || s_mqtt_timeout) {
        if (MN316_OneNET_MQTT_Connect() != HAL_OK)
            return HAL_ERROR;
    }

    int16_t signal = NBIoT_GetSignal();

    /* ---- 构造JSON Payload ---- */
    char json[512];
    int json_len = snprintf(json, sizeof(json),
        "{"
          "\"id\":\"%lu\","
          "\"version\":\"1.0\","
          "\"params\":{"
            "\"ph\":{\"value\":%.2f},"
            "\"turbidity\":{\"value\":%.1f},"
            "\"conductivity\":{\"value\":%.1f},"
            "\"tds\":{\"value\":%.1f},"
            "\"temperature\":{\"value\":%.1f},"
            "\"humidity\":{\"value\":%.1f},"
            "\"alarm_mask\":{\"value\":%d},"
            "\"signal_strength\":{\"value\":%d}"
          "}"
        "}",
        data->timestamp,
        data->ph, data->turbidity,
        data->conductivity, data->tds,
        data->temperature, data->humidity,
        alarm_mask, (int)signal);

    if (json_len <= 0 || json_len >= (int)sizeof(json))
        return HAL_ERROR;

    /* ---- JSON → HEX字符串 ---- */
    char hex[1024];
    int hex_len = 0;
    for (int i = 0; i < json_len; i++) {
        hex_len += snprintf(hex + hex_len, sizeof(hex) - hex_len,
                            "%02X", (uint8_t)json[i]);
    }

    /* ---- 构造Topic ---- */
    char topic[100];
    snprintf(topic, sizeof(topic), ONENET_TOPIC_PROP_POST,
             s_cfg.product_id, s_cfg.device_name);

    /* ---- MQTT发布 (hex编码) ---- */
    char pub_cmd[1536];
    snprintf(pub_cmd, sizeof(pub_cmd),
             "AT+MQTTPUB=\"%s\",0,0,0,%d,%s",
             topic, json_len, hex);

    if (NBIoT_SendCmd(pub_cmd, "OK", 10000) != HAL_OK) {
        s_mqtt_connected = 0;
        return HAL_ERROR;
    }

    /* 等待reply检查 */
    HAL_Delay(2000);

    return HAL_OK;
}

七、完整使用示例(main.c)

/* ======================== main.c 完整示例 ======================== */
#include "nbiot_config.h"

/* 全局变量 */
NBIoT_Config_t g_nb_config = {
    .server_ip   = "183.230.40.96",    /* OneNET MQTT接入IP */
    .server_port = 6002,
    .product_id  = "your_product_id",
    .device_name = "your_device_name",
    .token       = "your_access_token",
    .nb_band     = "5,8",              /* B5电信 + B8移动 */
    .protocol    = NBIOT_PROTO_MQTT,
};

SensorData_t g_sensor_data = {0};

int main(void)
{
    /* ---- 系统初始化 ---- */
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART2_UART_Init();   /* 9600bps, 8N1 */

    /* ---- MN316硬件初始化 ---- */
    NBIoT_Hardware_Init();

    /* ---- MN316模块初始化 (开机+注册+MQTT连接) ---- */
    HAL_StatusTypeDef ret = NBIoT_Init(&g_nb_config);
    if (ret != HAL_OK) {
        /* 初始化失败处理: 可重试或进入错误状态 */
        while (1) {
            HAL_Delay(1000);
        }
    }

    /* ---- 主循环: 周期上报 ---- */
    uint32_t last_upload = 0;

    while (1) {
        uint32_t now = HAL_GetTick();

        if (now - last_upload >= NBIOT_UPLOAD_INTERVAL_MS) {
            last_upload = now;

            /* 模拟传感器数据 */
            g_sensor_data.timestamp    = now / 1000;
            g_sensor_data.ph           = 7.2f;
            g_sensor_data.turbidity    = 15.3f;
            g_sensor_data.conductivity = 350.0f;
            g_sensor_data.tds          = 245.0f;
            g_sensor_data.temperature  = 25.1f;
            g_sensor_data.humidity     = 60.5f;

            /* 上报属性 */
            ret = NBIoT_MQTT_PropertyReport(&g_sensor_data, 0);
            if (ret != HAL_OK) {
                /* 上报失败, 下次循环自动重试 */
            }
        }

        /* 其他任务... */
        HAL_Delay(100);
    }
}

八、中断服务函数 (stm32f1xx_it.c)

/* ======================== stm32f1xx_it.c ======================== */
#include "stm32f1xx_hal.h"

extern UART_HandleTypeDef huart2;

/**
 * @brief USART2中断服务函数
 *        处理DMA接收完成 + UART IDLE中断
 */
void USART2_IRQHandler(void)
{
    /* IDLE中断: 一帧AT响应接收完毕 */
    if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
        __HAL_UART_CLEAR_IDLEFLAG(&huart2);
        NBIoT_UART_IdleCallback();   /* 记录已接收长度, 置rx_ready=1 */
    }

    /* HAL库通用处理 (DMA中断等) */
    HAL_UART_IRQHandler(&huart2);
}

九、调试技巧

9.1 串口监控

可在MCU端添加一个调试串口将MN316的收发数据透传出来:

void NBIoT_DebugPassthrough(void)
{
    /* 收到MN316数据时 */
    if (NBIoT_IsRxReady()) {
        uint8_t *buf = NBIoT_GetRxBuf();
        uint16_t len = NBIoT_GetRxLen();
        /* 通过调试串口输出 */
        HAL_UART_Transmit(&hlpuart1, buf, len, 100);
    }
}

9.2 常见问题排查

现象 可能原因 排查方法
AT 无响应 供电不足/PWRKEY时序 检查VBAT波形,确认PWRKEY拉低≥1s
+CGATT:0 无信号/SIM卡异常 AT+CSQ查信号,AT+CPIN?查SIM状态
AT+MQTTOPEN失败 服务器IP/端口错 ping服务器,检查token是否正确
+MQTTTO频繁 信号弱/keepalive过短 调整天线位置,延长keepalive到120s
MQTTPUB返回ERROR hex长度参数错误 确认第5参数=原始JSON字节数(非hex长度)

信息

路径
/firmware/通信模块/MN316 NBIOT模块/MN316 NB-IoT驱动代码 — STM32 UART+DMA+IDLE · MQTT物模型上报.md
更新时间
2026/5/26