Go Down

Topic: "Previoius definition of 'class TimerOne'" and creating PWM with Timer1 (Read 677 times) previous topic - next topic

Deva_Rishi

yeah i think the title of this thread is not really corresponding to it's issue anymore.
To 'Correct' you have to be Correct. (and not be condescending..)

gamd

@Deva_Rishi

Unfortunately that doesn't work either. The output on the Serial Monitor ist just "00000000000000000000000000000000000000000000".

Deva_Rishi

And setting the pwm pin as output and reading it's state on another pin ?
To 'Correct' you have to be Correct. (and not be condescending..)

LightuC

Try this code
Code: [Select]



#include "TimerOne.h"

unsigned long pwm_high = 0;
unsigned long current_t = 0;

volatile byte flag = LOW;

void setup()
{
  Serial.begin(9600);
  pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
Timer1.initialize(50000); // initialize timer1, and set a 1/2 second period
Timer1.pwm(9, 128);                // setup pwm on pin 9, 50% duty cycle
  Timer1.attachInterrupt(callback);
  sei();
}


void loop()
{
  if(flag == HIGH)
  {
Serial.print(millis());
flag = LOW;
  }
}

void callback()
{
  flag = HIGH;
}


At no point does the author of the TimerOne library enable the global interrupt flag. Thus the callback is never called and you only print out the pin state. Adding it in the setup function should enable the interrupts.
Quote
Modified Oct 2011 by Andrew Richards to avoid certain problems:
[...]
- Remove global enable of interrupts (sei())- could be running within an interrupt routine)

blh64

At no point does the author of the TimerOne library enable the global interrupt flag. Thus the callback is never called and you only print out the pin state. Adding it in the setup function should enable the interrupts.
interrupts are on by default.

If you change pin 9 to pin 13 and toggle it in the ISR callback, the onboard LED will blink quite nicely

LightuC

interrupts are on by default.

If you change pin 9 to pin 13 and toggle it in the ISR callback, the onboard LED will blink quite nicely
Uff, you are absolutely right. I only saw the cli() without any sei(). But in fact, the author stores and restores SREG.

gamd

Thank you for the help! It works now. Reading the signal on Pin 10 works. Now I have connected Pin 9 and Pin 6 by a cable. My current code looks like this:

Code: [Select]

#include "TimerOne.h"

unsigned long pwm_high = 0;
unsigned long current_t = 0;

volatile byte flag = LOW;

void setup()
{
  Serial.begin(9600);
pinMode(10, OUTPUT);
pinMode(9, OUTPUT);
pinMode(6, INPUT);
Timer1.initialize(50000); // initialize timer1, and set a 1/2 second period
Timer1.pwm(9, 128);                // setup pwm on pin 9, 50% duty cycle
Timer1.attachInterrupt(callback);
sei();
}


void loop()
{
Serial.print(digitalRead(6));
/*if(flag == HIGH)
  {
Serial.print(millis());
Serial.print(F(": "));
Serial.println(digitalRead(10));
flag = LOW;
}*/
}

void callback()
{
digitalWrite(10, !digitalRead(10));
flag = HIGH;
Serial.print("X");
}



And the output on my Serial Monitor like: 00011X11100000000000000000000000000000000000000000011X1110000

The goal was to actual see the PWM with duty cycle ~25%. But I am not sure how this works. When my timer1 overflows after 50ms so is the period of time for my pwm equal to 50ms right? And 25% of these 50ms should be on HIGH, but it turned out as wrong.

gamd

I've read the TimerOne.cpp and TimerOne.h a bit to understand the actual code in there in order to get how setting up a PWM signal even works.

So we have pwm():
Code: [Select]

void TimerOne::pwm(char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(microseconds);
  if(pin == 1 || pin == 9) {
    DDRB |= _BV(PORTB1);                                   // sets data direction register for pwm output pin
    TCCR1A |= _BV(COM1A1);                                 // activates the output pin
  }
  else if(pin == 2 || pin == 10) {
    DDRB |= _BV(PORTB2);
    TCCR1A |= _BV(COM1B1);
  }
  setPwmDuty(pin, duty);
  start();
}


And in there setPeriod():
Code: [Select]

void TimerOne::setPeriod(long microseconds)
{
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
  ICR1 = pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                                     // reset clock select register
}


So my first question is what exactly "long cycles = (F_CPU * microseconds) / 2000000;" does do? Is it the timer cycle? But what is F_CPU e.g. and why dividing by 2000000?

blh64

So my first question is what exactly "long cycles = (F_CPU * microseconds) / 2000000;" does do? Is it the timer cycle? But what is F_CPU e.g. and why dividing by 2000000?

It is a predefined constant that is set according to the speed of the processor you are using.  For example, a Uno runs at 16MHz.

That code is just determining what the prescale values need to be in order to achieve the desired period.  (The 2 is in there since the timer counts up and then down before an interrupt - look at the comment)

gamd

Thank you. To understand this I need to have a closer look here:

So cycles is the number who represents the period of time after that the timer should get interrupted. It is divided by 2, because the timer counts first up then down and the interrupt routine is called. Is that right?

But then I am wondering how exactly this pwm gets generaded. Let's take a closer look at setPwmDuty() in the code I posted below. There is "unsigned long dutyCycle", which is basically the number of cycles we determined in setPeriod();
After that dutyCacle gets multiplied with duty (this is a value from 0 to 1024 and represents the duty cycle, which means how long the signal is HIGH) and binary shifted right by 10dec.

But how does this determine how long the signal on e.g. output PIN 9 is HIGH?

Code: [Select]

void TimerOne::setPeriod(long microseconds)
{
  long cycles = (F_CPU * microseconds) / 2000000;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
  if(cycles < RESOLUTION)              clockSelectBits = _BV(CS10);              // no prescale, full xtal
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);              // prescale by /8
  else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10);  // prescale by /64
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);              // prescale by /256
  else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10);  // prescale by /1024
  else        cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10);  // request was out of bounds, set as maximum
  ICR1 = pwmPeriod = cycles;                                                     // ICR1 is TOP in p & f correct pwm mode
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                                     // reset clock select register
}

void TimerOne::setPwmDuty(char pin, int duty)
{
  unsigned long dutyCycle = pwmPeriod;
  dutyCycle *= duty;
  dutyCycle >>= 10;
  if(pin == 1 || pin == 9)       OCR1A = dutyCycle;
  else if(pin == 2 || pin == 10) OCR1B = dutyCycle;
}

void TimerOne::pwm(char pin, int duty, long microseconds)  // expects duty cycle to be 10 bit (1024)
{
  if(microseconds > 0) setPeriod(microseconds);
  if(pin == 1 || pin == 9) {
    DDRB |= _BV(PORTB1);                                   // sets data direction register for pwm output pin
    TCCR1A |= _BV(COM1A1);                                 // activates the output pin
  }
  else if(pin == 2 || pin == 10) {
    DDRB |= _BV(PORTB2);
    TCCR1A |= _BV(COM1B1);
  }
  setPwmDuty(pin, duty);
  start();
}

blh64

look at this line:
Code: [Select]
ICR1 = pwmPeriod = cycles;
That is the register the chip uses to count up to.  It is all done in the hardware of the '328 chip.  As part of the functionality, given the mode it is configured to operate in, it will toggle pin 9.  Download the Atmel '328 datasheet and read all about it here

Go Up