Author Topic: Delay function - Atmel SAM D21 without ASF  (Read 7155 times)

0 Members and 1 Guest are viewing this topic.

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Delay function - Atmel SAM D21 without ASF
« on: October 23, 2016, 08:12:39 pm »
Hello everybody

I am needing to implement a general delay function in my code without ASF Atmel (Delay Service).
 As I don't know any thing about Assembly, what is the best way to get it with very good accuracy once I will need delays in micro seconds.
Could I do it with TC or only Assembly is the best way?

Regards


 
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #1 on: October 23, 2016, 09:49:11 pm »
Something like this. Now tested, cut and modified from a working project.
Code: [Select]
void timer_init(void)
{
  PM->APBCMASK.reg |= PM_APBCMASK_TC4;

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC4_GCLK_ID) |
      GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

  TC4->COUNT16.CTRLA.reg =
      TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT16_Val) |
      TC_CTRLA_WAVEGEN(TC_CTRLA_WAVEGEN_MFRQ_Val) |
      TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV8_Val) |
      TC_CTRLA_PRESCSYNC(TC_CTRLA_PRESCSYNC_PRESC_Val);
  TC4->COUNT16.COUNT.reg = 0;
  TC4->COUNT16.CC[0].reg = TIMER_TOP;
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
}

void timer_delay(uint16_t us)
{
  uint16_t target = TC4->COUNT16.COUNT.reg + us;

  if (target > TIMER_TOP)
    target -= TIMER_TOP;

  TC4->COUNT16.INTFLAG.reg = TC_INTFLAG_MC1;

  TC4->COUNT16.CC[1].reg = target;

  while (0 == TC4->COUNT16.INTFLAG.bit.MC1);
}
Alex
 
The following users thanked this post: Franc

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #2 on: October 23, 2016, 09:52:36 pm »
But you may get better accuracy by writing something like this:
Code: [Select]
for (int i = 0; i < NNN; i++)
  asm("nop");
And calibrate the value of NNN to your desired delay. This delay will be affected by background interrupts, but if you are working with accurate us delays, then you should disable interrupts for the duration of the delay, of the whole operation that required this delay.
Alex
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #3 on: October 23, 2016, 10:22:47 pm »
Dear Alex, thanks for always share your knowleadge.

I found this routine below in assembly in other forum.  I don't know how it works but could be used in any SAM D

Do you think I will get a nice acurracy ? So I can save one TC and memory space.


Code: [Select]
#ifndef F_CPU
#error CPU frequency not defined in sam_gpio.h // Taradov gpio definitions
#endif
#define _CyclesPerDelayLoop 6
#define _LoopCount_ms(ms) (uint32_t)(((F_CPU / 1000UL) * (ms)) / _CyclesPerDelayLoop)
#define _LoopCount_us(us) (uint32_t)(((F_CPU / 1000000UL) * (us)) / _CyclesPerDelayLoop)
#define ddDelay_ms(ms) _ddCycleDelay(_LoopCount_ms(ms))
#define ddDelay_us(us) _ddCycleDelay(_LoopCount_us(us))

inline void _ddCycleDelay(volatile uint32_t count) {
asm volatile (
"mov r0, %0 \n" // Get the loop count
"loop%=:    \n" // Loop start label
"DMB        \n" // Data Memory Barrier (3 cycles)
"sub r0, #1 \n" // Decrement count     (1 cycle)
"bne loop%= \n" // Branch back to loop (2 cycles)
: // Nothing output from assembly
: "r" (count) // Register input for count
: "r0", "cc" // Clobber list
);
}
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #4 on: October 23, 2016, 10:24:51 pm »
That's a fancy way to write my second code in assembly. Same limitations about interrupts apply here as well.
Alex
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #5 on: October 23, 2016, 10:38:22 pm »
Quote
This delay will be affected by background interrupts, but if you are working with accurate us delays, then you should disable interrupts for the duration of the delay, of the whole operation that required this delay.

