软件复位由于RESET_MEM_ALLOC_FAIL

⚠️
大家好. .感谢来到论坛。令人兴奋的消息!我们现在正在转移到新的论坛平台的过程中,它将提供更好的功能,并包含在主对话网站。所有的帖子和账号已经迁移。我们现在只接受新论坛的流量-请发布任何新的帖子在//www.wsdof.com/support.我们会在接下来的几天修复bug /优化搜索和标记。
2个职位/ 0个新职位
最后发表
kwilliams
离线
最后看到:2个月3个星期前
加入:2020-09-23 20:34
软件复位由于RESET_MEM_ALLOC_FAIL

由于我的外设上的RESET_MEM_ALLOC_FAIL,我正在运行软件重置。让我先快速概述一下我想要实现的行为:

  1. 中心连接外设(无配对或键合)
  2. 中央订阅定制服务的“按钮状态”特征的通知1
  3. Central将一个非零值写入自定义服务1中的控制点特征。
  4. 在外围设备上,这将开始我在代码中称为“采样循环”(下面的代码片段)的操作。这个循环实际上只是一个app_easy_timer,它会一直重置自己,直到将0写到前面提到的控制点,它会检查DA14531 TINY子板(SW2)上的按钮的当前状态,并通过通知将其发送到中央。
  5. 一旦用户连续3秒没有按下按钮,“不活动计时器”就会触发。这将停止采样循环(即取消其计时器),并通过wakeupct启用一个按钮中断唤醒(在同一个按钮上,SW2)。此时,设备应该进入睡眠状态,只有当按钮被按下或必须发生连接事件时才会醒来(我将延迟设置为499,所以这些间隔应该非常大)。
  6. 下次用户按下按钮时,设备应唤醒,将按钮按下通知到中央(此通知是至关重要的),并恢复采样循环。
  7. 重复步骤4 - 6,直到将0写入控制点。

可以看出,这非常类似于ble_app_peripheral,它是我使用的起点。

我面临的问题是,有时(不是所有时间!)RESET_MEM_ALLOC_FAIL会在非活动计时器即将触发时被触发。在重置发生之前,外围设备通常需要在采样和休眠之间进行几个周期的切换,但在触发重置之前发生的周期数量是不一致的。

我在这里做了一些研究,似乎RESET_MEM_ALLOC_FAIL的通常原因是消息处理不当。我直接分配的唯一消息是按钮状态通知(参见下面的custSrvSetButtonState()),但我不认为我产生这些消息的速度快于它们的消耗速度,因为采样计时器延迟与连接间隔相同。

当重置发生时,我使用了堆日志记录来捕获堆的状态,下面是输出:

>>> env堆

在这个堆中使用的大小:496(当前)- 512(最大)

在其他堆中使用的大小:404(当前)- 420(最大)

>>> db堆<<

此堆中使用的尺寸:592(电流) - 592(最大值)

在其他堆中使用的大小:0(当前)- 0(最大)

>>> msg heap <<<

在此堆中使用的大小:1336(当前)- 1356(最大)

在其他堆中使用的大小:1180(当前)- 1180(最大)

>>>非ret堆<<<

在此堆中使用的大小:0(当前)- 196(最大)

在其他堆中使用的大小:0(当前)- 0(最大)

将MSG_HEAP_SZ增加到6880后,仍然会使用以下堆日志输出进行重置:

>>> env堆

在此堆中使用的大小:496(当前)- 528(最大)

在其他堆中使用的大小:404(当前)- 404(最大)

>>> db堆<<

此堆中使用的尺寸:592(电流) - 592(最大值)

在其他堆中使用的大小:0(当前)- 0(最大)

>>> msg heap <<<

在此堆中使用的大小:6856(当前)- 6856(最大)

在其他堆中使用的大小:1180(当前)- 1196(最大)

>>>非ret堆<<<

在此堆中使用的大小:0(当前)- 196(最大)

