Generating/Approximating a PPS signal from Teensy 4.1

adthoms

Member
Is it feasible to generate/approximate a PPS signal from a Teensy 4.1? More specifically, we would like to generate/approximate a PPS signal based on the Teensy's RTC clock. We understand the RTC clock will drift, and ideally, a function generator or GNSS unit should be used to generate the PPS signal, though for our application we are interested in maintaining sub-microsecond accuracy for < 1hr.
 
Last edited:
This is how to generate a PPS signal (1Hz signal) using the RTC to generate a periodic interrupt.

Code:
// this is called every 0.5s from the RTC
void callback()
{
    SNVS_HPSR |= 0b11;              // reset interrupt flag
    digitalToggleFast(LED_BUILTIN); // toggle a pin to generate the pps signal
    asm("dsb");                     // wait until flag is synced over the busses to prevent double calls of the isr
}

// sets up the RTC to generate a 2Hz interrupt.
void setupRTCInterrupt()
{
    // disable periodic interrupt
    SNVS_HPCR &= ~SNVS_HPCR_PI_EN;
    while ((SNVS_HPCR & SNVS_HPCR_PI_EN)) {} //The RTC is a slow peripheral, need to spin until PI_EN is reset...

    // set interrupt frequency to 2Hz / 0.5s
    SNVS_HPCR = SNVS_HPCR_PI_FREQ(0b1110);  // change to 0b1111 if you need a 1s interrupt

    // enable periodic interrupt
    SNVS_HPCR |= SNVS_HPCR_PI_EN;
    while (!(SNVS_HPCR & SNVS_HPCR_PI_EN)) {} // spin until PI_EN is set...

    // attach a callback
    attachInterruptVector(IRQ_SNVS_IRQ, callback);
    NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, 16); // some priority 0-255 (low number = high priority)
    NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);
}

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);
    setupRTCInterrupt();
}

void loop()
{
}

However, the RTC crystal is speced at some 20ppm (IIRC), so an accuracy of < 1µs per hour does not seem feasible (3600s x 20E-6 = 7.2ms)

See: https://github.com/TeensyUser/doc/w...ock#the-periodic-timer-of-the-real-time-clock
 
Sure, you can use this:
Code:
void loop()
{
    time_t t = rtc_get();    // get current time in seconds after 1970-01-01
    Serial.print(ctime(&t)); // pretty print the time
    delay(100);
}
 
