ESP-IDF Hardware Timers

fofi144
Posts: 7
Joined: Fri Jan 18, 2019 5:09 pm

ESP-IDF Hardware Timers

Postby fofi144 » Fri Jan 18, 2019 5:32 pm

Hi guys,

this is my first post here. So if I'm in the wrong topic or anything else feel free to correct me ;) .

Normally I am working with Atmel Xmega controllers, but as soon as i discovered the ESP32 I fell in love with it :D . So here I am, trying to understand FreeRtos and the ESP-IDF API. After reading the FreeRtos Tutorial, I am currently trying to get the hardware timers to work. At the moment everything works fine, as long as I am using 1 Timer out of 1 group. When I try to use both timers, some weird things are happening (stuck in Interrupt?)

Here's my library for the timers:

Code: Select all

#ifndef HW_Timer_H
#define HW_Timer_H

#include <stdio.h>
#include "driver/timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"

//define Struct 
typedef struct
{
	timer_idx_t Timer_id;
	TaskHandle_t Taskptr;	
}Timer_Spec_t;


/*Timer Init

 Timer is counting up!

 Timer Group = TIMER_GROUP_0 or TIMER_GROUP_1
 Timer_id= TIMER_0 or TIMER_1
 Division= Division Faktor on the 80Mhz Pher-Clock
 Timer_Ovfl= Value when the ISR is triggered 
 Timer_reload= TIMER_AUTORELOAD_EN/TIMER_AUTORELOAD_DIS

*/
void Timer_Init(timer_group_t Timer_Group, timer_idx_t Timer_id, uint32_t Divider, uint64_t Timer_Ovfl, bool Timer_reload,TaskHandle_t *Taskptr);

//ISR Handling Routine of Timer_Group_0
void IRAM_ATTR timer_group0_isr(void *para);


#endif // !HW_Timer_H

Code: Select all

#include "HW_Timers2.h"


//*************************************************************************************************************************/
//												FUNKTIONEN
//*************************************************************************************************************************/
void Timer_Init(timer_group_t Timer_Group, timer_idx_t Timer_id, uint32_t Divider, uint64_t Timer_Ovfl, bool Timer_reload, TaskHandle_t *Taskptr)
{
	//define Struct for ISR Pointer
	static Timer_Spec_t Timer_Specs;
	Timer_Specs.Taskptr = *Taskptr;
	Timer_Specs.Timer_id = Timer_id;
	
	//Timer Init. Group0, Timer0
	timer_config_t Zeitbasis;
	Zeitbasis.divider = Divider;
	Zeitbasis.counter_dir = TIMER_COUNT_UP;
	Zeitbasis.counter_en = TIMER_PAUSE;
	Zeitbasis.alarm_en = TIMER_ALARM_EN;
	Zeitbasis.auto_reload = Timer_reload;
	Zeitbasis.intr_type = TIMER_INTR_LEVEL;
	
	//Init Timer
	timer_init(Timer_Group, Timer_id, &Zeitbasis);
	
	//Set timer to start value
	timer_set_counter_value(Timer_Group, Timer_id, 0x00000000ULL);
	
	//Set timer alarm
	timer_set_alarm_value(Timer_Group, Timer_id, Timer_Ovfl);
	
	//Interrupt enable
	timer_enable_intr(Timer_Group, Timer_id);
	
	
	if (Timer_Group == TIMER_GROUP_0)
	{
		timer_isr_register(Timer_Group, Timer_id, timer_group0_isr, (void *) &Timer_Specs, ESP_INTR_FLAG_IRAM, NULL);	
	}
	else
	{
		//TODO	
	}
	
	//Timer start
	timer_start(Timer_Group, Timer_id);		
	
}



