Pages: 1 2 [3] 4 5   Go Down
Author Topic: How to create a 38 Khz pulse with arduino using timer or PWM?  (Read 14932 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes you could do that. But this does it with only one pin:

Code:
const byte LED = 9;  // Timer 1 "A" output: OC1A

ISR (TIMER2_COMPA_vect)
{
   TCCR1A ^= _BV (COM1A0) ;  // Toggle OC1A on Compare Match
  
   if ((TCCR1A & _BV (COM1A0)) == 0)
     digitalWrite (LED, LOW);  // ensure off
    
}  // end of TIMER2_COMPA_vect

void setup() {
  pinMode (LED, OUTPUT);
  
  // set up Timer 1 - gives us 38.095 MHz (correction: 38.095 KHz)
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV (CS10);   // CTC, No prescaler
  OCR1A =  209;          // compare A register value (210 * clock speed)
                         //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095
  
  // Timer 2 - gives us our 1 mS counting interval
  // 16 MHz clock (62.5 nS per tick) - prescaled by 128
  //  counter increments every 8 uS.
  // So we count 125 of them, giving exactly 1000 uS (1 mS)
  TCCR2A = _BV (WGM21) ;   // CTC mode
  OCR2A  = 124;            // count up to 125  (zero relative!!!!)
  TIMSK2 = _BV (OCIE2A);   // enable Timer2 Interrupt
  TCCR2B =  _BV (CS20) | _BV (CS22) ;  // prescaler of 128

}  // end of setup

void loop()
  {
  // all done by interrupts
  }

Output (pin D9 this time):



There is your 38 KHz signal pulsed at 1 KHz.
« Last Edit: April 26, 2012, 02:08:51 am by Nick Gammon » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Can't thank you enough mate.

Just one question, Since Timer 2 is assigned pins 9 & 10, I won't use them for other purposes as a precaution while using this code. Any pther precaution?

Again, thanks a lot mate.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 238
Posts: 24322
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
// set up Timer 1 - gives us 38.095 MHz
!
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah yes, I just put that there to make sure everyone was paying attention. smiley

That should read 38.095 KHz.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Come to think of it, it would be surprising to get a 38 MHz pulse out of a 16 MHz processor.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Just one question, Since Timer 2 is assigned pins 9 & 10, I won't use them for other purposes as a precaution while using this code. Any pther precaution?

The latest version, above, only uses pin 9. Feel free to use the other pins for whatever you want. Other precautions? Avoid bad dates.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 36
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Just one question, Since Timer 2 is assigned pins 9 & 10, I won't use them for other purposes as a precaution while using this code. Any pther precaution?

Other precautions? Avoid bad dates.
smiley-grin
« Last Edit: April 26, 2012, 02:56:13 am by praky » Logged

Pottstown, PA
Offline Offline
Sr. Member
****
Karma: 4
Posts: 297
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Nick. The project I am working on is currently using the TLC5940 library and the IRremote library. The two both utilize pin 2 on the Uno. Within the IRremote library I found a place to designate the IR output pin to pin 9.

// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc
#else
  #define IR_USE_TIMER1   // tx = pin 9 //uncommented for Tlc5940 project
  //#define IR_USE_TIMER2     // tx = pin 3 //commented for Tlc5940 project

However, the TLC5940 library also uses pin 9.

I've been poking around the net for a couple of days now seeking a solution and just recently came across your post using this code:

Code:
const byte LED = 10;  // Timer 2 "A" output: OC2A

void setup() {
  pinMode (LED, OUTPUT);
 
  // set up Timer 2
  TCCR2A = _BV (COM2A0) | _BV(WGM21);  // CTC, toggle OC2A on Compare Match
  TCCR2B = _BV (CS20);   // No prescaler
  OCR2A =  209;          // compare A register value (210 * clock speed)
                         //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095

}  // end of setup

void loop() { }

Could this code be adapted for use on a Uno and not use pins 3 or 9? - Scotty
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

As described in here: http://www.gammon.com.au/forum/?id=11504

The timer pins are:

Code:
Timer 0

input     T0     pin  6  (D4)

output    OC0A   pin 12  (D6)
output    OC0B   pin 11  (D5)

Timer 1

input     T1     pin 11  (D5)

output    OC1A   pin 15  (D9)
output    OC1B   pin 16  (D10)

Timer 2

output    OC2A   pin 17  (D11)
output    OC2B   pin  5  (D3)
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello Everybody
I have a little issue, and I just can't get it worked out. I already spent a good amount of time on it but I'm still super new to the Arduino board.

I would like to pulse width modulate a 38kHz carrier signal at 500hz, and adjust the duty cycle of the 500hz signal by reading a value from a potentiometer (value between 256 and 0 (100% to 0% duty cycle)).

So far, I came up with the code bellow, which does the following: It uses the tone() command to output a 38kHz signal on pin 11 and uses the delayMicroseconds() command to modulate it at 500hz. By
adjusting the delays for the on/off period of the 38kHz signal (by using a value I get from the Potentiometer), I can adjust the duty cycle of the 500hz modulated signal. (In the example below, I have 2048 increments by which I can adjust the duty cycle, but I would only need 256.)  

Code:
void loop () {
  
Bval = analogRead(PodPin) * 2;
//(Read Potentiometer, then multiplay value by x2 and store it in "Bval" (Value will be between 0 and 2048)

tone(11,38000);
//create 38kHz signal on pin 11)

delayMicroseconds(Bval);
//delay for "Bval" microseconds

noTone(11);
//turn off 38kHz signal on pin 11

delayMicroseconds(2048 - Bval);
//delay for "2048 - Bval" microseconds.

}

For some reason, my code doesn't work very reliable. My guess would be that everything is extremely "time sensitive" and by doing it with "delays" it will never be very accurate. I think it should be possible to do it much more reliably with timers but I just can't get it to work (I tried to play with code posted above by Nick Gammon, but didn't have much luck yet). Any chance somebody could help me on this? I would greatly appreciate any input...

PS: I have an Arduino Uno
« Last Edit: September 23, 2012, 09:34:44 pm by cordvision » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
// Example of modulating a 38 KHz frequency duty cycle by reading a potentiometer
// Author: Nick Gammon
// Date: 24 September 2012

const byte POTENTIOMETER = A0;
const byte LED = 10;  // Timer 1 "B" output: OC1B

// 16 MHz clock divided by 38 KHz frequency desired
const long timer1_OCR1A_Setting = 16000000L / 38000L;

void setup()
 {
  pinMode (LED, OUTPUT);

  // set up Timer 1 - gives us 38.005 KHz
  // Fast PWM top at OCR1A
  TCCR1A = _BV (WGM10) | _BV (WGM11) | _BV (COM1B1); // fast PWM, clear OC1B on compare
  TCCR1B = _BV (WGM12) | _BV (WGM13) | _BV (CS10);   // fast PWM, no prescaler
  OCR1A =  timer1_OCR1A_Setting - 1;                 // zero relative  
  }  // end of setup

void loop()
  {
  // alter Timer 1 duty cycle in accordance with pot reading
  OCR1B = (((long) (analogRead (POTENTIOMETER) + 1) * timer1_OCR1A_Setting) / 1024L) - 1;
  
  // do other stuff here
  }

That uses the hardware timer to output 38 KHz and modulates the duty cycle in hardware based on a pot reading.

Example:



Note the frequency is correct and in this particular case I have a 27% duty cycle.
« Last Edit: September 24, 2012, 12:16:20 am by Nick Gammon » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My code above is a bit dodgy at the edge conditions (pot at zero or 1023). They should really be detected and translated into some other output. After all, modulating with 100% duty cycle (or 0%) doesn't make any sense. That's because 100% on or 0% on isn't actually modulating at all.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I would like to pulse width modulate a 38kHz carrier signal at 500hz, and adjust the duty cycle of the 500hz signal by reading a value from a potentiometer (value between 256 and 0 (100% to 0% duty cycle)).

Looks like I answered the wrong question. Give me a bit of time to do the correct answer ...
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I got it this time. You want a standard 38 KHz pulse, which itself turns on and off at 500 Hz with a duty cycle controlled by a pot? This seems to do it:

Code:
// Example of modulating a 38 KHz carrier frequency at 500 Hz with a variable duty cycle
// Author: Nick Gammon
// Date: 24 September 2012


const byte POTENTIOMETER = A0;
const byte LED = 9;  // Timer 1 "A" output: OC1A

// 16 MHz clock divided by 500 Hz frequency desired (allowing for prescaler of 128)
const long timer2_OCR2A_Setting = 16000000L / 500L / 128L;

ISR (PCINT2_vect)
   {
   
   // if pin 3 now high, turn on toggling of OC1A on compare
   if (PIND & _BV (3))
     {
     TCCR1A |= _BV (COM1A0) ;  // Toggle OC1A on Compare Match
     }
   else
     {
     TCCR1A &= ~_BV (COM1A0) ;  // DO NOT Toggle OC1A on Compare Match
     digitalWrite (LED, LOW);  // ensure off
     }  // end of if
     
   }  // end of PCINT2_vect


void setup() {
  pinMode (LED, OUTPUT);
  pinMode (3, OUTPUT);  // OC2B
 
  // set up Timer 1 - gives us 38.095 KHz
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV (CS10);   // CTC, No prescaler
  OCR1A =  (16000000L / 38000L / 2) - 1;  // zero relative
 
  // Timer 2 - gives us our 1 mS counting interval
  // 16 MHz clock (62.5 nS per tick) - prescaled by 128
  //  counter increments every 8 uS.
  // So we count 250 of them, giving exactly 2000 uS (2 mS period = 500 Hz frequency)
  TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1);   // Fast PWM mode
  TCCR2B = _BV (WGM22) | _BV (CS20) | _BV (CS22) ;  // prescaler of 128
  OCR2A  = timer2_OCR2A_Setting - 1;                // count up to 250  (zero relative!!!!)
 
  // pin change interrupt
  PCMSK2 |= _BV (PCINT19);  // want pin 3
  PCIFR  |= _BV (PCIF2);    // clear any outstanding interrupts
  PCICR  |= _BV (PCIE2);    // enable pin change interrupts for D0 to D7
 
}  // end of setup

void loop()
  {
  // alter Timer 2 duty cycle in accordance with pot reading
  OCR2B = (((long) (analogRead (POTENTIOMETER) + 1) * timer2_OCR2A_Setting) / 1024L) - 1;

  // other stuff here
  }  // end of loop

Results:



I am outputting 38 Khz on pin 9 (output of timer 1 "A" side) and turning that on and off with a pin-change interrupt. The interrupt is generated by timer 2 "B" side which is the output of a fast PWM 500 Hz timer where the duty cycle is variable by the potentiometer input.

You can see from the image when pin 3 (timer 2) goes on and off (in this case with a 24.8% duty cycle) and that the 38 KHz signal on pin 9 is switched on and off corresponding to that.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 19
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That's awesome, I can't thank you enough. Looking at what it took, I don't think I would have been able to write this myself anytime soon. If I can ever return a favor, let me know (I don't have any programing knowledge but a lot of video/photography knowledge). Thank you so so much again... I really appreciate it smiley
Logged

Pages: 1 2 [3] 4 5   Go Up
Jump to: