Micro second timing of digital pulse on UNO/328

It's been a while since I last was using an Arduino UNO. I'm trying to create a sequence of digital pulses that are not PWM pulses. There are two distinct pulses with programmable widths and a programmable delay between the two. I'm trying to get 1 microsecond if not slightly better accuracy in the pulses and the delays. I know that the micros() time tick is only accurate for 16us ticks. So I'm thinking about either using some timers or assembly to bang out the pulses.

Here is a typical sequence:
Pulse A is 10us long, then followed by a 2ms delay and pulse B that is 5us long, then another delay of 4ms, a second pulse B, another delay of 4ms and a 3rd pulse B. This sequence will repeat itself every 100ms but the 100ms accuracy is 1ms so I don't have to count large counts until the repeat.

Any advice? FYI, it's for an NMR teaching apparatus.

It's 4us.

Cool.

For microsecond timing I would use one of the hardware timers. On an UNO, Timer1 works to output PWM to pins 9 and 10. But the same waveform generator unit can be configured to give the one shot pulses like you describe instead. You can have 62.5ns resolution with that and, since it's a hardware timer and waveform generator, you don't have to worry about the timing being messed up by some other interrupt in the system.

See section 16.

https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf

The TimerOne library may also be of some help:

1 Like

NMR aparatus, as a rule, has its own powerful pulse generating software... without aim of the arduino boards :slight_smile:

Thanks. My pulsed NMR has three modules: pulse generator that generates logic level square pulses as gate signals, RF output that generates 14MHz RF signals at KiloWatt power and is gated by the pulse generator, and the detector that reads in the RF signals and outputs an envelope signal. This project only aims to recreate the pulse generator as a way to involve students in hands-on instrumentation. Hopefully we can actually replace our current pulse generator at some point.

Great! I'll check out timer1. I've used it for some millisecond pulses in the past I think. I'll see how good I can get with us pulses.

The Input Capture module of Timer1 on the Uno can be used to count and time pulses in 62.5 ns intervals, i.e. with 1/(16 MHz) resolution.

Described in this tutorial; scroll down to Input Capture.

Turning this around, one can use the code in that post, triggered by the extremely precise 1 PPS pulse of a GPS unit with a good satellite lock, to measure the actual clock frequency of the Uno to +/- 1 Hz.

Is that for real? If so it is totally illegal.

Only if it's radiating it.

I'll take this to mean you don't know much about NMR. This one actually sounds like a rather small one.

Yes it is rather small. The constant B-field is created by a pair of rare-earth magnets instead of electromagnets. The RF-field is driven by the 14MHz RF generator. It's only about a 10th of the cost of a larger device because it's only for teaching the concept.

Pulsed/CW NMR | TeachSpin

Mine is an earlier model with only about 1/3 Tesla magnets so the frequency is around 14MHz instead of 21MHz.

I wish they'd had that when I was in school. We got to study NMR spectra, but we weren't even allowed in the room while the instrument was running.

Very interesting. I never worked on CW NMR and not knowing that it uses a pulse signals. Do you mean that you can use a pulse sequences on it? The one in the first message is a like a spin echo...

Here is my snippet:

#include <TimerOne.h>
#define ABout 8
#define SYNC 9
unsigned long ABt[]={100, 250, 3750, 125, 7875, 125, 7875, 125, 79874UL};
unsigned long SYNCt[]={0,1000};
unsigned short state_ABout=LOW;
unsigned short state_SYNC=LOW;
volatile unsigned int ptr_ABt=1;
unsigned int ptr_SYNCt=1;
unsigned long repeat_us=500*1000UL;
unsigned long start_us, now_us;
unsigned long len_ABt=sizeof(ABt)/sizeof(unsigned long);
unsigned long len_SYNCt=sizeof(SYNCt)/sizeof(unsigned long);
void setup() {
  //Serial.begin(9600);
  digitalWrite(ABout,LOW);
  digitalWrite(SYNC,LOW);
  pinMode(ABout,OUTPUT);
  pinMode(SYNC,OUTPUT);
  //Timer1.initialize(500000); //The led will blink in a half second time interval
  Timer1.attachInterrupt(do_pulses);
  //Timer1.stop();
  Timer1.initialize(ABt[0]); //The led will blink in a half second time interval
  //Timer1.restart();
}

void loop() {
  start_us=micros();
  while(1)
  {
    continue;
    now_us=micros();
    if ((now_us-start_us)>=repeat_us)
    {
      noInterrupts();
      ptr_ABt=1;
      ptr_SYNCt=1;
      interrupts();
      Timer1.initialize(ABt[0]); //The led will blink in a half second time interval
      Timer1.start();
      break; // Break out of while loop to restart the pulses
    }
  }
}

void do_pulses() {
  if (ptr_ABt==0) PORTB=PINB&B11111110;
  else PORTB=PINB^B1;
  Timer1.initialize(ABt[ptr_ABt]);
  ptr_ABt++;
  if (ptr_ABt==len_ABt)
  {
    ptr_ABt=0;
  }
}

This is supposed to generate a 250us A pulse, 125us B pulse, the delay between A pulse starting and B pulse starting is 4ms. B pulse repeats 2 more times with delay of 8ms between them. The pulse repeats every 100ms. So ABBB then ABBB, with delays between A and B and subsequent B pulses.

This shows roughly the correct pulses but the pulse widths are about 40us longer than they should, i.e. A is 290us instead of 250us and B is 165us instead of 125us. I attribute this to the Timer1.initialize() calls inside the interrupt that is a maze of different processors etc. Within AVR, it looks at the timer value to determine what kind of prescalar to use. So the calculation takes about 40us. So my next iteration is to either rewrite this initialize part to make it short, or to set a fixed rate of 1us and have the irq count up a 32-bit number to compare with an array of time stamp values instead of repeatedly initialize the timer to the next delay value.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.