Wake-up & Sleep over LIN on ATSAMC21J18A microcontroller with FreeRTOS

Hello,

I have configure the FreeRTOS with low power tickless ideal mode (used MPLAB Harmony) with LIN slave .I have configured 3 application task , They running independently, don’t have any blocking state/events. I have to go to sleep when no communication ( no data) on LIN bus for 1 sec & wake up again once communication started. I used timer to monitor the LIN bus. The timer interrupts (1s) is occur when no communication on LIN & calling vPortSuppressTicksAndSleep (). I written own function vPortSuppressTicksAndSleep() calling inside PM_StandbyModeEnter().
but controller not going to sleep . Can any one help here?

Thanks,

Can you try calling PM_StandbyModeEnter() without FreeRTOS and see if your device goes to sleep (may be try calling it from main)?

yes, without freeRTOS it working fine but i want to be work with freeRTOS

Is it possible for you to share your implementation of vPortSuppressTicksAndSleep?

Next step is to check if there are any pending interrupts or other tasks that are preventing the device from going to sleep. Try commenting out interrupts and tasks and see if you can narrow down the issue.

how to find the version of vPortSuppressTicksAndSleep()?

its FreeRTOS Kernel V10.5.1

I am sure their is no pending interrupts & before calling vPortSuppressTicksAndSleep() i am suspending all task

What @tony-josi-aws meant to ask is to share your implementation of vPortSuppressTicksAndSleep. Does the default implementation of vPortSuppressTicksAndSleep work for you?

Also, can you share the implementation of PM_StandbyModeEnter? Does it look like this - csp_apps_sam_d20/apps/ac/ac_sleepwalk_singleshot/firmware/src/config/sam_d20_xpro/peripheral/pm/plib_pm.c at master · Microchip-MPLAB-Harmony/csp_apps_sam_d20 · GitHub?

Thanks for reply.
yes default implementation of vPortSuppressTicksAndSleep not work.
before own implementation , I am directly calling vPortSuppressTicksAndSleep(2) but not work , microcontroller not going to sleep. hence I write own implementation of vPortSuppressTicksAndSleep,check below but it also not work (i am not sure how to stop systick , as well below code may wrong) :
void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{

   vTaskSuspend(xLINTASK_Tasks);
    vTaskSuspend(xREADINGLAMP_Tasks);
    vTaskSuspend(xPWMTASK_Tasks);
     PM_StandbyModeEnter();
    vTaskResume(xLINTASK_Tasks);
    vTaskResume(xREADINGLAMP_Tasks);
    vTaskResume(xPWMTASK_Tasks);                 

}
you can refer below link , use same implementation of PM_StandbyModeEnter

ttps://github.com/Microchip-MPLAB-Harmony/csp_apps_sam_c20_c21/tree/master/apps/pm/pm_wakeup_eic

Thanks,

Your implementation of vPortSuppressTicksAndSleep is not disabling the interrupts. Ideally, vPortSuppressTicksAndSleep should be called by the IDLE task when no task is ready to execute, which would program the systick timer to interrupt after the xExpectedIdleTime has elapsed or another interrupt (likey external) has tiggered.

Is it possible for you to make your other tasks blocking based on the status of the LIN bus so that the IDLE task can take care of putting your device to tickless sleep mode?

Sure , I can make other 3 tasks blocking based on the status of the LIN bus but how & which is good way (mutex, semaphore etc) to block other task. Could you share me examples & where should i put PM_StandbyModeEnter() ?

I’d suggest to use this default implementation first - FreeRTOS-Kernel/portable/GCC/ARM_CM0/port.c at main · FreeRTOS/FreeRTOS-Kernel · GitHub. The only thing PM_StandbyModeEnter does extra is SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; and I think you can do that by defining configPRE_SLEEP_PROCESSING. Something like the following:

#define configPRE_SLEEP_PROCESSING( xModifiableIdleTime )   \
    if( xModifiableIdleTime > 0 )                           \
    {                                                       \
        SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;                  \
    }

Thank you. can you give me example for putting the task in block state?

When a task waits for an event to happen, it is in the blocked state. These event can be anything -

  1. Wait for an item to arrive in a queue - xQueueReceive.
  2. Wait for a task notification - xTaskNotifyWait.
  3. Wait for a semaphore to be signaled - xSemaphoreTake.
  4. Wait for a bit to be set in an event group - xEventGroupWaitBits.

Thank you Gaurav, i used xEventGroupWaitBits & its working fine. Now if any interrupt is come i need to set the events hence all task again start to run.
now one issue is that when all called xEventGroupSetBits(xEventGroup,ALL_TASK_WAKE_UP_EVENT); inside configPOST_SLEEP_PROCESSING it prevent to go to sleep. Not sure how to solve it ?

