xTimerCreate with hours/days cycle

azz-zza
Posts: 45
Joined: Tue Sep 17, 2019 2:58 am

xTimerCreate with hours/days cycle

Postby azz-zza » Sun Nov 03, 2019 8:06 pm

Hello,

Code: Select all

static const TickType_t t_outlet_cycle = 43200000 ; /* 12 hours in ms = 60 000*60*12 ;  */
....

xTimerCreate(
                        "timer1Sec", /* name */
                        pdMS_TO_TICKS(t_outlet_cycle), /* period/time ms */
                        pdTRUE, /* auto reload */
                        (void*)0, /* timer ID */
                        CallBack/* callback */
                    );
Based on the:

Code: Select all

#define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )
#define configTICK_RATE_HZ				( CONFIG_FREERTOS_HZ )
#define CONFIG_FREERTOS_HZ 1000
and

Code: Select all

#if( configUSE_16_BIT_TICKS == 1 )
	typedef uint16_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffff
#else
	typedef uint32_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
at best i get 65 000 which is slightly less then i need (43 200 000 = 12 h).

What is the proper technique to have a timer to sleep for hours?

azz-zza
Posts: 45
Joined: Tue Sep 17, 2019 2:58 am

Re: xTimerCreate with hours/days cycle

Postby azz-zza » Mon Nov 04, 2019 3:35 am

i suppose i can :
pass number of floor(65k/time) groups to the task ( that awaits event group bit set);
create a timer with pdMS_TO_TICKS(round(hours/ #groups));
in callback function wait for the # of bit sets == # of groups before actually triggering the action....
but this is ugly....

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: xTimerCreate with hours/days cycle

Postby ESP_Angus » Mon Nov 04, 2019 7:05 am

Hi azz-zza,

ESP-IDF doesn't set configUSE_16_BIT_TICKS. I think the problem is that the first part of the pdMS_TO_TICKS calculation overflows the integer type:

Code: Select all

#define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )
Specifically, "( xTimeInMs ) * configTICK_RATE_HZ ) " will overflow if xTimeInMs is > (INT_MAX / configTICK_RATE_HZ) = (0x7FFFFFFF / 1000) = 2147483. If this part overflows, the macro result will be wrong.

There are a few options here:

1) Pre-calculate the ticks without using pdMS_TO_TICKS macro. In that case you can count up to UINT_MAX ticks == 0xFFFFFFFF ticks, so it should work. Writing (t_outlet_cycle / portTICK_RATE_MS) instead should work (as portTICK_RATE_MS equals 1 in the default config.
2) Set a static "hours" variable somewhere to 12, set the timer for an hour, have the timer callback count down from 12.
3) Use the high precision timer API. This is overkill, because you get microsecond precision. But it's a 64-bit signed timer value, so it can give over 250,000 year timeout values.

azz-zza
Posts: 45
Joined: Tue Sep 17, 2019 2:58 am

Re: xTimerCreate with hours/days cycle

Postby azz-zza » Tue Nov 05, 2019 12:04 am

ESP,
thank you for the reply.
I found similiar issue filed in 2016 - https://sourceforge.net/p/freertos/bugs/135/

Barry noted:
pdMS_TO_TICKS() will only get defined in projdefs.h if it is not already defined by the user. Now, admittedly, this is probably not documented anywhere (!)(?), but the definition appears as follows:

#ifndef pdMS_TO_TICKS
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) )
#endif
the version i have does NOT have that logic:

Code: Select all

/home/az/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/freertos/freertos/projdefs.h (2019 Apr 16 is the timestamp)

<skip>
#ifndef PROJDEFS_H
#define PROJDEFS_H

/*
 * Defines the prototype to which task functions must conform.  Defined in this
 * file to ensure the type is known before portable.h is included.
 */
typedef void (*TaskFunction_t)( void * );

/* Converts a time in milliseconds to a time in ticks. */
#define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )
where as the trunc has it defined properly:

Code: Select all

https://sourceforge.net/p/freertos/code/HEAD/tree/trunk/FreeRTOS/Source/include/projdefs.h
typedef void (*TaskFunction_t)( void * );

/* Converts a time in milliseconds to a time in ticks.  This macro can be
overridden by a macro of the same name defined in FreeRTOSConfig.h in case the
definition here is not suitable for your application. */
#ifndef pdMS_TO_TICKS
	#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) )
#endif
I'll raise the ticket with PIO .

Meanwhile i'll proceed with your suggestions # 1 as the most streamlined ( read : I'm been lazy ) .
thank you.
AZZ

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: xTimerCreate with hours/days cycle

Postby ESP_Angus » Tue Nov 05, 2019 3:50 am

azz-zza wrote:
Tue Nov 05, 2019 12:04 am
the version i have does NOT have that logic:
...
I'll raise the ticket with PIO .
This code comes from ESP-IDF so probably they'll ask you to raise a ticket against IDF.

