DTU低功耗
1 低功耗操作
1.1 Clock Gating
在系统空闲时,开启Clock Gating可以让DTU进入暂停状态,基带中断或外部中断可唤醒,然后系统继续运行。
1.2 睡眠:RTC唤醒
设定时间后进入睡眠,使用RTC定时唤醒。这种方式比较简单,精度不是很高。
1.3 睡眠:DFE唤醒
设定时间后进入睡眠,使用DFE定时唤醒。这种方式相对RTC唤醒时间精度更高,达到us级别。
1.4 睡眠:同步信号检测唤醒
进入睡眠,基带会一直检测同步信号,一旦检测到同步唤醒信号,并确认唤醒ID正确就,则被唤醒。
1.5 睡眠:信号检测唤醒
进入睡眠,基带会一直检测信号,一旦检测到唤醒信号,并确认唤醒ID正确就,则被唤醒。
2 低功耗测试
2.1 测试模型
一种应用场景是把DTU作为一个模组使用,受MCU控制。
这里采用IOTE评估板作为MCU控制另外一个IOTE评估板(需要有外部32k晶振)演示DTU低功耗测试。
测试模型步骤:
步骤1:启动IOTE,等待其接入网关
步骤2:启动MCU,MCU向IOTE发送数据,发送完数据进入阻塞,等待通知信号
步骤3:IOTE收到数据后,将数据透传到网关,透传完数据,进入睡眠(DFE唤醒)
步骤3:IOTE醒来后通知MCU进行下次数据发送
步骤4:MCU收到通知信号,进行数据发送。然后进行步骤3,如此循环。
这里的睡眠时间满足:发送数据周期时间 = 发送数据时间 + 睡眠时间 + 程序启动到下次发送的时间,程序会自动计算,只需设置发送周期就行了。
MCU的参考程序在本文末尾。
2.2 参数配置
发送要求:MCU发送数据长度50字节、发送周期时间2s。
可以计算出在symbol len为256,MCS为3的情况下,IOTE需要发送2包才能发完,发送时长小于300ms(不重传),预留了足够的时间给IOTE睡眠和启动。
子系统配置参数(网关和IOTE保持一致):
名称 | 描述 |
---|---|
symbol len | 设1,代表256 |
下行上行比 | 设0,代表1:1 |
上行group数量 | 设0,代表1 |
其他保持一致,保证能正常接入网关 |
网关端:
名称 | 描述 |
---|---|
上行编码 | 设2,代表16进制编码 |
连接态时间 | 设3,需要大于发送间隔时间2s |
广播帧发送周期 | 设1 |
上行发送周期 | 设2000,代表2000ms的发送间隔 |
发送一次数据需要的帧数 | 设2,代表需要发送2包才能发完一次数据 |
IOTE端:
名称 | 描述 |
---|---|
接入设备类型 | 设0,同步到网关 |
使能外部晶振 | 设1,用外部晶振时间才准确 |
硬件板类型 | 设1,代表IOTE评估板 |
连接态时间 | 设3,与网关端保持一致 |
数据速率 | 设3,即MCS设为3 |
分时发送开关 | 设1,开启后IOTE按照网关分配时隙发送数据 |
唤醒后IO输出开关 | 设1,开启唤醒后IO输出 |
唤醒后IO输出pin脚 | 设0x11,对应十进制为17 |
唤醒后IO输出高电平保持时间 | 设10,代表10ms |
2.3 开始测试
如下图,将MCU和IOTE连接起来。其中IOTE评估板的GPIO17有针脚引出,同时连接了未定义指示功能的LED灯,有信号时可以观察到该灯的闪烁情景。
先保证IOTE正常运行,接入到网关。然后插上跳帽线启动MCU板。
观察MCU日志信息,是否正常发送数据;观察IOTE日志信息,是否收到配置命令,正常发送数据到网关,然后进入睡眠;
在MQTT工具中,查看收到的MQTT消息内容与发送信息是否一致,收到的时间是否满足2s时间间隔(选择时间精度高且刷新快的MQTT工具)。
IOTE日志和MCU日志信息如下:
MQTT消息如下:
观察MQTT消息接收时间,满足2s间隔。此时可以观察IOTE运行状态指示灯和其他指示灯,睡眠时所有灯是熄灭的。
程序调通后,可以将MCU单独供电,在静态数据表中把如下IOTE的灯都关闭,然后使用仪器测试功耗。
附录:
低功耗测试的MCU示例程序
#include <rtthread.h>
#include <rtdevice.h>
#include "uc_gpio.h"
const rt_uint8_t g_data[] = { \
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, \
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, \
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, \
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, \
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
static rt_device_t g_user_com;
static rt_thread_t g_send_data_thread;
static rt_sem_t g_awaken_sem;
static rt_uint32_t g_send_cnt;
static void handle_awake_inform(void *p)
{
if (g_awaken_sem)
{
rt_kprintf("g_awaken_sem release\n");
rt_sem_release(g_awaken_sem);
}
}
static void send_data_thread(void *p)
{
rt_uint16_t send_len = 0;
rt_thread_mdelay(2000);
send_len = rt_device_write(g_user_com, 0, g_data, sizeof(g_data));
rt_kprintf("send len %d send cnt %d\n", send_len, ++g_send_cnt);
while (1)
{
rt_sem_take(g_awaken_sem, RT_WAITING_FOREVER);
rt_kprintf("g_awaken_sem take success\n");
send_len = rt_device_write(g_user_com, 0, g_data, sizeof(g_data));
rt_kprintf("send len %d send cnt %d\n", send_len, ++g_send_cnt);
}
}
int main(void)
{
rt_err_t open_ret;
g_user_com = rt_device_find("uart0");
if (g_user_com)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
RT_ASSERT(RT_Device_Class_Char == g_user_com->type);
config.baud_rate = BAUD_RATE_4800;
rt_device_control(g_user_com, RT_DEVICE_CTRL_CONFIG, &config);
open_ret = rt_device_open(g_user_com, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
if (RT_EOK != open_ret)
{
rt_kprintf("open device uart0 fail\n");
}
}
else
{
rt_kprintf("not find device uart0\n");
RT_ASSERT(0);
}
gpio_set_pin_mux(UC_GPIO_CFG, GPIO_PIN_17, GPIO_FUNC_0);
rt_pin_write(GPIO_PIN_17, PIN_LOW);
rt_pin_mode(GPIO_PIN_17, PIN_MODE_INPUT);
rt_pin_attach_irq(GPIO_PIN_17, PIN_IRQ_MODE_RISING, handle_awake_inform, NULL);
rt_pin_irq_enable(GPIO_PIN_17, PIN_IRQ_ENABLE);
g_awaken_sem = rt_sem_create("awaken_sem", 0, RT_IPC_FLAG_PRIO);
g_send_data_thread = rt_thread_create("send_data_thread", send_data_thread, NULL, 512, 5, 3);
rt_thread_startup(g_send_data_thread);
return 0;
}