Changing timer frequency on the fly

Hi All...

I have this application where I need to generate a pulse stream. The frequency of the pulse stream is directly proportional to an analog sensor input. I have this (sort of) working without timers, but I think it would be far better using timer interrupts. The issue at hand is that I don't think the timer period on either timer1 or timer2 can be changed on the fly. I haven't tried stopping and restarting or re-initializing the timer, but I'm guessing that the overhead in time would make that impractical to do hundreds of times per second.

The way I was able to get OK results was to create a function that does the analog read, attach it to a timer and set the timer to 100ms. I don't need to read the sensors on every scan and reducing the number of times the analogRead() is executed greatly improved performance.

Does anyone have any ideas on how to update the timer period without stopping the timer. I'm OK with using either timer1 or timer2. I've been using the high level timer libraries Timer1 and MsTimer2.

Thanks very much.

You don't nescessarily need to change the timer frequency. If you set the timer frequency so that it provides your needed resolution, you can vary your output frequency in discrete steps that match that resolution.

For instance if you have a timer frequency of 1000Hz (or whatever) than you can vary your output frequency in 2 millisecond steps.

Does anyone have any ideas on how to update the timer period without stopping the timer.

Yes just bang a value in the appropriate register. This code generates a signal on pin 3 using timer 2.

/* Code to pulse pin 3 with a modulated signal
* Can be used to drive an IR LED to keep a TSOP IR reciever happy
* This allows you to use a modulated reciever and a continious beam detector
* By Mike Cook Nov 2011 - Released under the Open Source licence
*/
 volatile byte pulse = 0;

ISR(TIMER2_COMPB_vect){  // Interrupt service routine to pulse the modulated pin 3
    pulse++;
  if(pulse >= 8) { // change number for number of modulation cycles in a pulse
    pulse =0;
    TCCR2A ^= _BV(COM2B1); // toggle pin 3 enable, turning the pin on and off
  }
}

void setIrModOutput(){  // sets pin 3 going at the IR modulation rate
  pinMode(3, OUTPUT);
  TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Just enable output on Pin 3 and disable it on Pin 11
  TCCR2B = _BV(WGM22) | _BV(CS22);
  OCR2A = 51; // defines the frequency 51 = 38.4 KHz, 54 = 36.2 KHz, 58 = 34 KHz, 62 = 32 KHz
  OCR2B = 26;  // deines the duty cycle - Half the OCR2A value for 50%
  TCCR2B = TCCR2B & 0b00111000 | 0x2; // select a prescale value of 8:1 of the system clock
}

void setup(){
  setIrModOutput();
  TIMSK2 = _BV(OCIE2B); // Output Compare Match B Interrupt Enable
}

void loop(){
// do something here
}

If you change the registers:-

OCR2A = 51; // defines the frequency 51 = 38.4 KHz, 54 = 36.2 KHz, 58 = 34 KHz, 62 = 32 KHz
OCR2B = 26;  // deines the duty cycle - Half the OCR2A value for 50%

On the fly it will change the frequency of the output stream.

Thank Guys...I've been doing this sort of thing for a while but haven't gone past the C IDE into the lower level programming.

Looks like the key line in this code to update the frequency is...

OCR2A = 51; // defines the frequency 51 = 38.4 KHz, 54 = 36.2 KHz, 58 = 34 KHz, 62 = 32 KHz

I'm guessing those values are in HEX.

Is there any reason I couldn't set up and start the timer using C code and then just update the frequency using this line of lower level code once the timer is running?

I'm guessing those values are in HEX.

No hex values start with a 0x those values are in decimal.

Is there any reason I couldn't set up and start the timer using C code and then just update the frequency using this line of lower level code once the timer is running?

I thought that is what I said. It is all in C code, no assembler. Change both values if you want to keep it a square wave.

Cool, thanks. What I meant by "low level was not using the timer libraries. While not assembler, it's lower level than using the libraries. :)

It's not exactly a square wave. There are 4us pulses separated by a time interval. I already have the function that generates the 4us pulse. The plan was to just put that in an interrupt and change the overflow period of the timer. I'm guessing that in this case, duty cycle should be kept short and at one setting while the frequency will be changed.

to be honest, I'm a little confused as to why duty cycle and frequency come into play on a timer. I would expect there to be just one period setting. When the time hits that set period, the interrupt is triggered. The only thing that comes to mind is that the duty cycle sets how long the timer is at it's "high" point. You could then attach the timer to a pin and while the timer is at it's high state, so would be the pin.

Am I on the right track? First time venturing outside of the Arduino libraries, so thanks again for the help. I'll give all of this a try tonight.

to be honest, I'm a little confused as to why duty cycle and frequency come into play on a timer.

The two registers of the timer determine the duty cycle and the frequency. OCR2A controls how long the cycle is. OCR2B controls how long along that cycle the bit flops from low to high. So for a square wave OCR2B must be half the value of OCR2A. If OCR2B is bigger than OCR2A then it never flips over and you don't get any output. If OCR2B is a value of 1 you always get the same width pulse out at a rate controlled by OCR2A.

When the time hits that set period, the interrupt is triggered

While that code triggers interrupts there is no need to, the output pin toggles directly under the timer control. You can remove that line that enables the interrupts if you like.

Doing a PWM output you are simply writing to the OCR2B register on the fly. To change the frequency as well you need to use the OCR2A in conjunction with the OCR2B.

Could you take a look at my comments on each line of code. I read through the data sheet on Timer1 and I think I've got it figured out. I modified the code to work with Timer1.