Why do you want to set bits from configPOST_SLEEP_PROCESSING? Can you share your task definitions?

check below implementation of 3 task. When is LIN timeout occurs ,need to clearing the eventsBits for blocking the all task and set the eventBits to run the all tasks when one or many interrupts occur .

   (void) xTaskCreate((TaskFunction_t) lLINTASK_Tasks,
                "LINTASK_Tasks",
                1024,
                NULL,
                3,
                &xLINTASK_Tasks);

    /* Create OS Thread for READINGLAMP_Tasks. */
    (void) xTaskCreate((TaskFunction_t) lREADINGLAMP_Tasks,
                "READINGLAMP_Tasks",
                512,
                NULL,
                2,
                &xREADINGLAMP_Tasks);

    /* Create OS Thread for PWMTASK_Tasks. */
    (void) xTaskCreate((TaskFunction_t) lPWMTASK_Tasks,
                "DIMMING_Tasks",
                128,
                NULL,
                1,
                &xDIMMING_Tasks);
				
EventGroupHandle_t  xEventGroup;
const EventBits_t xWaitLinEvent = LIN_TASK_WAKE;
#define LIN_TASK_WAKE (1<<0u)
#define DIM_TASK_WAKE (1<<1u)
#define LAMP_TASK_WAKE (1<<2u)
#define ALL_TASK_WAKE_UP_EVENT (LIN_TASK_WAKE|DIM_TASK_WAKE|LAMP_TASK_WAKE)

void LIN_Runnable(void) 
{
    EventBits_t xEventLIN;
    xEventLIN  = xEventGroupWaitBits(xEventGroup,
                                            xWaitLinEvent,
                                            pdTRUE,
                                            pdTRUE,
                                            portMAX_DELAY
                                            );
    if((xEventLIN & LIN_TASK_WAKE) !=0)
    {
        LIN_MainFunction();
        
        if(rxTimoutCntr < LIN_Timeout)
        {
            xEventGroupSetBits(xEventGroup,ALL_TASK_WAKE_UP_EVENT);
            sleepState = IDEL;
        }
        else
        {
            
               TC2_TimerStop();
              xEventGroupClearBits(xEventGroup,ALL_TASK_WAKE_UP_EVENT);  
              sleepState = SLEEP;
        }
    }
}


const EventBits_t xWaitLampEvent = LAMP_TASK_WAKE;
void DimmingRunnable(void)
{
    EventBits_t xEventDimming;
    xEventDimming  = xEventGroupWaitBits(xEventGroup,
                                            xWaitDimEvent,
                                            pdTRUE,
                                            pdTRUE,
                                            portMAX_DELAY
                                            );
    if((xEventDimming & DIM_TASK_WAKE) !=0)
    {
        Dimming_MainFunction();
    }
   
}

const EventBits_t xWaitLampEvent = LAMP_TASK_WAKE;
void ReadingLampRunnable(void)
{
    EventBits_t xEventReadLamp;
    xEventReadLamp  = xEventGroupWaitBits(xEventGroup,
                                            xWaitLampEvent,
                                            pdTRUE,
                                            pdTRUE,
                                            portMAX_DELAY
                                            );
    if((xEventReadLamp & LAMP_TASK_WAKE) !=0)
    {
        ReadLamp_MainFunction();
    }

}

The code you shared seems incomplete. Does the following describe correctly what you want to achieve:
lLINTASK_Tasks waits for LIN_TASK_WAKE event bit. When LIN_TASK_WAKE is set, it wakes up lREADINGLAMP_Tasks and lPWMTASK_Tasks by setting corresponding bits.

If the above is correct, you need to change the xEventGroupSetBits call in the LIN_Runnable task to wake only the 2 tasks -

xEventGroupSetBits( xEventGroup,LIN_TASK_WAKE | DIM_TASK_WAKE );

You do not need anything extra in configPOST_SLEEP_PROCESSING.

i miss below function called on power ON.

void createBlockEvent(void)
{ 
    /* Attempt to create the event group. */
    xEventGroup = xEventGroupCreate();
    /* Was the event group created successfully? */
    if( xEventGroup == NULL )
    {
        /* The event group was not created because there was insufficient
        FreeRTOS heap available. */
    }
    else
    {
        /* The event group was created. */
        xEventGroupSetBits(xEventGroup,LIN_TASK_WAKE);
    }  
}

at power on I set LIN_TASK_WAKE event & LIN Task set events for other 2 task.
when No lin communication below else part is called & go in sleep

else
        {
            
               TC2_TimerStop();
              xEventGroupClearBits(xEventGroup,ALL_TASK_WAKE_UP_EVENT);  
              sleepState = SLEEP;
        }

but when LIN or other (total 6 )external interrupt occurs I need to call LIN_MainFunction() hence in post sleep config i need to set LIN_TASK_WAKE event