IR Modulation Processing Algorithm Development – Part VII

In my previous post on this subject, I discussed my decision to change from an Arduino Uno SBC to a Teensy 3.5 for implementing the ‘degenerate N-path’ digital band-pass filter (BPF) originally introduced to me by my old friend and mentor John Jenkins. After replacing the Uno with the Teensy and getting everything running (which took some doing, mostly due to my own ignorance/inability), it was time to see if the change would pay off in actual operation.

In my initial perusal of the available documentation for the Teensy 3.x SBC (have I told you lately how much I love the widespread availability of information on the inet?), I ran across some new programming features that aren’t available in the rest of the Arduino world. The Teensy 3.x supports two independent 32-bit timers, supported by two new libraries (TimerOne and TimerThree). When I first looked at this new functionality, I thought – “wow – this is just what I need to implement the sampling front-end portion of the digital BPF – I can use it with an appropriate ISR to get accurate sample timing!”. And then I ran across Paul’s ‘Delay and Timing‘ page with it’s description of the new ‘elapsedMillis’ and ‘elapsedMicros’ functions; These functions allow for accurate periodic execution of code blocks inside the normal ‘loop()’ function, without having to deal with interrupts and ISRs – cool! And then I ran across the ‘FrequencyTimer2’ library written by Jim Studt….

So now I found myself going from no real good options for accurate sample timing to a ‘veritable plethora’ of options, all of which looked pretty awesome – what’s a guy to do? Since the ‘elapsedMicros’ option looked like the simplest one to implement, I decided to try it first.

elapsedMicros:

From previous work I have a Trinket SBC transmitting an IR beam modulated by a square-wave at approximately 520Hz. The plan is to sample this waveform 20 times per cycle, and to have the sampling frequency as close as possible to 20×520 = 10.4Ksamples/sec, or approximately 96μS/sample.

I created a small test program to explore the feasibility of using the ‘elapsedMicros()’ function for IR detector sensor sampling.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

constintOUTPUT_PIN=32;//lower left pin

constintIRDET1_PIN=A0;//aka pin 14

elapsedMicros sinceLastOutput;

voidsetup()

{

Serial.begin(115200);

pinMode(OUTPUT_PIN,OUTPUT);

pinMode(IRDET1_PIN,INPUT);

digitalWrite(OUTPUT_PIN,LOW);

}

voidloop()

{

if(sinceLastOutput>95.7)// this value stabilized display

{

sinceLastOutput=0;

digitalWrite(OUTPUT_PIN,HIGH);

delayMicroseconds(10);

digitalWrite(OUTPUT_PIN,LOW);

intsamp=analogRead(IRDET1_PIN);

}

}

In the above program, I simply generate a 10μS pulse every 95.7μS. The ‘95.7’ value was empirically determined by watching the transmitted IR waveform and the 10μS pulses together on a scope, and adjusting the value until the difference between the two frequencies was as small as possible (i.e. when the movement of the transmit waveform compared to the pulse train was as slow as possible), as shown in the short video below:

In the above video, the lower trace is the generated pulse train, and the upper trace is the transmitted IR modulation waveform. The scope trigger was set to the pulse train, with the modulation waveform free to slide left or right based on the ‘beat frequency’ between the two waveforms.

Next, I added code to save ADC samples to an array for later printout. Now that I am no longer constrained by the minuscule amount of RAM available on the Uno, I opened up the array size to 2000 elements to allow more viewing time before the program was interrupted by the serial output delays. The code for this and the resulting Excel plot are shown below:

The resulting 2000 element array was dropped into Excel and plotted, as shown below:

All 2000 samples from the test program

First 40 samples. Note that 40 samples covers exactly two cycles of the modulation waveform

So, it looks like the ‘elapsedMicros()’ function is doing exactly what I want it to do – sampling the input waveform at almost exactly 20 samp/sec without me having to figure out the exact delay time needed.

The next step was to determine how much ‘free time’ is left over for other processing steps like sampling multiple sensor channels, doing the ‘sample’ and ‘cycle’ sums, etc. For this step, I removed the array loading section and replaced it with a call to ‘delayMicros()’. Then I manually adjusted the delay value until the period of pulse train started expanding away from the desired 95.7μS value. The result was that a delay value of 85μS did not change the pulse period, but a value of 90μS did (slightly). So, I have between 85 and 90μS of ‘free time’ available (out of a total of 96!!!) for other processing chores. Adding a single call to ‘analogRead(IRDET_PIN)’ reduced the available ‘free time’ by about 15μS, from between 85 & 90 to between 70 & 75μS. This shows that the time for a single analog read is about 15μS, which may be due to the same pre-scaling issue as I saw on the Uno (to be determined). In any case, even if I utilize 4 sensor channels, I should be have about 25μS left over for the summation and array load operations.

To investigate the analogRead() timing issues, I set up a small program to measure the time required to read a pin 1000 times. Here’s the code:

Serial.print(", Time required for 1000 reads was ");Serial.println(endMsec-startMsec);

}

With the above code, and with all default settings, the time required for 1000 reads was 17mSec, so about 17μS, which tracks well with the above measurements.

After changing the conversion speed to ADC_CONVERSION_SPEED::HIGH_SPEED, the time required for 1000 measurements was reduced to 11mS, so about 11μS per read.

I ran a whole series of test with the different Teensy ADC library settings, with the following results. All times are in microseconds, and are the average of 1000 iterations

conversion and sampling speed set to “HIGH”: 10.997

all adjustments commented out: 17.281

just conversion speed set to “HIGH”: 11.014

just sampling speed set to “HIGH”: 15.190

just resolution changed to 12 bits: 17.276

just resolution changed to 8 bits: 17.242

HIGH conversion and sampling speeds, and with 8-bit res: 8.931

HIGH conversion and sampling speeds, and with 12-bit res: 10.998

All of the above, plus averaging set to 1: 4.758

So, I can get the ADC time down to about 5uS/sensor, which means that even with four sensor channels being monitored, I will have over 70uSec for ‘other stuff’, which should be more than enough to get everything done.