Personally I'd look at using the main processor clock rather than the RTC clock for this.
While it may not be significantly more accurate than the RTC crystal (I couldn't find any specs on the part used) it's certainly not going to be worse.
However since it's running at a far higher speed you can tune the output speed to a greater degree and so tune out the errors in the crystal. Assuming you've got a good enough true time source to measure them in the first place that is. If you have 1us time resolution on the main clock then you should be able to get 1ppm accuracy. If you can track your sub-cycle error and don't mind a little bit of jitter in cycle lengths then you can get even better than this.

In the past I've managed to maintain ~0.1 ppm on a 120Mhz processor using this method. Until someone opened a door. Generally basic crystals will be very stable while at constant temperature so with the correct correction you can get a very good clock. But as soon as the temperature changes, and it can be as simple as a draft of air over the board, then all bets are off. This also means that you should leave things powered up for at least 10 minutes, ideally longer, before attempting to compensate for any crystal errors, it'll be constantly changing when it first powers up.
 
Using this loop() with above p#2 gives a view of RTC second versus processor cycle counts and running micros():
Code:
void loop() {
  static time_t tLast = 0;
  static uint32_t ccLast = 0;
  static uint32_t usLast = 0;
  time_t t = rtc_get();  // get current time in seconds after 1970-01-01
  if (tLast != t) {
    uint32_t cc = ARM_DWT_CYCCNT;
    uint32_t us = micros();
    Serial.print(cc - ccLast);  // show cycle count diff per RTC second
    Serial.print(" cycles\t");
    Serial.print(us - usLast);  // show micros() diff per RTC second
    Serial.print(" us  \t");
    Serial.print(ctime(&t));  // pretty print the time
    ccLast = cc;
    tLast = t;
    usLast = us;
  }
}

Each 'measured second' is some thousands short in cycles and some micros() - and the RTC results match counts versus previous connected GPS PPS for T_3.6 and T_4.x:
Code:
...
599993048 cycles	999989 us  	Mon Jun 26 10:07:34 2023
599993212 cycles	999989 us  	Mon Jun 26 10:07:35 2023
599993120 cycles	999988 us  	Mon Jun 26 10:07:36 2023
599992988 cycles	999988 us  	Mon Jun 26 10:07:37 2023
599993160 cycles	999989 us  	Mon Jun 26 10:07:38 2023
...
 
Triggering time print on RTC callback for timing - prints 2X per second - CB cycles for ref time since _ISR():
Code:
184 CB cycles	299996595 cycles	499994 us  	Mon Jun 26 10:28:37 2023
147 CB cycles	299996503 cycles	499994 us  	Mon Jun 26 10:28:37 2023
114 CB cycles	299996503 cycles	499994 us  	Mon Jun 26 10:28:38 2023
172 CB cycles	299996598 cycles	499995 us  	Mon Jun 26 10:28:38 2023
136 CB cycles	299996500 cycles	499994 us  	Mon Jun 26 10:28:39 2023
114 CB cycles	299996506 cycles	499994 us  	Mon Jun 26 10:28:39 2023

complete:
Code:
// this is called every 0.5s from the RTC
// https://forum.pjrc.com/threads/73076-Generating-Approximating-a-PPS-signal-from-Teensy-4-1
volatile uint32_t cbCnt=0;
void callback() {
  cbCnt=ARM_DWT_CYCCNT;
  SNVS_HPSR |= 0b11;               // reset interrupt flag
  digitalToggleFast(LED_BUILTIN);  // toggle a pin to generate the pps signal
  asm("dsb");                      // wait until flag is synced over the busses to prevent double calls of the isr
}

// sets up the RTC to generate a 2Hz interrupt.
void setupRTCInterrupt() {
  // disable periodic interrupt
  SNVS_HPCR &= ~SNVS_HPCR_PI_EN;
  while ((SNVS_HPCR & SNVS_HPCR_PI_EN)) {}  //The RTC is a slow peripheral, need to spin until PI_EN is reset...

  // set interrupt frequency to 2Hz / 0.5s
  SNVS_HPCR = SNVS_HPCR_PI_FREQ(0b1110);  // change to 0b1111 if you need a 1s interrupt

  // enable periodic interrupt
  SNVS_HPCR |= SNVS_HPCR_PI_EN;
  while (!(SNVS_HPCR & SNVS_HPCR_PI_EN)) {}  // spin until PI_EN is set...

  // attach a callback
  attachInterruptVector(IRQ_SNVS_IRQ, callback);
  NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, 16);  // some priority 0-255 (low number = high priority)
  NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  setupRTCInterrupt();
}

void loop() {
  static time_t tLast = 0;
  static uint32_t ccLast = 0;
  static uint32_t cbLast = 0;
  static uint32_t usLast = 0;
  if (cbLast != cbCnt) {
    uint32_t cc = ARM_DWT_CYCCNT;
    time_t t = rtc_get();  // get current time in seconds after 1970-01-01
    uint32_t us = micros();
    Serial.print(cc - cbCnt);  // show cycle count diff ref the callback
    Serial.print(" CB cycles\t");
    Serial.print(cc - ccLast);  // show cycle count diff per RTC second
    Serial.print(" cycles\t");
    Serial.print(us - usLast);  // show micros() diff per RTC second
    Serial.print(" us  \t");
    Serial.print(ctime(&t));  // pretty print the time
    ccLast = cc;
    tLast = t;
    usLast = us;
    cbLast = cbCnt;
  }
}
 
