chipKIT® Development Platform

Inspired by Arduino™

Let's solve this once and for all - Generating 38kHz PWM

Created Tue, 14 Oct 2014 04:23:42 +0000 by dstark125


dstark125

Tue, 14 Oct 2014 04:23:42 +0000

Ok, so I've done ~10 hours of searching now on using IR libraries to send data between an emitter and a receiver.

I thought that the IR remote that was so talked about with Arduino would work for the Max32, but it doesn't.

I need to figure out how to send a non-blocking 38kHz PWM signal to an IR Led.

I've tried setting a timer and then manually setting a pin as described here : http://www.chipkit.net/forum/viewtopic.php?f=6&t=363&hilit=38khz&start=10

I've tried the IRremote library (obviously doesn't work as it controls Arduino registers.)

I've also looked into, but not tried, using SoftPWM to do it. I think this one should be possible. https://github.com/chipKIT32/chipKIT32-MAX/blob/master/hardware/pic32/libraries/SoftPWMServo/SoftPWMServo.h

HOW CAN I DO THIS!?!?!

Thanks in advance. If more information is needed, please, let me know. I have the Max32 and need 38kHz PWM on one pin.


majenko

Tue, 14 Oct 2014 08:51:55 +0000

98kHz or 38kHz? You change your frequency half way through :P

Ok, if you want to go the timer route you might find it simpler using my Timer library:

[url]https://github.com/majenkotech/Timer[/url]

That should allow you to toggle a pin at a suitable speed with just a handful of easy to use instructions.

Using hardware PWM should be perfectly doable, though you may have to drop the resolution to increase the frequency. What have you tried in this respect, and what has worked / not worked?

SoftwarePWM may not be fast enough to generate a signal of the right frequency. I think it runs in the low kHz maximum - maybe not even that.


dstark125

Thu, 16 Oct 2014 02:48:07 +0000

98kHz or 38kHz? You change your frequency half way through :P

38kHz, sorry, I edited it.

98kHz or 38kHz? You change your frequency half way through :P Ok, if you want to go the timer route you might find it simpler using my Timer library: [url]https://github.com/majenkotech/Timer[/url] That should allow you to toggle a pin at a suitable speed with just a handful of easy to use instructions.

So with the timer route I have to set a timer to the frequency I want, and then tie a PWM pin to that timer? Can I tie any pin or does it have to be PWM? Can you give me a general function call sequence for using your library? Such as Timer stop, set frequency, start.

Using hardware PWM should be perfectly doable, though you may have to drop the resolution to increase the frequency. What have you tried in this respect, and what has worked / not worked?

I'm not exactly sure what you mean drop the resolution to increase the frequency. As in I can't get as good of resolution on the width of the pulse? All I really care about is a 38kHz frequency with a 50% duty cycle. This is for a beam break so I don't need to send specific data, just get the detector to detect.

I actually got this working with a few of the original links I posted, but I'm wondering how good my way is and if there is a better way. This is the code that works for me:

//set up timer2 on pic32 CloseTimer2(); //Close the timer to reset it OpenTimer2(T2_ON | T2_PS_1_1, 2105); //Open the timer at the 80Mhz/2105 frequency (and default multiplier)

//Set up ocomp1 on pic32 CloseOC1(); //Close the PWM pin OpenOC1(OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); //Open the PWM pin with timer 2 as the source and 100% duty cycle SetDCOC1PWM(PR2 / 2); //Set the duty cycle of PWM pin to half of the period

What do you think, better way?

SoftwarePWM may not be fast enough to generate a signal of the right frequency. I think it runs in the low kHz maximum - maybe not even that.

Do you have a solid number for the limit on software PWM? It would be really nice to do so because I would like to be able to use non PWM pins. Is the software PWM a blocking PWM?

In order to do what I have in mind for my overall project, I'm going to need to generate this signal for as many LEDs as possible. I'm talking like 20. If I could get a good PWM through software, that would be good, but I'm not sure if that can happen. I've also thought about tying multiple LED's to one pin, but I could over current the pin at that point.

Can I hook all of the PWM pins up to one timer? So I set one timer to run at 38kHz like my code above and then I just hook all PWM pins up to it?

Sorry about the tons of questions.... I'm pretty new to all this. I want this thread to deliver a solution for all of those people googling after me.... I was not able to find a super clear answer.


majenko

Thu, 16 Oct 2014 13:04:47 +0000

There's three basic ways of doing PWM.

One is hardware PWM where you configure a timer as a timebase for the OC module to generate the signal. The timer's frequency and period define the frequency and resolution of PWM (as in how many possible values of duty cycle you can have). For fixed 50% duty cycle you don't really care about the resolution, so you can have ultimate in flexibility of speeds. You could have a 1-bit resolution if you wanted, which would be able to net you speeds in the megahertz. This is 100% non-blocking, but will only work on the PWM pins linked to OC1-OC5.

