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.
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.
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.
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.
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.
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...
#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.