在其他堆中使用的大小:0(当前)- 0(最大)

我怀疑堆大小是问题所在,因为我没有做任何密集的事情(我每30毫秒发送一个值为一个字节的通知)。所以,对我来说,它看起来像是内存泄漏,但我不确定在内存管理方面我做错了什么。任何帮助都将不胜感激!非常感谢大家。

相关代码片段:

/ * *宏***************************************************************************************** */ #define CUSTS1_SAMPLE_TIMER_DELAY MS_TO_TIMERUNITS(30) #define CUSTS1_INACTIVITY_TIMEOUT MS_TO_TIMERUNITS(3000) /* * GLOBAL VARIABLE DEFINITIONS **************************************************************************************** */ ke_msg_id_t samplingTimer __SECTION_ZERO("retention_mem_area0"); //@RETENTION MEMORY ke_msg_id_t inactivityTimer __SECTION_ZERO("retention_mem_area0"); //@RETENTION MEMORY /* * FUNCTION DEFINITIONS **************************************************************************************** */ static void custSrvStartSampleLoop(void) { samplingTimer = app_easy_timer(CUSTS1_SAMPLE_TIMER_DELAY, custSrvSampleLoopTimerCallback); inactivityTimer = app_easy_timer(CUSTS1_INACTIVITY_TIMEOUT, custSrvInactivityTimerCallback); arch_puts("Sample loop started\r\n"); } static void custSrvStopSampleLoop(void) { app_easy_timer_cancel(samplingTimer); app_easy_timer_cancel(inactivityTimer); samplingTimer = EASY_TIMER_INVALID_TIMER; inactivityTimer = EASY_TIMER_INVALID_TIMER; arch_puts("Sample loop stopped\r\n"); } /** **************************************************************************************** * @brief Updates the state of the button so that the kernel can notify the subscriber. * @param[in] _pressed Whether the button is pressed. **************************************************************************************** */ static void custSrvSetButtonState(const bool _pressed) { struct custs1_val_ntf_ind_req *req = KE_MSG_ALLOC_DYN(CUSTS1_VAL_NTF_REQ, prf_get_task_from_id(TASK_ID_CUSTS1), TASK_APP, custs1_val_ntf_ind_req, DEF_SVC1_BUTTON_STATE_CHAR_LEN); if (req == NULL) { arch_puts("Failed to allocate notification\r\n"); return; } uint8_t buttonState = _pressed ? CUSTS1_BUTTON_DOWN : CUSTS1_BUTTON_UP; req->conidx = app_env->conidx; req->handle = SVC1_IDX_BUTTON_STATE_VAL; req->length = DEF_SVC1_BUTTON_STATE_CHAR_LEN; req->notification = true; memcpy(req->value, &buttonState, DEF_SVC1_BUTTON_STATE_CHAR_LEN); ke_msg_send(req); } void custSrvControlPointWriteHandler(ke_msg_id_t const msgid, struct custs1_val_write_ind const *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { uint8_t val = 0; memcpy(&val, ¶m->value[0], param->length); // note(KJW): we may want to use bools instead of using the timer variables to check state if ((val != CUSTS1_CONTROL_POINT_DISABLE) && (samplingTimer == EASY_TIMER_INVALID_TIMER)) custSrvStartSampleLoop(); else if ((val == CUSTS1_CONTROL_POINT_DISABLE) && (samplingTimer != EASY_TIMER_INVALID_TIMER)) custSrvStopSampleLoop(); } void custSrvSampleLoopTimerCallback(void) { if (GPIO_GetPinStatus(BTN_PORT, BTN_PIN)) { custSrvSetButtonState(false); if (inactivityTimer == EASY_TIMER_INVALID_TIMER) inactivityTimer = app_easy_timer(CUSTS1_INACTIVITY_TIMEOUT, custSrvInactivityTimerCallback); } else { custSrvSetButtonState(true); if (inactivityTimer != EASY_TIMER_INVALID_TIMER) { app_easy_timer_cancel(inactivityTimer); inactivityTimer = EASY_TIMER_INVALID_TIMER; } } if (ke_state_get(TASK_APP) == APP_CONNECTED) { // Set it once again until Stop command is received in Control Characteristic samplingTimer = app_easy_timer(CUSTS1_SAMPLE_TIMER_DELAY, custSrvSampleLoopTimerCallback); } } void custSrvInactivityTimerCallback(void) { if (ke_state_get(TASK_APP) == APP_CONNECTED) { arch_puts("Inactivity limit reached, sleepytime...\r\n"); app_easy_timer_cancel(samplingTimer); samplingTimer = EASY_TIMER_INVALID_TIMER; wkupct_register_callback(custSrvButtonWakeupCallback); wkupct_enable_irq(WKUPCT_PIN_SELECT(BTN_PORT, BTN_PIN), WKUPCT_PIN_POLARITY(BTN_PORT, BTN_PIN, WKUPCT_PIN_POLARITY_LOW), // button is active LOW 1, // 1 event (press) 10); // debouncing time = 10 ms // TODO there are also "wkupct2" versions of the wakeupct functions, are they any better? } } void custSrvButtonWakeupCallback(void) { #if !defined (__DA14531__) if (GetBits16(SYS_STAT_REG, PER_IS_DOWN)) #endif { // not sure if this is necessary. // wkupct_quadec.h says that it is. // however, in the sample code (ble_app_sleepmode), it's only done when waking from sleep before a connection has been established. periph_init(); } arch_puts("Button wake up\r\n"); //wkupct_disable_irq(); not sure if this is necessary. only seems to be necessary if canceling irq before the callback is called. custSrvStartSampleLoop(); custSrvSetButtonState(true); }

