delay_cycles using DWT registers

Rick Kimball
Mon Jun 15, 2015 9:20 pm
I got pretty spoiled with other microcontroller architectures that allowed you to use delay_cycles to do sub microsecond delays just by using NOPS. The cortex-m3 has all kinds of things going on like interrupts, code caching, pipeling, etc. In reality, you can’t hand count the number of instructions cycles for a given chunk of code. Fortunately, on the cortex-m3 they do provide a Debug Watchpoint Trace peripheral that can be hijacked and used to implement a delay_cycles function. I created a class and packaged it up as a header file here:

https://gist.github.com/RickKimball/8bc … 6f73ccf2e9

To use it in your program do something like this. include the header file, create an instance of dwt_timer, call its init() function in setup(). Then use the delay_cycles() function with the number of cycles you want to delay.
//
#include "dwt_timer.h"

static const dwt_timer usec_timer;

void setup() {
usec_timer.init();
}

void loop() {
DoSomething1();
usec_timer.delay_cycles((usec2cycles(1)-0)); /* optionally subtract cycles to deal with DoSomething1()'s overhead */
DoSomething2();
usec_timer.delay_cycles((usec2cycles(9)-0));
}


RogerClark
Mon Jun 15, 2015 10:04 pm
Rick,

Perhaps the current delay() can be replaced with this version.

I looked at the current version some time ago, and leaflabs made an effort to get the timing right, but from the comments I’ve seen on the original maple forum, basically the delay function is not very accurate.


Rick Kimball
Mon Jun 15, 2015 11:01 pm
I would put this stuff in the category of “Wet Paint”. I got it working and decided to share the wealth. You might want to let it age some. I’m not sure if there are any implications of using the DWT stuff with debug. It seems to work. However, I haven’t fully researched this. I was just trying to see if I could achieve more accurate microsecond timing.

Here is another code snippet, using the dwt_timer.get() method to figure out how many cycles the digitalWrite functions take, and account for them with the delay:
// slightly more accurate usec blink

#include "dwt_timer.h"
#define PIN PB12
static const dwt_timer usec_timer;

void setup() {
pinMode(PIN, OUTPUT);
usec_timer.init();
}

void loop() {
uint32_t s0, e0;
s0 = usec_timer.get();
digitalWrite(PIN,1);
e0 = usec_timer.get();
const uint32_t overhead = e0-s0;
digitalWrite(PIN,0);
const uint32_t overhead1 = (e0-s0) + 21; /* the 21 hmm .. just kept tweaking until it was 10 usec cycle*/

while(1) {
digitalWrite(PIN,1);
usec_timer.delay_cycles(usec2cycles(1)-(overhead));
digitalWrite(PIN,0);
usec_timer.delay_cycles(usec2cycles(9)-(overhead1));
}
}


RogerClark
Mon Jun 15, 2015 11:23 pm
Rick,

OK.

Its definitely worth bearing in mind, as a future replacement to delay() (and microsecond delay)


stuartw
Tue Jun 16, 2015 12:02 am
Only if you are not actually using the DWT to do debugging of course… Not having looked carefully at this I would
wonder if it was a good idea in general purpose code because of that..

Of course I can see its value in situations where you need a low overhead accurate delay, very useful. But if its
used to replace delay(), which is often sprinkled all through code..

Of course I could be talking rubbish – does it actually interfere? Need to go read the actual code when I head is clearer
(damn meetings, the enemy of productivity!)


mrburnette
Tue Jun 16, 2015 12:26 am
stuartw wrote:Only if you are not actually using the DWT to do debugging of course… Not having looked carefully at this I would
wonder if it was a good idea in general purpose code because of that..

Rick Kimball
Tue Jun 16, 2015 1:20 am
In the code, my access is limited to the DWT_CYCCNT register. I set it once at init() time and then just read it after that. Seeing as most people aren’t using sophisticated IDE’s, I doubt it will affect anything. I know with some IDEs I’ve used they provided a cycle count feature that could be triggered from the IDE. I see a lot of other projects using this style of sub millisecond delay code to achieve more accurate timing than busy wait nop loops. So it is probably somewhat safe. I wouldn’t plop it into the code at this point. People should try it out and report any strangeness.

-rick


mrburnette
Tue Jun 16, 2015 12:21 pm
People should try it out and report any strangeness.

Well, that made me gulp my coffee!
Sounds like a CERN poster for: Quark Strangeness and Charm
https://www.youtube.com/watch?v=lFPLgGWMndc

Ray


stevech
Wed Sep 09, 2015 6:19 pm
with a 1,000 interrupts per second systick in most cases, to get reliable microsecond level accuracy, interrupts would have to be off for that duration. Agree?

Rick Kimball
Thu Sep 10, 2015 4:36 am
Running at 72MHz, you get 72000 cycles between each Systick interrupt. It seems like that would give you plenty of time to do stuff in between each run.

mrburnette
Thu Sep 10, 2015 11:54 pm
Rick Kimball wrote:
<…>
Fortunately, on the cortex-m3 they do provide a Debug Watchpoint Trace peripheral that can be hijacked and used to implement a delay_cycles function. I created a class and packaged it up as a header file here:

https://gist.github.com/RickKimball/8bc … ccf2e9[url][/url]


pe1mxp
Fri Sep 18, 2015 3:11 pm
Very handy because I need a very accurate mircosecond delay.

Leave a Reply

Your email address will not be published. Required fields are marked *