SAML21 One Shot reload procedure

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hi guys, I need a little bit of help.

I have to move all my code, on SAM L21 microcontroller.

But how it's possible to reload a TC timer set as one shot mode ?

I used the wizard examples and everything has been good for other peripherals. But not for this timer.

It works perfectly as one shot, but later after the first time, I didn't understand how to reload it.

I have to reload it on every uart data received so, after the right period, I 'm going to have a elapsed interrupt to understand the modbus end of frame.

If you need more details, no problem.

Thanks in advance.

Attilio

 




// INIT TIMER
bool TC4_Inizialize(void)
{
	bool ret_val = false;

	TC4_TimerStru.interval = GetModbusEndOfFrameTime(); -> about 3,6 ms with baud = 9600
	TC4_TimerStru.cb       = TC4_ModbusEndOfFrame_Handler;
	TC4_TimerStru.mode     = TIMER_TASK_ONE_SHOT;
	if(timer_add_task(&NEWTIMER_TC4, &TC4_TimerStru) == ERR_NONE)
	{
		if(timer_start(&NEWTIMER_TC4) == ERR_NONE)
		{
			ret_val = true;
		}
	}
	return ret_val;
}



// this function is called on every received char



void ReloadModbusEoFTimer(void)
{
  // here I have to reload the timer, so after 3.5 char of silence (no reload), I will have the TC4 handle

}




// HANDLE TIMER
static void TC4_ModbusEndOfFrame_Handler(const struct timer_task *const timer_task)
{
	ModbusAvailableFrameFlag = true;
	// ecc. ecc.
}

 

 

 

 

 

 

 

 

Last Edited: Mon. Aug 9, 2021 - 10:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe this helps: 

https://community.atmel.com/foru...

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Lajon.

Thanks for your help.

To be honest I tried that kind of solution. But unfortunately it's not the right procedure.

In fact when I send a char from serial, that operation cause a fault handler.

If I send one char may be it works properly.

But as soon I try with more than one (2 or or more chars from serial) it faults.

There must be a simple procedure like send a retrigger command. It is written in the datasheet too, but it doesn't work and I dont'know why.

probably I lose something other.

Attilio

 

 

Here a little piece of datasheet info.

 

"Re-Trigger Command and Event Action
A re-trigger command can be issued from software by writing the Command bits in the Control B Set register
(CTRLBSET.CMD = 0x1, RETRIGGER), or from event when a re-trigger event action is configured in the Event
Control register (EVCTRL.EVACT = 0x1, RETRIGGER).
When the command is detected during counting operation, the counter will be reloaded or cleared, depending on the
counting direction (CTRLBSET.DIR or CTRLBCLR.DIR). When the re-trigger command is detected while the counter
is stopped, the counter will resume counting from the current value in the COUNT register."

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From looking at the code that start timer (HAL:Driver:Timer) in one-shot mode is not implemented using the one shot mode of the TC so anything like setting CTRLBSET.CMD is not going to work.

It would be more efficient to just use the TC but I think it should work to reload the timer like this:

timer_remove_task(&NEWTIMER_TC4, &TC4_TimerStru);
timer_add_task(&NEWTIMER_TC4, &TC4_TimerStru);

But if you get to the fault handler from just calling timer_add_task I guess there could be some other problem with your project.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hi Lajon, thanks for your help.

My apologies for late reply.

 

I have checked the code but I didn't find any error. If you call the suggested function, there is a fault handler, always.

You can check it from the related example too when the project is created.

 

Anyway, I found the right solution. The problem was that in the first interrupt handler, the related callback handler it's automatically removed. It means that in the

second call the interrupt handler it's skipped, forever.

To relod the timer ( I mean at hardware level) it's enought to clear and then to set the enable bit in the CTRLA register. The hardware, answer immediatly with its interrupt

call, but the related callback isn't in the list.

More over you need do reload the label too, to carry on the counter ready for the next interrupt.

 

I wrote this code and now it work perfectly, for my purpose.

Attilio.

 

 


void ModbusTime_Handler(const struct timer_task *const timer_task)
{
	timer_add_task(&MDBTIMER, &MdbTimerStru);
	timer_stop(&MDBTIMER);
}


void ReloadModbusEoFTimer(void)
{	
	hri_tc_clear_CTRLA_ENABLE_bit(MDBTIMER.device.hw);
	MdbTimerStru.time_label = MDBTIMER.time;
	hri_tc_set_CTRLA_ENABLE_bit(MDBTIMER.device.hw);
	timer_start(&MDBTIMER);
}

 

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greetings, AttilioR!
I have the exact same problem but with an SAML10E16A! I've tried your solution and it didn't work. I mean, at least my device doesn't freeze anymore but  my timer does not trigger again as its supposed to do.
Could you give me a little help? I can't find out what I'm doing wrong. 

#include <atmel_start.h>

uint8_t toggle;
uint32_t timer;


static struct timer_task TIMER_0_task1;//, TIMER_0_task2;

/**
 * Example of using TIMER_0.
 */
static void TIMER_0_task1_cb(const struct timer_task *const timer_task)
{
	//timer_stop(&TIMER_0);
	//timer++;
	//toggle++;
	//gpio_set_pin_level(PA25, true);
	gpio_toggle_pin_level(TEST1);
	timer_add_task(&TIMER_0, &TIMER_0_task1);
	timer_stop(&TIMER_0);
	
	//(TC0)->COUNT16.INTENCLR.reg = TC_INTENSET_MC0_Msk;

}

//static void TIMER_0_task2_cb(const struct timer_task *const timer_task)
//{
//}

//void ModbusTime_Handler(const struct timer_task *const timer_task)
//{
//timer_add_task(&MDBTIMER, &MdbTimerStru);
//timer_stop(&MDBTIMER);
//}

void ReloadModbusEoFTimer(void)
{
	hri_tc_clear_CTRLA_ENABLE_bit(TIMER_0.device.hw);
	TIMER_0_task1.time_label = TIMER_0.time;
	hri_tc_set_CTRLA_ENABLE_bit(TIMER_0.device.hw);
	timer_start(&TIMER_0);
}

void TIMER_0_e(void)
{
	TIMER_0_task1.interval = 1;
	TIMER_0_task1.cb       = TIMER_0_task1_cb;
	//TIMER_0_task1.mode     = TIMER_TASK_REPEAT;
	TIMER_0_task1.mode     = TIMER_TASK_ONE_SHOT;
	timer_add_task(&TIMER_0, &TIMER_0_task1);
	timer_start(&TIMER_0);
}

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	
	gpio_set_pin_level(TEST1, false);
	
	TIMER_0_e();
	
	toggle = 0;
	/* Replace with your application code */
	while (1) 
	{
		gpio_toggle_pin_level(TEST0);
		
		toggle++;
		if (toggle > 10)
		{
			ReloadModbusEoFTimer();
		
			toggle = 0;
		}
		
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You should at least wait for TIMER_0_task1_cb to be called before you call ReloadModbusEoFTimer, your 10 toggle loop will be done before the callback has been called I guess.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

oh my... you're probably right! I feel so stupid now hahahaha. I think I had the MCU configured for 4 Mhz and the timer for 50 microsecond intervals. I'll test that on monday once I get back to the lab. Thanks a bunch, Lars!