Thank you everyone for your support. Luni, I have gone ahead and integrated your changes and I am successful in generating the PPS. On the topic of getting the time of the RTC clock using:


Code:
time_t t = rtc_get();    // get current time in seconds after 1970-01-01


is there a way of getting the time in a resolution of microseconds? This is required to timestamp sensor data in `void loop() { ... }`.
 
Last edited:
Thank you everyone for your support. Luni, I have gone ahead and integrated your changes and I am successful in generating the PPS. On the topic of getting the time of the RTC clock using:
...
is there a way of getting the time in a resolution of microseconds? This is required to timestamp sensor data in main
Code:
void loop() {}[CODE].[/QUOTE]

Started to add and removed that the RTC only gives resolution to the second. There is a way to read the registers - at least shown on T_3.6: [URL="https://forum.pjrc.com/threads/53372-Microsecond-Level-Synced-Clock"]pjrc.com/threads/53372-Microsecond-Level-Synced-Clock[/URL]
> that or other - have been shown to get internal stored registers IIRC 

Other way would be to use p#8 type code to track and count the ARM_DWT_CYCCNT since the last update from the RTC and use that to assign a microsecond offset since last update. That is basically what PJRC's micros() does tracking CPU clock ticks since the last update to the millis_tick value. The micros() code used an interrupt detect in case the read is done during a millis_tick update. In this case it would reset on each time second to add to the resolution and not systick_millis_count.
from: ...\arduino-1.8.19\hardware\teensy\avr\cores\teensy4\delay.c
[CODE]
// Returns the number of microseconds since your program started running.
// This 32 bit number will roll back to zero after about 71 minutes and
// 35 seconds.  For a simpler way to build delays or timeouts, consider
// using elapsedMicros.
uint32_t micros(void)
{
	uint32_t smc, scc;
	do {
		__LDREXW(&systick_safe_read);
		smc = systick_millis_count;
		scc = systick_cycle_count;
	} while ( __STREXW(1, &systick_safe_read));
	uint32_t cyccnt = ARM_DWT_CYCCNT;
	asm volatile("" : : : "memory");
	uint32_t ccdelta = cyccnt - scc;
	uint32_t frac = ((uint64_t)ccdelta * scale_cpu_cycles_to_microseconds) >> 32;
	if (frac > 1000) frac = 1000;
	uint32_t usec = 1000*smc + frac;
	return usec;
}
 
Teensy4 has a _gettimeofday function:
Code:
extern "C" int _gettimeofday(struct timeval *tv, void *ignore __attribute__((unused)));

Pass it a struct timeval* and it will fill it with whole seconds in the tv_sec field and microseconds in the tv_usec field.
 
Indeed it does work as p#11 noted ... knew there was a reason not to say it didn't - but had not seen this.

Replace loop in p#8 with this to see seconds and usec:
Code:
extern "C" int _gettimeofday(struct timeval *tv, void *ignore __attribute__((unused)));
void loop() {
  int aV;
  static uint32_t ccLast = 0;
  static uint32_t cbLast = 0;
  static uint32_t usLast = 0;
  if (cbLast != cbCnt) {
    struct timeval tv;
    uint32_t cc = ARM_DWT_CYCCNT;
    time_t t = rtc_get();  // get current time in seconds after 1970-01-01
    uint32_t us = micros();
    Serial.print(cc - cbCnt);  // show cycle count diff ref the callback
    Serial.print(" CB cycles\t");
    Serial.print(cc - ccLast);  // show cycle count diff per RTC second
    Serial.print(" cycles\t");
    Serial.print(us - usLast);  // show micros() diff per RTC second
    Serial.print(" us  \t");
    Serial.print(ctime(&t));  // pretty print the time
[B]    _gettimeofday( &tv, &aV );
    Serial.print("\t");
    Serial.print(tv.tv_sec%60 );
    Serial.print('.');
    Serial.println(tv.tv_usec );
[/B]    ccLast = cc;
    usLast = us;
    cbLast = cbCnt;
  }
}