One question...Is the number used to set the frequency and duty cycle 8 bit (0-255)? I need to map that to the analog input of 1023 and I'm not sure of the range.

void setFrequency(){ pinMode(9, OUTPUT);

//Set pin 9 to output

TCCR2A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11) | _BV(WGM10);

// Sets pin to 9, disables 10, Combined with following line, sets mode to fast PWM, 10 bit. // Something tells me that I'll need to be in normal mode.

TCCR2B = _BV(WGM12) | _BV(CS12);

//Sets presaler to 256

OCR2A = 51; // defines the frequency 51 = 38.4 KHz, 54 = 36.2 KHz, 58 = 34 KHz, 62 = 32 KHz OCR2B = 26; // deines the duty cycle - Half the OCR2A value for 50%

// Sets frequency and duty cycle

TCCR2B = TCCR2B & 0b00111000 | 0x2; // select a prescale value of 8:1 of the system clock

//Looks like this line also sets TCCR2B, just in a different way.

rrc1962:
I modified the code to work with Timer1.

  TCCR2A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11) | _BV(WGM10);

No you didn’t, or that would be TCCR1A.

rrc1962: One question...Is the number used to set the frequency and duty cycle 8 bit (0-255)? I need to map that to the analog input of 1023 and I'm not sure of the range.

Timer 1 is a 16-bit timer, if that answers your question.

Is the number used to set the frequency and duty cycle 8 bit (0-255)? I

All registers are 8 bits so if you are addressing a 16 bit register you have to do it in two bytes.

I need to map that to the analog input of 1023

Why? I think it is time to ask exactly what you are doing.

Timer 1 works differently from Timer 2, so I would need to look at that.

Have you implemented this with Timer 2 and found you haven't got sufficient resolution? Or is it just that the A/D returns a 10 bit value?

I’m sending pulses to a step motor driver. The frequency is based on the difference between an analog sensor input and a reference. The pulse width needs to be 4us ± 1us. The time between pulses varies with the sensor input. The code I ended up with last night is below. With the setFrequency() function disabled, this code generates a nice 2ms square wave pulse on the scope. If I enable setFrequency(), the pulse width goes to 6.5ms.

2 things I noticed…(1) Changing OCR1A on the fly doesn’t effect the pulse…or not in the way I expected. With the for loop, I expected to see the pulse frequency get progressively smaller on the scope, instead I saw the pulse width go right to 6.5ms and stay there. If I disable setFrequency() and change OCR1A in setup() the change is visible on the scope. If I change OCR1A in the main loop() nothing happens.

(2) Changing OCR1B has no effect on duty cycle. I can set OCR1B to any number and I still get a perfect square wave.

I was able to set frequency and duty cycle using the Timer1 library and verified that on the scope. The following line seemed to work OK and generated a pulse width of 3us. The problem is that it will not update the frequency on the fly.

Timer1.pwm(9,1,1000);

You mentioned the timers acting differently and that could very well be the case. Could be that Timer1 is nt capable of setting the duty cycle and/or not capable of doing either on the fly. Timer resolution shouldn’t be a problem. Timer2 at 8 bits should work fine. I just used Timer1 because I’ve already implemented Timer2.


long i = 0;

void setup() {
pinMode(9, OUTPUT);
TCCR1A = _BV(COM1A0) | _BV(WGM10) | _BV(WGM11) | _BV(WGM12);
TCCR1B = _BV(CS11) | _BV(WGM13) ;
OCR1A = 20000;
OCR1B = 10000;
TCCR1B = TCCR1B & 0b00111000 | 0x2;
}

void loop() {
for (i = 100; i <= 20000; i++); {
// setFrequency(i);
delay(10); }
}

void setFrequency(long f){
OCR1A = f;
OCR1B = f / 2;
}

I think I found my problem. It appears that only channel B, which on timer1 is pin 10, can be used for PWM in Fast PWM mode. I'm trying to use channel A (Pin 9). There seems to be an exception if COM1A0:1 is set to "Toggle on compare match". In this mode, channel A will work, but only at a fixed 50% duty cycle, which is about what I'm seeing.

I must say...after goint through the data sheet line by line, Timer2 seems to be much easier to implement.

I've done some stuff on timers here.

http://gammon.com.au/forum/?id=11504

Certain modes use both "sides" of the timer, and the output is only on the B side. That is because the A side sets the frequency and the B side the duty cycle. The documentation is a bit unclear on that point.

Thanks Nick....

I was able to get it working on timer1. I read two voltages and output a pulse stream based on the difference between the two. It worked very well up until the point that I started adding in the ISR's on timer2 and pins 2 and 3. With the timer pulsing, which it does pretty much continuously, nothing else seems to work right.

My first though was to not use readAnalog() but rather dissect it and speed it up. I suspect there is a lot going on in that function that I don't need. I need to initialize the analog input pin once and read the value multiple times. If that doesn't work, I thought I could move the readAnalog() to a timer ISR and just hit it every 100ms or so. I don't really need to do an analog read every scan, although if I can speed it up, I'd like to.

I'm thinking that by the time I read 3 analog inputs, process that data and set OCR1A accordingly, there isn't a whole lot of time left for other processing.

Any idea where I can find the C code behind the readAnalog() function?

It's in the file wiring_analog.c which is in the "cores/arduino" directory.

You may want to check out this:

http://www.gammon.com.au/interrupts

Near the bottom I describe how you can start an analogRead (in effect) and pick up the results later when an interrupt fires.

Hi rrc1962,

this is the exact problem if thrying to figure out, how did you go in the end.

My post http://forum.arduino.cc/index.php?topic=405997.0