0
\$\begingroup\$

I am at the moment trying generate a PWM signal using timer1, but a failling misserably.

I am using this library available from arduino to interface the timer1.

http://playground.arduino.cc/Code/Timer1

The code i am running is this

#include "test.h"

volatile int step_count = 1;

test::test()
{
  pinMode(10,OUTPUT);
  Timer1.initialize(20);
  Timer1.attachInterrupt(callback);
}


static void test::callback()
{

}


void test::test_pwm()
{

  Serial.print("period: ");
  Serial.println(period_used);
  Serial.print('\n');
  Serial.print("value: ");
  Serial.print(value);
  Serial.print('\n');

  Timer1.pwm(10, (50.0 / 100) * 1023);

}

The constructor initializes the timer.

void TimerOne::initialize(long microseconds)
{
  TCCR1A = 0;                 // clear control register A 
  TCCR1B = _BV(WGM13);        // set mode 8: phase and frequency correct pwm, stop the timer
  setPeriod(microseconds);
}

SetPeriod() should be one that determines the frequency of the PWM

void TimerOne::setPeriod(long microseconds)     // AR modified for atomic access
{

  long cycles = (F_CPU / 2000000) * microseconds;                                // 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

  oldSREG = SREG;               
  cli();                            // Disable interrupts for 16 bit register access
  ICR1 = pwmPeriod = cycles;                                          // ICR1 is TOP in p & f correct pwm mode
  SREG = oldSREG;

  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
  TCCR1B |= clockSelectBits;                                          // reset clock select register, and starts the clock
}

I don't see anything wrong with the way it implemented, but are pretty sure that something must be wrong here, but can't quite figure out where the error is.

And the pwm function is coded here:

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);
  resume();         // Lex - make sure the clock is running.  We don't want to restart the count, in case we are starting the second WGM
                    // and the first one is in the middle of a cycle
}

the problem here is that I can't create a PWM signal that doesn't have a frequency more or less than 490 hz. If try to change the value it initializes changes the duty cycle, rather than the frequency... What could be wrong here?

Becomming annoyed by the library i began setting things up manually

Here is the code. It should create an interrupt each 1 hz,in which the state of the pin gets toggled. The frequency of the pin toggling occurs at 490 hz.

#include "test.h"

test::test()
{
  pinMode(10,OUTPUT);
  //Timer1 setup1 Interrup at 1hz
  cli(); // Stop interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 15624; // Compare register value = cpu_fre/(interrupt_freq*prescaler)-1 (must be <65536)
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << CS12) | (1 << CS10); 
  TIMSK1 |= (1 << OCIE1A);

  sei(); //allow interrupts
}

ISR(TIMER1_COMPA_vect)
{
 digitalWrite(10,!digitalRead(10));
}
\$\endgroup\$
16
  • \$\begingroup\$ How are you measuring the frequency? How do you know that setPeriod() doesn't work to change timer1's frequency? Have you tried running timer1 with different clock divider (pre-scaller) values, i.e. have you tried setting TCCR1B CS12, CS11, and CS10 to all values from 001 to 101? It is pretty much impossible for that not to affect the frequency. \$\endgroup\$
    – gbulmer
    Oct 3, 2016 at 16:22
  • \$\begingroup\$ Yes.. an nothing works.. I am currently scoping things.. the only thing that changes is the duty cycle. \$\endgroup\$ Oct 3, 2016 at 17:04
  • \$\begingroup\$ Okay, silly question time. You do call test::test(), right? \$\endgroup\$ Oct 3, 2016 at 17:21
  • \$\begingroup\$ yes............. (sorry - but this little thing has kinda been annoying for couple of hours now..) \$\endgroup\$ Oct 3, 2016 at 17:23
  • \$\begingroup\$ So are you saying you have an oscilloscope on the correct output pins (9 or 10), and have with a minimal Arduino program that does nothing but: void setup() {} void loop() { TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); TCCR1B |= _BV(CS12) | _BV(CS10); analogWrite(9, 128); } and its frequency is still 490Hz, constant duty cycle? In that case, there is something deeply broken. If you aren't cutting the test down to a minimal example, then try to learn how to do that. Testing a pile of code which doesn't seem to work tells you that the code doesn't work, but it'll be very hard to find why. \$\endgroup\$
    – gbulmer
    Oct 3, 2016 at 17:23

3 Answers 3

3
\$\begingroup\$

Do not configure hardware in a global constructor. Your constructor will be called before main(), and main() will call init() for initializing the Arduino core library. And init() will reconfigure Timer 1 for PWM at 490 Hz.

That's why many Arduino libraries have classes that implement an begin() method: for delaying the hardware initialization until after init() is done.

\$\endgroup\$
1
\$\begingroup\$

As noted in Gerben's comment, selecting clock source 7 via (1 << CS12)| (1 << CS11) | (1 << CS10) sets up for External clock source on T1 pin, Clock on rising edge.

See Table 16-5, Clock Select Bit Description, in ATmega328 spec sheet.

CS12 CS11 CS10 Description
 0    0    0   No clock source (Timer/Counter stopped).
 0    0    1   clkI/O/1 (No prescaling)
 0    1    0   clkI/O/8 (From prescaler)
 0    1    1   clkI/O/64 (From prescaler)
 1    0    0   clkI/O/256 (From prescaler)
 1    0    1   clkI/O/1024 (From prescaler)
 1    1    0   External clock source on T1 pin. Clock on falling edge.
 1    1    1   External clock source on T1 pin. Clock on rising edge.

Instead of setting all of the CS12, CS11, CS10 bits, perhaps you should set only one or two, depending on what prescale factor you want.

\$\endgroup\$
-1
\$\begingroup\$

You should be able to adjust the frequency by changing the frequency of the timer for the pin you are using. For example, pins 5 and 6 are controlled by Timer 0 which has a frequency of up to 62500 Hz. To change the timer, you need to set TCCR0B:

Setting     Divisor     Frequency
0x01        1           62500
0x02        8           7812.5
0x03        64          976.5625   <--DEFAULT
0x04        256         244.140625
0x05        1024        61.03515625

TCCR0B = (TCCR0B & 0b11111000) | <setting>;

You can change the other timers in a similar way by changing TCCR1B or TCCR2B, although they have a different base frequency (31372.55 Hz).

See the tutorial here (the source of the above table): http://playground.arduino.cc/Main/TimerPWMCheatsheet

Here is some example code that changes the frequency: http://playground.arduino.cc/Code/PwmFrequency

\$\endgroup\$
4
  • \$\begingroup\$ isn't there way to do it smoothly rather than doing it using those prescaler sizes? \$\endgroup\$ Oct 3, 2016 at 15:09
  • \$\begingroup\$ Weirder may be why setPeriod isn't doing that in the first place? \$\endgroup\$ Oct 3, 2016 at 15:11
  • \$\begingroup\$ @CarltonBanks: It is doing that, it's just using the actual bit names instead of magic numbers. \$\endgroup\$ Oct 3, 2016 at 17:09
  • \$\begingroup\$ Well.. The frequency doesn't change. I even tried set it up myself and it only changes the duty cycle. not the frequency \$\endgroup\$ Oct 3, 2016 at 17:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.