//*************************************************************************************************************************/
//												Interrupt Service
//*************************************************************************************************************************/
void IRAM_ATTR timer_group0_isr(void *para)
{
	
	
	//save the data from the pointer
	Timer_Spec_t Settings =  *((Timer_Spec_t *) para);
	
	//Get interrupt status
	uint32_t intr_status = TIMERG0.int_st_timers.val;
	
	
	//Delete interrupt flags 
	if((intr_status & BIT(Settings.Timer_id)) && (Settings.Timer_id) == TIMER_0) 
	{
		TIMERG0.int_clr_timers.t0 = 1;
	}
	else if((intr_status & BIT(Settings.Timer_id)) && (Settings.Timer_id) == TIMER_1) 
	{
		TIMERG0.int_clr_timers.t1 = 1;
	}	
	
	//Reactivate alarm
	TIMERG0.hw_timer[Settings.Timer_id].config.alarm_en = TIMER_ALARM_EN;	
	
	
	//TaskNotify
	xTaskNotifyFromISR(Settings.Taskptr,0x00,eNoAction,NULL);
	portYIELD_FROM_ISR();
	
}
This is my main:

Code: Select all

#include <stdio.h>
#include "esp_types.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/timer_group_struct.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#include "HW_Timers2.h"

	

void Handler(void *para)
{
	static int i;
	
	while (true)
	{
		xTaskNotifyWait(0x00, 0xffffffff, NULL, portMAX_DELAY);

		if (i == 0)
		{
			i = 1;
			gpio_set_level(2, 0);
		}
		else
		{
			i = 0;
			gpio_set_level(2, 1);
		}
		
	}
	
}
	

void Handler2(void *para)
{
	static int i;
	
	while (true)
	{
		xTaskNotifyWait(0x00, 0xffffffff, NULL, portMAX_DELAY);

		if (i == 0)
		{
			i = 1;
			gpio_set_level(4, 0);
		}
		else
		{
			i = 0;
			gpio_set_level(4, 1);
		}
		
	}
	
}

void app_main()
{
	TaskHandle_t Handle;
	TaskHandle_t Handle2;
	
	//Debug
	gpio_pad_select_gpio(2);
	gpio_set_direction(2, GPIO_MODE_OUTPUT);
	gpio_set_level(2, 0);
	
	gpio_pad_select_gpio(4);
	gpio_set_direction(4, GPIO_MODE_OUTPUT);
	gpio_set_level(4, 0);
	
	
	
	xTaskCreate(Handler, "Handler_task", 2048, NULL, 4, &Handle);
	xTaskCreate(Handler2, "Handler_task2", 2048, NULL, 3, &Handle2);
	
	Timer_Init(TIMER_GROUP_0, TIMER_0, 16, 12140, TIMER_AUTORELOAD_EN,&Handle);
	Timer_Init(TIMER_GROUP_0, TIMER_1, 64, 23150, TIMER_AUTORELOAD_EN, &Handle2);
}
If I uncomment one of the Timer_Init functions, then the timer works fine. (picture shows timer0)
working.PNG
working.PNG (32.05 KiB) Viewed 11806 times
As soon as I try to init both timers this happens:
not_working.PNG
not_working.PNG (19.94 KiB) Viewed 11806 times
It seems, that the Handler2 function always gets its task notification.
But why? Does the interrupt get constantly triggered?

Greetings from Germany,
Florian

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP-IDF Hardware Timers

Postby ESP_Sprite » Sat Jan 19, 2019 4:02 am

Hallo Florian,

I see a few issues with your code:
- You declare Timer_Specs as static. This means there's only one of them through your entire code. This means that as soon as you initialize the 2nd timer, you overwrite the Timer_Specs data in the 1st timer. In this case, you're better off doing something like 'Timer_Spec_t Timer_Specs_p = calloc(sizeof(Timer_Specs_t), 1)' to allocate a new instance every time the init function is called.
- With your line 'Timer_Spec_t Settings = *((Timer_Spec_t *) para);', I think the compiler copies all the settings from your original object to a local Settings variable, which you then proceed to use. There's no real harm in that at this point, but do note that changes to Settings won't propagate back to the original Timer_Spec_T object that gets fed into the ISR. Better use a pointer: 'Timer_Spec_t *Settings_p = ((Timer_Spec_t *) para);'

Note that both changes result in a Timer_Spec_t pointer rather than an object being used, so you'll need to reference to members with `Timer_Specs_p->Taskptr=bla` instead of `Timer_Specs.Taskptr=bla`.

fofi144
Posts: 7
Joined: Fri Jan 18, 2019 5:09 pm

Re: ESP-IDF Hardware Timers

Postby fofi144 » Sun Jan 20, 2019 12:53 pm

