I maintain a library that does output of all IR signals. Normally I use hardware PWM by directly manipulating the registers and setting my output frequency. I call it PWM but actually it's just a 33% on in all instances. What I really need to be able to do is to vary the frequency from between 36 kHz and 57 kHz. It all works just fine if you have a PWM pin available and a timer available. But as the variety of Arduino platforms grows it's a bit difficult to maintain. I would like to be able to do output a specific frequency using any pin to make the code much more hardware independent.
I basically need a function such as "send_frequency(freq,count)" which would cycle and output been at the specified frequency for the specified number of cycles.
My concern is that at the speeds I'm talking about, and ordinary C++ while or for loops and delayMicroseconds might not be fast enough. I might have to write some in-line assembly code although that might defeat the goal of hardware independence.
My bottom line question is… Does anyone know of an existing library that would do this? I hate to put a lot of effort into something if someone has already solved this problem. Google searches haven't turned up anything that would work at that speed. I just thought I would check with the experts here to see if anyone else had tried this or knew of existing code before I go reinventing the wheel.
I was thinking you could do it blink without delay style - but 36 KHz is 27.8uS and 57 KHz is 17.5uS, so watching micros() pass won't really cut it.
For IR signals you have to not have some bits.
I wonder if SPI or soft-SPI could be used to drive the led?
The "tone()" function uses hardware timers. One of the things I'm trying to avoid is a conflict between the timers that I normally use and tone among other conflicts. I think what I want to do maybe cannot be done without use of the timers. But thanks for your advice anyway.
That is the library from Ken Shirriff upon which my library is based. We both use the same hardware timers. Both his library and mine work fairly well. We use hardware interrupts for receiving IR signals and hardware timers for sending IR signals. However I recently rewrote the receiving portion of the code that does not use interrupts thus making it less hardware dependent and less likely to conflict with other uses of hardware timers and interrupts. I was hoping to be able to do the same thing for the sending portion of the library to make it completely hardware timer independent and to keep it from conflicting with other uses of the timers. Here's a link to my library if anyone is interested.
http://tech.cyborg5.com/irlib/
HI Cy
The only 3 options I am aware of are:
A: using the inbuilt PWM features of the AVR which are currently being used in both libraries (IRremote & IRlib). This of course limits output on only the supported PWM pins of each MCU & as you said they are different on many models.The inbuilt PWM function always gives you the cleanest signal. (Any other method could potentially result in an occasional stretch/distortion of the modulated signal because of other interrupts. I have seen situations where this confuses some IR receivers, resulting in a corrupted demodulated signal)
B: The other option would be to configure your own timer, with an associated ISR which toggles the desired output pin (or pins). This would give the ultimate flexibility. However, for 56kHz modulation (17.8 uSec period) you would be toggling twice per period which is equal to 8.9 uSecs @ 50% duty cycle or circa 5.95uSecs @ 33% duty cycle. Maybe do-able at 16Mhz but less so at 8Mhz or 1MHz.
C: Use a standard code loop to generate your own PWM by toggling the output pin as required. To do this you need to use delayMicroseconds rather than the Timer0 (Timer0 is only 4usecs granularity), but delayMicroseconds is close to 1 uSec granularity. Even better would be to generate your own ASM loops to exactly match the timings required for each standard modulation frequency & supported duty cycles. You would also have to use direct port access for toggling. delayMicroseconds has an example of an ASM loop to get a fixed delay, based on MCU cycles. An oscilloscope or LA would help in calibrating any new code.
I have also seen discussions about using the SPI & USART clocks to generate the IR PWM as already mentioned, but I suspect you are likely to run into similar isues to the ones you raised.
So my immediate guess would be to invest your effort into making the existing approach (A) more configurable between the different timers on each of the models. Reserve Timer0 for Arduino millis() and then allow users to configure Timer1 or Timer2 etc (if available). Once the first few are implemented (in a generic manner) it should be easier adding more models to the mix via conditional "#defines". . You should also consider adding in a way to configure duty cycle from 10->50%. BTW: A lot of the Arduino Core is implemented this way (#define's).
You could also look at the Timer1 (or Timerone) library which supports PWM & ISRs etc. - but will have the same issues as mentioned above.
Finally, inbuilt PWM (A) is the most accurate approach, with no CPU overhead while generating the PWM.
Keep up the great work & thanks for making IRLib available to the community
I got a SAM3X Teensy 3.1 since this thread started and see another affordable option. The thing will overclock to 96MHz and loves to run Arduino code. My UNO cost more back when I started Arduino.
However... Teensy 3.1 needs extra careful soldering and the points are not all easy to get to, I'll be looking to pick up a Due (with more RAM, bigger than a thumbnail board with headers and stereo audio but over twice the price) for actual experimenting with.
I managed to put together a tight loop that does bit-bang PWM. It gets the frequency to within about 5% of the targeted value. I included the code as an option in the latest release of my IRLib that I released a few days ago. IRLib | CY's Tech Talk
I just scanned your new code & it looks pretty generic now...great!
The only caveat, is that when an interrupt happens during a Mark, you may(will) get a corrupted modulation pattern, which some receivers reject (=insert a short space in the middle of a mark). But probalby worth it if users need access to all the timers for other purposes. As a minimum there will be a Timer0 interrupt every ~1ms. When I get back to base next week I will check it out on the oscilloscope, along with the timings and let you know.
Well done!
I just received a USB/PC-based oscilloscope that I ordered from (persona non grata). I know it's not going to be as good as the real thing but it will help me analyze an occasional signal and take some measurements. Now I've just got to figure out how to use the thing. I don't have any experience with real oscilloscopes so this semi-simulated one is making a lot of assumptions that I know what I'm doing when in fact I don't
I'll figure it out eventually. It's going to be a fun little toy.