Unknown function 0xA60: seems to happen while calling a particular application API only

OK I'm seeing this weird behavior that I'm trying to understand

I have a sensor task that reads sensor data continuously every 4 seconds. The value is then notified to the subscriber (NotificationManager) which then sends the data to UART, (and later to BLE custom service. 

Until I connect the BLE service from nRF Connect app, it all works ok (I see the UART print fine)

But when I attempt to connect to the BLE service via nRF Connect App, the program halts at 0xA60. (I have previously dealt with issues around logging and I wonder if it's related to it). Note that when I comment out the Notify call, the BLE connects OK. What does that tell us?

void MCP9808::Run()
{
    Write(Register::AMBIENT, 1);
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    while(true)
    {
        Read();
        
        uint32_t taskNotify = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        ParseTempInC();
        //Notify();  // uncommenting out results in A60 error upon BLE connection

        vTaskDelay(pdMS_TO_TICKS(DELAY_MS_PER_READ));
    }
}

void NotificationManager::Update(Publisher* publisher) 
{
    // retrieve the publisher's category
    auto category = publisher->GetCategory();

    switch (category)
    {
      case Publisher::Category::TEMPERATURE:
      {
	    MCP9808* mcp9808 = dynamic_cast<MCP9808*>(publisher);
	    if (!mcp9808) return;
	    
	    uint16_t value = mcp9808->GetTempInC();

	    char msg[100] = {0};
	    snprintf (msg, sizeof(msg) - 1, "Temp = %uC", value);
	    PushNotification(MakeNotification(msg), 
      }
    }
}  

Notification NotificationManager::MakeNotification(char* msg)
{
    Notification notification = {0};
    strncpy(notification.msg, msg, sizeof(notification.msg));
    notification.category = category;
    return notification; 
}

void NotificationManager::PushNotification(Notification notification)
{
    // write data to UART
    mUART.Write(notification.msg);
    
    // updating the custom value in the BLE characteristic table
    // mBLENotifierSrv.UpdateValue(reinterpret_cast<uint8_t*>(notification.msg));
}

  • Forgot to answer this question.

    morpho said:
    What's the actual stack size of nRF52840? It's set to 2KB in Segger config

    Stack size is compiler language. With nRF52840 we should talk about RAM and Flash size. You can set the whole RAM as stack but that depends on the architecture of the solution. If you are using BLE, then the softdevice uses some stack space in the RAM. On top of that the FreeRTOS kernel and tasks uses some stack space in the RAM and on top of that your application  needs stack space in RAM.

    The main stack size you are seeing there is the stack size of the main thread (typically the main() function), You might know this already but if you don't then please read about ARM Cortex M4 interrupt and Main thread contexts which are hardware related. Linked one of those threads for your convinience.

  • I increased configTOTAL_HEAP_SIZE to 8192 and the tasks' stack depth to 200 and I no longer see the issue.

    But why do you think increasing the heap size is a temporary solution? I changed the heap size back to 4096 with 200 stack depth and it still works. Previously just increasing the stack depth resulted in  NRF_ERROR_NO_MEM.

    Can you make up a conclusion?

  • morpho said:
    But why do you think increasing the heap size is a temporary solution? I changed the heap size back to 4096 with 200 stack depth and it still works. Previously just increasing the stack depth resulted in  NRF_ERROR_NO_MEM.

    I might have confused you there. What I meant to say is that increase the HEAP size to a large number temporarily and when we know which task stack was overflowing, then you can calibrate the increase in heap size and increase in the stack size of that task to a value that is not too big.

    While debugging I normally change the heap size to 4096 and the stack size of every task to atleast 512 bytes. But on application with tight available memory, this might be too much. 

    So my use of temporary was only to temporarily increase the heap size to a very big number and the temporarily increase to the task size to a very big number just to pinpoint  the rogue task that is using more stack than ancipated. And then for release version of your application you can fine tune the the heap and stack size (hence moving away from the temporary high values)

  • While debugging I normally change the heap size to 4096 and the stack size of every task to atleast 512 bytes. But on application with tight available memory, this might be too much. 

    I already am on 4096 heap and 400 bytes of stack for a task.

    Anyways, there's one task named "Process" but here, buff contains "Pro". Do you see why it could be?

    extern "C"
     {
        void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
        {
            ( void ) pcTaskName;
            ( void ) xTask;
    
            char buff[100] = {0};
            strncpy(buff, (const char *)pcTaskName, sizeof(buff) - 1); // buff = "Pro"
    
            UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        
            while(1);
        }
    }

  • maybe do a strncpy like below instead

    strncpy(buff, (const char *)pcTaskName, configMAX_TASK_NAME_LEN); 

    Or check in the memory if there is only Pro in the sequential address of the memory of if it has the whole "Process" name and it is just the matter of using the right size in the API.

Related