Code:
151 CB cycles	299998872 cycles	499998 us  	Tue Jun 27 16:38:28 2023
[B]	28.0[/B]
132 CB cycles	299998829 cycles	499998 us  	Tue Jun 27 16:38:28 2023
[B]	28.500000[/B]
147 CB cycles	299998859 cycles	499998 us  	Tue Jun 27 16:38:29 2023
	29.0
129 CB cycles	299998830 cycles	499998 us  	Tue Jun 27 16:38:29 2023
	29.500000
134 CB cycles	299998857 cycles	499998 us  	Tue Jun 27 16:38:30 2023
	30.0
 
Thank you defragster and jmarsh, I will take a look at both of your solutions. jmarsh - will _gettimeofday give me microseconds with respect to the RTC clock? defragster - to give you an idea of my application, I would like to output the PPS signal (and an accompanying NMEA message that is also simulated by the Teensy) as follows:

Code:
RTC Time: 1687909771.000000
NMEA string: $GPRMC,234931,A,4365.107,N,79347.702,E,022.4,084.4,270623,003.1,W*72

RTC Time: 1687909772.000000
NMEA string: $GPRMC,234932,A,4365.107,N,79347.702,E,022.4,084.4,270623,003.1,W*71

RTC Time: 1687909773.000000
NMEA string: $GPRMC,234933,A,4365.107,N,79347.702,E,022.4,084.4,270623,003.1,W*73

etc.

for ease of use, I will use elapsedMicros and reset its counter each time the PPS is generated.
 
Thank you defragster and jmarsh, I will take a look at both of your solutions. jmarsh - will _gettimeofday give me microseconds with respect to the RTC clock? defragster - to give you an idea of my application, I would like to output the PPS signal (and an accompanying NMEA message that is also simulated by the Teensy) as follows:

...
for ease of use, I will use elapsedMicros and reset its counter each time the PPS is generated.

p#12 shows the usec is tied to the value returned by _gettimeofday. The _ISR is twice each second and the added prints show they are coming regularly at 0.0 and 0.5 usec respectively to the matching current second some -.25 usec after the _ISR was triggered.

But just using the elapsedMicros would be close and give an properly increasing offset to the last second read - just note the two calls for HIGH then LOW per second will need to be accounted for.

Interesting use to emulate the NMEA output.
 
jmarsh - will _gettimeofday give me microseconds with respect to the RTC clock?

Yes, the tv_sec and tv_usec values are from the same source. The actual rate of the RTC seems to be 32768Hz so it ticks roughly 30.5 microseconds at time.
 
Yes, the tv_sec and tv_usec values are from the same source. The actual rate of the RTC seems to be 32768Hz so it ticks roughly 30.5 microseconds at time.

That's why the micros() code p#10 was created using the ARM_DWT_CYCCNT for offset, as a slower clock gives the millis_tick and micros() resolution was worse than 10+ usec.

So better use would be the elapsedMicros for offset.
 
Thank you everyone for your support. Luni, I have gone ahead and integrated your changes and I am successful in generating the PPS. On the topic of getting the time of the RTC clock using:
... is there a way of getting the time in a resolution of microseconds? This is required to timestamp sensor data in `void loop() { ... }`.

