Thursday, 28 April 2016

arduino - Change PWM frequency?



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));
}

Answer



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.


No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...