我的连接参数:

static const struct connection_param_configuration user_connection_param_conf ={///双槽最小连接间隔(1.25ms) ///使用宏MS_TO_DOUBLESLOTS从毫秒(ms)转换为双槽。intv_min = MS_TO_DOUBLESLOTS(30),//使用宏MS_TO_DOUBLESLOTS从毫秒(ms)转换为双槽。intv_max = MS_TO_DOUBLESLOTS(30), ///连接事件测量的延迟。Latency = 499,//使用宏MS_TO_TIMERUNITS将毫秒(ms)转换为计时器单位。time_out = MS_TO_TIMERUNITS(32000),///使用宏MS_TO_DOUBLESLOTS从毫秒(ms)转换为双槽。ce_len_min = MS_TO_DOUBLESLOTS(0),//使用宏MS_TO_DOUBLESLOTS从毫秒(ms)转换为双槽的最大连接事件持续时间。ce_len_max = MS_TO_DOUBLESLOTS(0),};

PM_Dialog
离线
最后看到:1周3天前
工作人员
加入:2018-02-08 11:03
嗨kwilliams,

嗨kwilliams,

感谢您的详细信息,并感谢您对我们的TINY模块BLE解决方案的兴趣。这是一个平台重置。Τhe platform_reset_func()由platform_reset()调用,它在ROM代码中实现。正如您提到的,产生此断言的最可能的原因是内存不足,因为您分配的消息永远不会被使用。例如,如果您正在分配通知消息,并且您有一个小的连接间隔,那么消息将被堆起来直到连接事件到达,但是如果连接间隔较大,则在连接事件到达之前就会耗尽内存。您可以增加连接间隔。在您的应用程序中,在每个连接之后可能会出现某种内存泄漏堆积,因为错误代码是RESET_MEM_ALLOC_FAIL。为此,请检查是否有任何挂起消息,并确保您正在使用在处理消息时获得的消息,或者您正在分配应该释放它们的数据。

在附件的代码中,我告诉你使用了小的连接间隔。你能试着增加连接间隔并并行地保持增加堆大小吗?

谢谢,PM_Dialog