In case you like to fiddle with the more modern aspects of C++, you might like my HiResClock library (https://github.com/luni64/HiResClock). It implements a std::chrono compliant c++ clock (see e.g. here https://en.cppreference.com/w/cpp/chrono) which uses F_CPU (600MHz) as time base. Thus it has a resolution of 1.666ns and measures time in nanoseconds since 1970-01-01. By default it syncs to the RTC at start. Here an example how you generate a RTC synced ns timestamp with this clock:

Code:
#include "HiResClock.h"

using namespace std::chrono;

void setup()
{
    HiResClock::begin(); // start the clock and sync to rtc (works with and without battery)
}

void loop()
{
    // long form:
    auto now       = HiResClock::now();               // get current timepoint
    auto dur       = now.time_since_epoch();          // calculate the chrono::duration since 1970-01-01 (in some system units, here 1/600Mhz)
    auto dur_in_ns = duration_cast<nanoseconds>(dur); // cast to a chrono::duration in units of nanoseconds (or milliseconds, or hours...)
    uint64_t ns    = dur_in_ns.count();               // read out the actual nanoseconds from the duration

    // short form:
    // uint64_t ns = duration_cast<nanoseconds>(HiResClock::now().time_since_epoch()).count();

    Serial.print(ns / 1E9, 9);      // print the timestamp in the desired form (e.g. 1687969637.753705324)

    //--- quick check ---
    Serial.print(" - ");
    time_t s = (time_t)(ns / 1E9); // transform to time_t...
    Serial.print(ctime(&s));       // and pretty print corresponding time

    delay(250);                    // repeat every 250ms
}

Prints:
Code:
1687981600.000000000 - Wed Jun 28 19:46:40 2023
1687981600.250008344 - Wed Jun 28 19:46:40 2023
1687981600.500017404 - Wed Jun 28 19:46:40 2023
1687981600.750026226 - Wed Jun 28 19:46:40 2023
1687981601.000035285 - Wed Jun 28 19:46:41 2023
1687981601.250044345 - Wed Jun 28 19:46:41 2023
1687981601.500053167 - Wed Jun 28 19:46:41 2023
1687981601.750062227 - Wed Jun 28 19:46:41 2023
1687981602.000071287 - Wed Jun 28 19:46:42 2023
1687981602.250080585 - Wed Jun 28 19:46:42 2023
1687981602.500089406 - Wed Jun 28 19:46:42 2023
1687981602.750098228 - Wed Jun 28 19:46:42 2023
1687981603.000107288 - Wed Jun 28 19:46:43 2023
1687981603.250116348 - Wed Jun 28 19:46:43 2023
1687981603.500125408 - Wed Jun 28 19:46:43 2023
1687981603.750134229 - Wed Jun 28 19:46:43 2023

The integer part of the calculated values corresponds to a normal time_t value. Resolution is 1.66ns. There are a few more examples how to use it in the lib repository and here https://github.com/TeensyUser/doc/wiki/Durations-Timepoints-and-Clocks and here: https://github.com/TeensyUser/doc/wiki/Implementing-a-high-resolution-Teensy-clock
 
Oh, silly me! Didn't notice that the repo was private. Changed it to public, should work now.
 
Oh, silly me! Didn't notice that the repo was private. Changed it to public, should work now.

Thanks, luni, that works now. Wow, there really is a lot of new C++ stuff in the middle layer (StdCppClock.h). It's not many lines of code, but my lack of familiarity with templates, and with "using" and "typename" makes it hard to follow what even the very short functions are doing. Is the purpose of the 1-second interrupt simply to be sure that getTicks() is called often enough that every roll-over of ARM_DWT_CYCCNT is captured? And you disable interrupts in getTicks() to prevent a call from non-interrupt level from being corrupted by a call from the one-second-timer ISR?
 
TeensyDuino micros() does p#10 read only access to millis update values so it can repeat with this do{}while(); to make the two reads 'atomic' if any interrupt occurs:
Code:
	do {
		__LDREXW(&systick_safe_read);
		smc = systick_millis_count;
		scc = systick_cycle_count;
	} while ( __STREXW(1, &systick_safe_read));

Indeed that code in "static tickType getTicks()" could be interrupted any time 'static time_point now()' is called. Since it is doing a write update on the two Hi/Lo values to get a third 64 bit value, it seems stopping interrupts is the only way to assure it isn't read when the values are half updated and uncombined?

Indeed 'new' C++ code across three files is fun reading - even with github's new file click interface panes. Even with all three in one editor to search questions the compiler answers are also 'fun' to trace - like how does "t0" factor in?
 
Indeed that code in "static tickType getTicks()" could be interrupted any time 'static time_point now()' is called. Since it is doing a write update on the two Hi/Lo values to get a third 64 bit value, it seems stopping interrupts is the only way to assure it isn't read when the values are half updated and uncombined?

Yes, and that makes me wonder if it would be worth having a bool argument to begin() to control whether the 1-sec interrupt is enabled. The default could be true, but if you have an application where you can guarantee that getTicks() will be called often enough to never miss a roll-over of ARM_DWT_CYCCNT, then you might prefer not to have to disable interrupts on every call to now(). I suppose it's just simpler to know for sure that under no circumstances could a roll-over be missed.

Luni's comments show that the implementation is in-line, and only a couple of lines of code, but it's hard for me to see how much the C++ code boils down to something that is "light-weight" at run-time. I know there's a way to look at the ASM of a given file, but I've not yet done that for Teensy. What I do now is simply read ARM_DWT_CYCCNT and compute elapsed time in F_CPU counts, then convert to us or ns via macros. I don't think I want to replace those reads with calls to a function that disables interrupts, even if ever-so-briefly.
 
Yes, and that makes me wonder if it would be worth having a bool argument to begin() to control whether the 1-sec interrupt is enabled. ... I don't think I want to replace those reads with calls to a function that disables interrupts, even if ever-so-briefly.

The other annoying possibility is caller() has interrupts OFF and it calls ::now() and returns with interrupts ON.

T_3.x micros() does that Interrupts{Off ... On} - luckily the support for p#10 worked out without needing to do that - where the T_3.x code needs to read clock count registers for offset calc.
 
The other annoying possibility is caller() has interrupts OFF and it calls ::now() and returns with interrupts ON.
The noInterrupts()/Interrupts() bracket was kind of sloppy indeed. I changed it to the LDREX/STREX protection pattern (v0.1.2) as @defragster proposed.

What I do now is simply read ARM_DWT_CYCCNT and compute elapsed time in F_CPU counts, then convert to us or ns via macros.
. Sure, but this only works for dt < 7.2 seconds. If you need to track longer times you need to somehow detect overflows which is what the code in question does.

I don't think I want to replace those reads with calls to a function that disables interrupts, even if ever-so-briefly.
Agreed and fixed. Anyway, disabled interrupts are not that unusual. This is what a search for __disable_irq() on the Teensy4 core returns.

Code:
cores\teensy4\AudioStream.cpp:
   63  	if (num > maxnum) num = maxnum;
   64: 	__disable_irq();
   65  	memory_pool = data;

   99  	end = p + NUM_MASKS;
  100: 	__disable_irq();
  101  	index = memory_pool_first_mask;

  139  
  140: 	__disable_irq();
  141  	if (block->ref_count > 1) {

  258  			
  259: 		__disable_irq();
  260  		

  356  	if (dest_index >= dst->num_inputs) return 2; // should never happen!
  357: 	__disable_irq();
  358  	

  387  		// release() re-enables the IRQ. Need it to be disabled a little longer
  388: 		__disable_irq();
  389  		dst->inputQueue[dest_index] = NULL;

cores\teensy4\avr_emulation.h:
  42  static inline void GPIO_SETBIT_ATOMIC(volatile uint32_t *reg, uint32_t mask) {
  43: 	__disable_irq();
  44  	*reg |= mask;

  48  static inline void GPIO_CLRBIT_ATOMIC(volatile uint32_t *reg, uint32_t mask) {
  49: 	__disable_irq();
  50  	*reg &= ~mask;

cores\teensy4\delay.c:
  103  
  104: 	__disable_irq();
  105  	tick = SYST_CVR;

cores\teensy4\DMAChannel.cpp:
  48  
  49: 	__disable_irq();
  50  	if (!force_initialization && TCD && channel < DMA_MAX_CHANNELS

  94  	DMA_CERQ = channel;
  95: 	__disable_irq();
  96  	dma_channel_allocated_mask &= ~(1 << channel);

cores\teensy4\eeprom.c:
  248  {
  249: 	__disable_irq();
  250  	FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;

  288  {
  289: 	__disable_irq();
  290  	FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;

  312  {
  313: 	__disable_irq();
  314  	FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;

  336  {
  337: 	__disable_irq();
  338  	FLEXSPI_LUTKEY = FLEXSPI_LUTKEY_VALUE;

cores\teensy4\EventResponder.h:
  230  		__asm__ volatile("mrs %0, primask\n" : "=r" (primask)::);
  231: 		__disable_irq();
  232  		return (primask == 0) ? true : false;

  269  		__asm__ volatile("mrs %0, primask\n" : "=r" (primask)::);
  270: 		__disable_irq();
  271  		return (primask == 0) ? true : false;

cores\teensy4\HardwareSerial.cpp:
  432  	// WATER> 0 so IDLE involved may want to check if port has already has RX data to retrieve
  433: 	__disable_irq();
  434  	head = rx_buffer_head_;

  479  	if (head == tail) {
  480: 		__disable_irq();
  481  		head = rx_buffer_head_;  // reread head to make sure no ISR happened

  514  	if (head == tail) {
  515: 		__disable_irq();
  516  		head = rx_buffer_head_;  // reread head to make sure no ISR happened

  563  	if(half_duplex_mode_) {		
  564: 		__disable_irq();
  565  	    port->CTRL |= LPUART_CTRL_TXDIR;

  597  	}
  598: 	__disable_irq();
  599  	transmitting_ = 1;

  684  		if(half_duplex_mode_) {		
  685: 			__disable_irq();
  686  		    port->CTRL &= ~LPUART_CTRL_TXDIR;

cores\teensy4\imxrt.h:
  9786  
  9787: #define __disable_irq() __asm__ volatile("CPSID i":::"memory");
  9788  #define __enable_irq()  __asm__ volatile("CPSIE i":::"memory");

cores\teensy4\startup.c:
  583  	// disallow any nested interrupts
  584: 	__disable_irq();
  585  	// store crash report info

cores\teensy4\Tone.cpp:
   70  	// the interrupt on a single timer.
   71: 	__disable_irq();
   72  	if (pin == tone_pin) {

  132  	if (pin >= CORE_NUM_DIGITAL) return;
  133: 	__disable_irq();
  134  	if (pin == tone_pin) {

cores\teensy4\usb_audio.cpp:
  238  
  239: 	__disable_irq();
  240  	left = ready_left;

  360  	}
  361: 	__disable_irq();
  362  	if (left_1st == NULL) {

cores\teensy4\usb_touch.c:
  73  	if (y > 32767) y = 32767;
  74: 	__disable_irq();
  75  	if (pressure[finger] == 0 && press > 0) {

cores\teensy4\usb.c:
   215  	} else {
   216: 		__disable_irq(); // secure mode NXP ROM reboot
   217  		USB1_USBCMD = 0;

   373  {
   374: 	__disable_irq();
   375  	sof_usage |= (1 << interface);

   955  	}
   956: 	__disable_irq();
   957  	//digitalWriteFast(1, HIGH);

  1092  	while (1) {
  1093: 		__disable_irq();
  1094  		USB1_USBCMD = cmd | USB_USBCMD_ATDTW;

cores\teensy4\wiring.h:
  198  #define sei() __enable_irq()
  199: #define cli() __disable_irq()
  200  #define interrupts() __enable_irq()
  201: #define noInterrupts() __disable_irq()
  202
 
The noInterrupts()/Interrupts() bracket was kind of sloppy indeed. I changed it to the LDREX/STREX protection pattern (v0.1.2) as @defragster proposed.

...

Used that in writing the micros() code - but it only reads 1 or more times as interrupts occur until it has an 'atomic copy' of both values in sync.

In this case what happens if on an interrupt, within that do{}while; block, the time is gotten? Do those partially updated and changing values impact the time returned?

Assumed they would as posted - but perhaps the 'new C++' code doesn't flow that way?
 
Back
Top