Hello ESP_Sprite,

thanks for your help and your really good explanations. I didn't pay attention to the static variable being used multiple times, as soon as using multiple timers. Now it's totally clear for me :oops: .

So I corrected my code and everything works fine :)

Code: Select all

#include "HW_Timers2.h"


//*************************************************************************************************************************/
//												FUNKTIONEN
//*************************************************************************************************************************/
void Timer_Init(timer_group_t Timer_Group, timer_idx_t Timer_id, uint32_t Divider, uint64_t Timer_Ovfl, bool Timer_reload, TaskHandle_t *Taskptr)
{
	//define Struct for ISR Pointer
	Timer_Spec_t *Timer_Specs_p = calloc(sizeof(Timer_Spec_t), 1);
	Timer_Specs_p->Taskptr = *Taskptr;
	Timer_Specs_p->Timer_id = Timer_id;
	
	//Timer Init. Group0, Timer0
	timer_config_t Zeitbasis;
	Zeitbasis.divider = Divider;
	Zeitbasis.counter_dir = TIMER_COUNT_UP;
	Zeitbasis.counter_en = TIMER_PAUSE;
	Zeitbasis.alarm_en = TIMER_ALARM_EN;
	Zeitbasis.auto_reload = Timer_reload;
	Zeitbasis.intr_type = TIMER_INTR_LEVEL;
	
	//Init Timer
	timer_init(Timer_Group, Timer_id, &Zeitbasis);
	
	//Set timer to start value
	timer_set_counter_value(Timer_Group, Timer_id, 0x00000000ULL);
	
	//Set timer alarm
	timer_set_alarm_value(Timer_Group, Timer_id, Timer_Ovfl);
	
	//Interrupt enable
	timer_enable_intr(Timer_Group, Timer_id);
	
	
	if (Timer_Group == TIMER_GROUP_0)
	{
		timer_isr_register(Timer_Group, Timer_id, timer_group0_isr, (void *) Timer_Specs_p, ESP_INTR_FLAG_IRAM, NULL);	
	}
	else
	{
		timer_isr_register(Timer_Group, Timer_id, timer_group1_isr, (void *) Timer_Specs_p, ESP_INTR_FLAG_IRAM, NULL);	
	}
	
	//Timer start
	timer_start(Timer_Group, Timer_id);		
	
}



//*************************************************************************************************************************/
//												Interrupt Service
//*************************************************************************************************************************/
void IRAM_ATTR timer_group0_isr(void *para)
{
	
	
	//save the data from the pointer
	Timer_Spec_t *Settings =  (Timer_Spec_t *) para;
	
	//Get interrupt status
	uint32_t intr_status = TIMERG0.int_st_timers.val;
	
	
	//Delete interrupt flags 
	if((intr_status & BIT(Settings->Timer_id)) && (Settings->Timer_id) == TIMER_0) 
	{
		TIMERG0.int_clr_timers.t0 = 1;
	}
	else if((intr_status & BIT(Settings->Timer_id)) && (Settings->Timer_id) == TIMER_1) 
	{
		TIMERG0.int_clr_timers.t1 = 1;
	}	
	
	//Reactivate alarm
	TIMERG0.hw_timer[Settings->Timer_id].config.alarm_en = TIMER_ALARM_EN;	
	
	
	//TaskNotify
	xTaskNotifyFromISR(Settings->Taskptr,0x00,eNoAction,NULL);
	portYIELD_FROM_ISR();
	
}


void IRAM_ATTR timer_group1_isr(void *para)
{
	
	
	//save the data from the pointer
	Timer_Spec_t *Settings =  (Timer_Spec_t *) para;
	
	//Get interrupt status
	uint32_t intr_status = TIMERG1.int_st_timers.val;
	
	
	//Delete interrupt flags 
	if((intr_status & BIT(Settings->Timer_id)) && (Settings->Timer_id) == TIMER_0) 
	{
		TIMERG1.int_clr_timers.t0 = 1;
	}
	else if((intr_status & BIT(Settings->Timer_id)) && (Settings->Timer_id) == TIMER_1) 
	{
		TIMERG1.int_clr_timers.t1 = 1;
	}	
	
	//Reactivate alarm
	TIMERG1.hw_timer[Settings->Timer_id].config.alarm_en = TIMER_ALARM_EN;	
	
	
	//TaskNotify
	xTaskNotifyFromISR(Settings->Taskptr, 0x00, eNoAction, NULL);
	portYIELD_FROM_ISR();
	
}