The updated version doesn't quite solve the problem - TickType_t is uint32_t in ESP-IDF so the overflow still happens but at double the value - if xTimeInMs is > (UINT_MAX / configTICK_RATE_HZ) = (0xFFFFFFFF / 1000) = 4294967. Still less than 1.2 hours if measured in milliseconds.
azz-zza wrote:
Tue Nov 05, 2019 12:04 am
Meanwhile i'll proceed with your suggestions # 1 as the most streamlined ( read : I'm been lazy ) .
thank you.
Sounds good, I think this is the best solution.

azz-zza
Posts: 45
Joined: Tue Sep 17, 2019 2:58 am

Re: xTimerCreate with hours/days cycle

Postby azz-zza » Tue Nov 05, 2019 11:24 pm


idahowalker
Posts: 166
Joined: Wed Aug 01, 2018 12:06 pm

Re: xTimerCreate with hours/days cycle

Postby idahowalker » Wed Nov 06, 2019 5:03 pm

Consider using the ULP to have a timer that waits for hours or days or????

Here is some code I got from someplace and changed it a little for my own use:

Code: Select all

void ULP_BLINK_RUN(uint32_t us);
int int_Pin = 2;
volatile unsigned int IntCount = 0;
const int ULP_WAKE_INTERVAL = 1000; // a microsecond value
setup()
{
 ULP_BLINK_RUN( ULP_WAKE_INTERVAL );
}
/*
   Using the blink demo, the ULP is loaded with a value, the value changes the state of GPIO2 which is used as an input for an interrupt that triggers a function.

   So, if you want to use the data over reboot, store it into the RTC memory by defining a global variable with RTC_DATA_ATTR attribute. For example, RTC_DATA_ATTR int bootCount = 0;

*/
void ULP_BLINK_RUN(uint32_t us)
{
  int memPortState = 8000; // memory address outside of program space to use
  int memCounts = 8001; // memory address to hold a count
  size_t load_addr = 0;
  RTC_SLOW_MEM[memPortState] = 0;
  RTC_SLOW_MEM[memCounts] = 0;
  ulp_set_wakeup_period(0, us);
  const ulp_insn_t  ulp_blink[] =
  {
    I_MOVI( R2, memCounts ), // get info from memCounts address
    I_LD( R1, R2, 0 ),       // put contents of memCounts into R1
    I_ADDI( R1, R1, 1 ),     // Add 1 to R1 holding result into R1
    I_ST( R1, R2, 0 ),       // Put R1 into mem address pointed to by R2
    I_MOVI(R3, memPortState),               // memPortState -> R3
    I_LD(R0, R3, 0),                        // R0 = RTC_SLOW_MEM[R3(memPortState)]
    M_BL(1, 1),                             // GOTO M_LABEL(1) IF R0 < 1
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1),  // RTC_GPIO2 = 1
    I_SUBI(R0, R0, 1),                      // R0 = R0 - 1, R0 = 1, R0 = 0
    I_ST(R0, R3, 0),                        // RTC_SLOW_MEM[R3(memPortState)] = R0
    M_BX(2),                                // GOTO M_LABEL(2)
    M_LABEL(1),                             // M_LABEL(1)
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0),  // RTC_GPIO2 = 0
    I_ADDI(R0, R0, 1),                      // R0 = R0 + 1, R0 = 0, R0 = 1
    I_ST(R0, R3, 0),                        // RTC_SLOW_MEM[R3(memPortState)] = R0
    M_LABEL(2),                             // M_LABEL(2)
    I_HALT()                                // HALT COPROCESSOR
  };
  rtc_gpio_init( GPIO_NUM_2 ); // GPIO2 built in led
  rtc_gpio_set_direction( GPIO_NUM_2, RTC_GPIO_MODE_INPUT_OUTPUT );
  rtc_gpio_set_level( GPIO_NUM_2, 0);
  size_t size = sizeof(ulp_blink) / sizeof(ulp_insn_t);
  ulp_process_macros_and_load( load_addr, ulp_blink, &size);
  ulp_run( load_addr );
} // void ULP_BLINK_RUN(uint32_t us)
Instead of counting up some large uS number just set a few variables in the RTC ram and use it like days:hours:minues:seconds...

These are 2 32bit address spaces set aside in rtc ram:

Code: Select all

 int memPortState = 8000; // memory address outside of program space to use
  int memCounts = 8001; // memory address to hold a count
Starting at RTC address 8K you can get about 100 variables.

azz-zza
Posts: 45
Joined: Tue Sep 17, 2019 2:58 am

Re: xTimerCreate with hours/days cycle

Postby azz-zza » Sat Nov 09, 2019 2:17 pm

Thank you Idaho for you input and the example. It is helpful.

I went with the ESP's solution #1, as it required the minimum amount of changes to my code.

At the same time, i think, all this counting business is very inefficient from pwer consumtion perspective. But i'm not up against that yet and will revisit sometime later.

Who is online

Users browsing this forum: Google [Bot] and 49 guests