Sorry, I did not understand  it totally. Do you have an example? I will miss accuracy if I use this delay together a NVIC or inside a NVIC?
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #6 on: October 23, 2016, 10:41:59 pm »
Sorry, I did not understand  it totally. Do you have an example? I will miss accuracy if I use this delay together a NVIC or inside a NVIC?
This function counts number of cycles assuming that each cycle takes some time to execute (1 / F_CPU seconds per cycle).

If interrupt happens while you are in this loop, counting will stop for the duration of the interrupt and will resume from the same point once interrupt routine exits. So delay performed by this function will be extended by the time it took to execute interrupt handler.
Alex
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #7 on: October 23, 2016, 10:48:59 pm »
Ohh cool, I understood.  Therefore there is not other way to do a secure delay routine once TC also is affected by NVIC?
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #8 on: October 23, 2016, 10:57:59 pm »
Ohh cool, I understood.  Therefore there is not other way to do a secure delay routine once TC also is affected by NVIC?
Why do you think TC is affected by NVIC?

TC code waits for the flag to be set. That waiting can be interrupted, as as long as this interruption is not longer that the actual delay.

You need to temporarily block interrupts if you want exact intervals.
Alex
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #9 on: October 23, 2016, 11:24:16 pm »
Quote
You need to temporarily block interrupts if you want exact intervals.

in DS, page 48,  'For the NVIC to activate the interrupt, it
must be enabled in the NVIC interrupt enable register (SETENA/CLRENA bits in ISER/ICER).'
Maybe the best way to disabled is Assembly when the accuracy is very important? (maybe in some points of my firmware)
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #10 on: October 23, 2016, 11:28:23 pm »
Maybe the best way to disabled is
Just use standard code for critical sections:
Code: [Select]
    #define ATOMIC_SECTION_ENTER   { register uint32_t __atomic; \
                                     __asm volatile ("mrs %0, primask" : "=r" (__atomic) ); \
                                     __asm volatile ("cpsid i");
    #define ATOMIC_SECTION_LEAVE   __asm volatile ("msr primask, %0" : : "r" (__atomic) ); }

Use:
Code: [Select]
ATOMIC_SECTION_ENTER
// code that must not be interrupted goes here
ATOMIC_SECTION_LEAVE

And keep atomic sections as short as possible, of course. All pending interrupts will be served after the code leaves the atomic section.

Assembly when the accuracy is very important? (maybe in some points of my firmware)
Hos is assembly  supposed to help with this? Assembly code is interrupted by interrupts as well.
Alex
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #11 on: October 23, 2016, 11:41:47 pm »
Fantastic Alex....thanks again!! I will use as you recommended.

Quote
Hos is assembly  supposed to help with this? Assembly code is interrupted by interrupts as well.

Below is assembly and I don't know anything in Assembly language. I need to buy one good book to learn it in ARM .  |O

Code: [Select]
   #define ATOMIC_SECTION_ENTER   { register uint32_t __atomic; \
                                     __asm volatile ("mrs %0, primask" : "=r" (__atomic) ); \
                                     __asm volatile ("cpsid i");
    #define ATOMIC_SECTION_LEAVE   __asm volatile ("msr primask, %0" : : "r" (__atomic) ); }
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #12 on: October 23, 2016, 11:44:35 pm »
Below is assembly and I don't know anything in Assembly language. I need to buy one good book to learn it in ARM .  |O
All documentation is available from ARM. In this case you need to be looking for the following documents:

DDI0419C_arm_architecture_v6m_reference_manual.pdf (instruction set and core registers)
DDI0484C_cortex_m0p_r0p1_trm.pdf (actual core).

Both are available from arm.com after registration.
Alex
 
The following users thanked this post: Franc

Offline westfw

  • Super Contributor
  • ***
  • Posts: 3098
  • Country: us