Code: Select all

#ifndef HW_Timer_H
#define HW_Timer_H

#include <stdio.h>
#include "driver/timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"

//define Struct 
typedef struct
{
	timer_idx_t Timer_id;
	TaskHandle_t Taskptr;	
}Timer_Spec_t;


/*Timer Init

 Timer is counting up!

 Timer Group = TIMER_GROUP_0 or TIMER_GROUP_1
 Timer_id= TIMER_0 or TIMER_1
 Division= Division Faktor on the 80Mhz Pher-Clock
 Timer_Ovfl= Value when the ISR is triggered 
 Timer_reload= TIMER_AUTORELOAD_EN/TIMER_AUTORELOAD_DIS

*/
void Timer_Init(timer_group_t Timer_Group, timer_idx_t Timer_id, uint32_t Divider, uint64_t Timer_Ovfl, bool Timer_reload,TaskHandle_t *Taskptr);

//ISR Handling Routine of Timer_Group_0
void IRAM_ATTR timer_group0_isr(void *para);
void IRAM_ATTR timer_group1_isr(void *para);


#endif // !HW_Timer_H
Just one question at the end: Does anything speak against using one ISR for all 4 Timers (2 Groups a 2 Timers)? I would have to pass my timer group value to the ISR, but with the struct this would be easy. Pretty Noob question, but everyone starts with little things ;)

Have a nice day!

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP-IDF Hardware Timers

Postby ESP_Sprite » Tue Jan 22, 2019 1:58 am

No, that's a perfectly feasible way to do things, especially useful if the code that handles the interrupts is similar between timers.

fofi144
Posts: 7
Joined: Fri Jan 18, 2019 5:09 pm

Re: ESP-IDF Hardware Timers

Postby fofi144 » Fri Jan 25, 2019 11:25 am

Hi :)

is there a way to set the counter of the hardware timer to a specific value during runtime? I would need it to synchronize two devices, that are sending messages over RF.

Why?:
Both of the devices have a internal timer running. It creates time-slices, that tell the devices when to send and when not. One device is a master, one a slave. The slave has to sync on the master RF packets. So, if the slave receives a paket (GPIO interrupt by the RF module) and it turns out to be a master frame (reading the fifo over SPI in the GPIO-Int handler), the the timer has to be set to a specific offset value, that equals the time elapsed since the interrupt. Doing that,the timers of both devices would be synchronous to each other.

Have a nice day :)

ezalegria
Posts: 2
Joined: Thu Apr 16, 2020 12:10 am

Re: ESP-IDF Hardware Timers

Postby ezalegria » Thu Apr 16, 2020 3:48 am

hi, i am new using esp32, and i have doubts about interrupt task. Why do they use a while inside the handle, according to which I would stay inside the while forever or am I wrong? I have used the same code as you, but still I cannot generate a blink. Pls any help or explain about the code.

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP-IDF Hardware Timers

Postby ESP_Sprite » Thu Apr 16, 2020 8:18 am

'Handler', in his code is a task (that happens to get unblocked every now and then by something that happens to be an interrupt), not an interrupt handler.

ezalegria
Posts: 2
Joined: Thu Apr 16, 2020 12:10 am

Re: ESP-IDF Hardware Timers

Postby ezalegria » Thu Apr 16, 2020 11:32 pm

Hi ESP_Sprite, If you wanted to create an interrupt for each cycle of timer 0 or 1 (1 kHz or another number) to enable or disable GPIO or some other peripheral, wouldn't it be the same? Maybe that's where my confusion comes from.

ESP_Sprite
Posts: 9724
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP-IDF Hardware Timers

Postby ESP_Sprite » Fri Apr 17, 2020 11:22 am

Sorry, I have problems understanding your question, but in general perhaps it's easier if you start your own topic to fully describe the issue you're running into?

Who is online

Users browsing this forum: Gaston1980, Majestic-12 [Bot] and 263 guests