The next is timer toggling. For this you set up a timer to run at double the frequency you require, then attach an interrupt. That interrupt then either turns an IO pin on, or turns it off, in a simple on/off/on/off sequence. You can get a good clean regular square wave with this, but the speed is limited to how fast you can trigger the interrupt and toggle the pin. You can use absolutely any IO pin though.

The third is software PWM. This again can be uses on any pin, but instead of using a normal timer it uses the "core" timer which is always running and ticking away in the background. The software PWM runs as a "core timer service" in the background, but the speed at which these services are executed is generally lower than you can achieve with a true dedicated timer. There's also a certain amount of jitter at times, as other tasks execute in the same context (like the millis() counter, for instance). So the output may not be a perfect square wave all the time.

For setting up hardware PWM you need to set the right frequency, and that frequency depends on the resolution you choose. If you were to choose am 8-bit resolution (which would give you 256 possible PWM duty cycle values), then the timer would count from 0 to 255 before resetting, and the OC's duty cycle is compared to that value. So the timer has to run at 256 times your required frequency in order for it to count the full 0-255 for every output pulse. So for 38kHz you would need a timer frequency of 9.728MHz. Having a 4-bit resolution, so the timer just counts from 0 to 15 each time would mean the timer needs to run at 16 times the target frequency, so 608kHz for a 38kHz output frequency. For a 50% duty cycle you'd set the OC's compare value to 128 for the 8-bit, or 8 for the 4-bit resolution.

For pure timer toggling the frequency, as I mentioned, would be double. So for a 38kHz frequency you'd be toggling the pin twice in that time, so 76kHz. That equates to an on or off period of 1/76000 = 0.000013889s (13.889µs)

The timer library contains a blink example which does (although slowly of course) exactly what you would need for a timer pin toggling solution. You'd just need to crank up the speed to 76kHz.


dstark125

Fri, 17 Oct 2014 20:45:24 +0000

majenko,

You've totally blown my mind. I've needed a couple of days to think about this and research interrupts and timers. Thanks a ton for the great information.

I still have so many questions.... I'll try and keep it limited to a few main ones.

Can I have as many ISRs as I want?

Can I change any timer?

Do we have to clear the flag at the end of every ISR?

The next is timer toggling. For this you set up a timer to run at double the frequency you require, then attach an interrupt. That interrupt then either turns an IO pin on, or turns it off, in a simple on/off/on/off sequence. You can get a good clean regular square wave with this, but the speed is limited to how fast you can trigger the interrupt and toggle the pin. You can use absolutely any IO pin though. For pure timer toggling the frequency, as I mentioned, would be double. So for a 38kHz frequency you'd be toggling the pin twice in that time, so 76kHz. That equates to an on or off period of 1/76000 = 0.000013889s (13.889µs) The timer library contains a blink example which does (although slowly of course) exactly what you would need for a timer pin toggling solution. You'd just need to crank up the speed to 76kHz.

So the code to use your timer would look like this?

[color=#0000FF]#include <Timer.h> Timer4 timer;

const int LED = PIN_LED1; const int FREQ= 76000;

void setup() { pinMode(LED, OUTPUT); timer.setFrequency(FREQ); timer.attachInterrupt(isr); timer.start(); }

void loop() { }

void attribute((interrupt)) isr() { digitalWrite(LED, !digitalRead(LED)); clearIntFlag(_TIMER_4_IRQ); } [/color]

The way you did the led toggling in the ISR was different.... perhaps because you wanted to as few calls to digitalRead and digitalWrite as possible?

Again, thanks.


majenko

Fri, 17 Oct 2014 22:58:20 +0000

Can I have as many ISRs as I want?

One per timer.

Can I change any timer?

There's 5 timers - you can use any of them, but some may also be used by other things, like the default hardware PWM (analogWrite) and tone().

Do we have to clear the flag at the end of every ISR?

Yes, every time, so it knows the interrupt has been handled and won't trigger again until it has to.

The way you did the led toggling in the ISR was different.... perhaps because you wanted to as few calls to digitalRead and digitalWrite as possible?

That code looks reasonable, yes. I did want to keep the calls to digitalRead() and digitalWrite() to a minimum, yes, as they are quite heavyweight functions. ISRs really need to be kept as short as possible, and over-use of heavy functions can limit the speed at which you can toggle.


dstark125

Sun, 19 Oct 2014 19:24:56 +0000

Awesome! You've taught me a ton. Thank you very much.