Re: Delay function - Atmel SAM D21 without ASF
« Reply #13 on: October 24, 2016, 12:13:00 am »
For "arduino-like" delay() and delay_microseconds() on a SAMD10, I used the RTC peripheral, configured to count at 1MHz (microseconds) and interrupt at 1kHz (milliseconds.)  This would give you delays accurate to within, say, +/- 2us, but probably not small numbers of microseconds with 1/10th microsecond accurate...
Code is at https://github.com/WestfW/SAMD10-experiments/blob/master/D10-LED_TOGGLE0/src/UserSource/ticker_rtc.c
I don't know if this is a great way to do it; I had a horrible time with clock synchronization (and IIRC ataradov was a big help!)
 
The following users thanked this post: Franc

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #14 on: October 26, 2016, 11:39:32 pm »
Hi friends!
Is possible to do a delay for nanoseconds ? I will get a nice accuracy?

What is the best way, just changing the macro below or I defining the delay variable as volatile (delay_us(0.100))?
In AVR forum I saw some people using the ASM 'NOP or defining the delay as volatile in delay_us() to get nano delays. This is a way in SAM?

Quote
#define _LoopCount_us(us)  (uint32_t)(((F_CPU / 1000000UL) * (us)) / _CyclesPerDelayLoop)


Regards!
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Delay function - Atmel SAM D21 without ASF
« Reply #15 on: October 26, 2016, 11:43:01 pm »
Quote
I will need delays in micro seconds.

It may not be possible.

For something that fast, using NOP() is likely the way to go.

For longer delays, you may just loop around or using a hardware timer.
================================
https://dannyelectronics.wordpress.com/
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #16 on: October 26, 2016, 11:43:28 pm »
Is possible to do a delay for nanoseconds ? I will get a nice accuracy?
Well, if MCU runs at 48 MHz, then it takes more than 20 ns to execute one instruction (including NOP).

Also, you need to run the code from RAM, since Flash has at least one wait state.
Alex
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Delay function - Atmel SAM D21 without ASF
« Reply #17 on: October 26, 2016, 11:48:02 pm »
Quote
I found this routine below in assembly in other forum.  I don't know how it works but could be used in any SAM D

it works, unless SAM D has different instruction sets.

Assemblies generally are not portable.

Quote
Do you think I will get a nice acurracy ?

If that kind of approaches works for you, you don't need accurate delays.

and the whole thing can take an entirely different direction.
================================
https://dannyelectronics.wordpress.com/
 

Offline Franc

  • Regular Contributor
  • *
  • Posts: 52
  • Country: br
Re: Delay function - Atmel SAM D21 without ASF
« Reply #18 on: October 26, 2016, 11:53:30 pm »
So, if I need 100ns of delay, is possible I use 5 * NOP?

regards!
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 6102
  • Country: us
    • Personal site
Re: Delay function - Atmel SAM D21 without ASF
« Reply #19 on: October 26, 2016, 11:54:37 pm »
So, if I need 100ns of delay, is possible I use 5 * NOP?
Yes, provided your code runs from RAM and MCU is clocked at 48 MHz.
Alex
 

Offline rob42

  • Contributor
  • Posts: 9
  • Country: aq
Re: Delay function - Atmel SAM D21 without ASF
« Reply #20 on: November 05, 2016, 07:51:11 am »
Code: [Select]
#define DELAY_LOOP_CYCLES 3.0 // cycles per loop

#define DELAY_US_LOOPS(US) ((u32)((double)(US) * F_CPU / DELAY_LOOP_CYCLES / 1000000.0))
#define DELAY_MS_LOOPS(MS) ((u32)((double)(MS) * F_CPU / DELAY_LOOP_CYCLES / 1000.0))
#define DELAY_S_LOOPS (S)  ((u32)((double)(S)  * F_CPU / DELAY_LOOP_CYCLES))
#define delay_us( US ) delay_loops( DELAY_US_LOOPS(US) )
#define delay_ms( MS ) delay_loops( DELAY_MS_LOOPS(MS) )
#define delay_s ( S )  delay_loops( DELAY_S_LOOPS(S) )

void delay_loops(u32 loops) {
asm(".syntax unified");
asm volatile (
"1: SUBS %[loops], %[loops], #1 \n"
"   BNE 1b \n"
: [loops] "+r"(loops)
);
asm(".syntax divided");
}
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf