Several approaches are possible, some of which, but far from all, have been listed in previous answers. In following, I assume that by “use it as a light barrier” you mean that the TSOP4838 will be part of an object-intrusion detection system, rather than for transmitting data. As a consequence, some jitter and/or inaccuracy is acceptable. Note, all of the following methods assume that Arduino core code has set up timer 0 to support ordinary micros() and millis() calls.
Approach 1: Compute or calibrate a simple loop: In setup()
, run a series of tests that find a value k
to minimize error, via C code equivalent to the following Python:
for k in plausibleRange():
err = genBurst19(k)
if err < bestErr:
bestErr = err
bestK = k
where genBurst19()
is like the following:
def genBurst19(k):
t1=micros()
for i in range(38):
setPortBit(i&1)
for j in range(k):
nop
t2=micros()
return abs(t2-t1-500)
In other words, genBurst19()
sends a burst of 19 cycles, which at 38 KHz should take 500 microseconds. It returns a measure of error to the caller.
After finding bestK
, you can subsequently use it as a limit for other delay loops, or could just use repeated calls to genBurst19(bestK)
whenever you want to generate some "38KHz".
Note, you might wish to try some minor variations, such as adding a second or third nop
in the inner delay loop, or generating some higher multiple of 19 cycles. While a delay loop containing a single nop
(or no nop
at all, just an empty loop) has finer resolution than a loop with multiple nop
s, it may happen (depending on loop code) that a smaller error occurs with more. [But also see Approach 3 regarding fine tuning.]
Summary: This method is easy to use, has low jitter, but is limited in the accuracy it can attain.
Approach 2: Use a method analogous to Bresenham's to produce a signal with high-accuracy period, on the average, but with more jitter than Approach 1. Bresenham's algorithm tries to draw a straight line, on an x-y grid of pixels; you are trying to draw a straight line, on a time-vs-cycles grid.
In this method, you keep track of how many microseconds total should have elapsed, and when you fall too far behind, issue a delay with k-1
instead of k
, to catch up; or if too far ahead, with k+1
, to drop back. See my answer to stackoverflow 8113883 for a little more re Bresenham's method, in the line drawing context.
Summary: By applying Bresenham's method, you can get the average frequency as close as you like (when averaged over a few milliseconds).
Approach 3: This is more of a fillip than a whole approach. It modifies Approach 1 by adding the following C code before the for j in range(k): nop
loop. It also needs a second parameter, l
.
switch (l) {
case 9: nop;
case 8: nop;
case 7: nop;
case 6: nop;
case 5: nop;
case 4: nop;
case 3: nop;
case 2: nop;
case 1: nop;
case 0: ;
}
To use this approach, first find the best k
using a series of calls like err = genBurst19(k,5)
. Then find the best l
via err = genBurst19(bestK,l)
for l
from 0 to 9. [Note, a number smaller than 9 can be used in the above, if the for j in range(k): nop
loop uses fewer cycles.]
Summary: This fillip can tune genBurst19()
closer to single-nop accuracy.