EduAQA
September 5, 2020, 2:37pm
#1
I’m trying to generate a pwm on D9 and D10
it is working on D9, but not on D10, I’m using an Arduino Nano
Maybe I’m confusing Channels and PINs
I’m assuming OCR1A = PIN D9 and OCR1B = PIN D10
#include <avr/interrupt.h>
#include <avr/io.h>
/*
Input Frequency → CLK=16MHz
Timer Resolution = (1 / (Input Frequency / Prescale)) = (Prescale / Input Frequency)
Prescaler Value | Resolution at 16MHz
1 | 62.5nS
8 | 0.6uS
64 | 4uS
256 | 16uS
1024 | 64uS
Target Timer Count = (1 / (Target Frequency * 2)) / (Prescale / Input Frequency) - 1
= (Target Period / 2) / Timer Resolution - 1
If: Target Period = 125nseg. → Target frequency = 8MHz
With prescaler = 1
Target Timer Count = (125nS/2)/62.5nS - 1 = 0
*/
#define TMR1 0 //for 8MHz: TMR1 = 0 , for 10kHz: TMR1 = 799
void setup() {
// put your setup code here, to run once:
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
//*********************************************************************
//TIMER1 (16 bits) in mode TOP
TCCR1B |= (1 << CS10); //selecting prescaler 0b001 (Tclk/1)
TCCR1B &= ~((1<<CS11) | (1<<CS12)); // turn off CS12 and CS11 bits
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
TCCR1A &= ~(1 << COM1A1);
TCCR1A |= (1 << COM1B0); // Enable timer 1 Compare Output channel B in toggle mode
TCCR1A &= ~(1 << COM1B1);
TCNT1 = 0;
OCR1A = TMR1;
OCR1B = TMR1;
//*********************************************************************
}
void loop() {
}
pcbbc
September 5, 2020, 2:39pm
#2
What are you missing?
[code]code tags[/code]
EduAQA
September 5, 2020, 2:54pm
#3
It is generating the PWM on D9, but it's NOT generating the PWM on D10
EduAQA
September 5, 2020, 3:36pm
#4
Let me put in a different way:
It’s generating the PWM in D9
What do I have to change to make the PWM on D10?
#include <avr/interrupt.h>
#include <avr/io.h>
#define TMR1 0 //for 8MHz: TMR1 = 0 , for 10kHz: TMR1 = 799
void setup() {
// put your setup code here, to run once:
pinMode(9, OUTPUT);
//*********************************************************************
//TIMER1 (16 bits) in mode TOP
TCCR1B |= (1 << CS10); //selecting prescaler 0b001 (Tclk/1)
TCCR1B &= ~((1<<CS11) | (1<<CS12)); // turn off CS12 and CS11 bits
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
TCCR1A &= ~(1 << COM1A1);
TCNT1 = 0;
OCR1A = TMR1;
//*********************************************************************
}
void loop() {
}
hzrnbgy
September 5, 2020, 4:03pm
#5
I think you have it backwards, its working on D10 and not on D9
D10 – OC1B
D9 – OC1A
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
With the mode you have, the TOP is OCR1A so you can’t generate a PWM on the D9 (OC1A) pin
If you want PWM on D9 (OC1A) , change your mode to
WGM 13:10 to (1 1 1 0)
Change WGM10 to 0
This will make ICR1 your TOP
Then set the duty cycle of OC1A using OCR1A
make sure you have
DDRB |= (1<<1) | (1<<2) (D10 and D9 as outputs)
And then use ICR1 as the TOP
hzrnbgy
September 5, 2020, 4:10pm
#6
If you are doing direct register access, it’s easier to read using one-liners
void timer1_init(void)
{
// use Fast PWM with TOP of ICR1
TCCR1A = 1<<COM1A1 | 0<<COM1A0 | 1<<COM1B1 | 0<<COM1B0 | 1<<WGM11 | 0<<WGM10;
// zero out counter
TCNT1 = 0;
// set TOP
ICR1 = 25000;
// set OC1A duty cycle
OCR1A = 10000 - 1;
// set OC1B duty cycle
OCR1B = 5000 - 1;
// no interrupts
TIMSK1 = 0<<OCIE1B | 0<<OCIE1A | 0<<TOIE1;
// start timer
TCCR1B = 0<<ICNC1 | 0<<ICES1 | 1<<WGM13 | 1<<WGM12 | 1<<CS12 | 0<<CS11 | 0<<CS10;
return;
}
Warning: The Arduino library sets some bits in the Timer1 control registers before your setup() is called. It is best to set the control registers to zero before you start setting bits:
TCCR1A = 0;
TCCR1B = 0;
Once you set any of the CS (Clock Select) bits in TCCR1B the timer will start running. It might be best to save those for last.
aarg
September 5, 2020, 4:27pm
#8
TCCR1A = 1<<COM1A1 | 0<<COM1A0 | 1<<COM1B1 | 0<<COM1B0 | 1<<WGM11 | 0<<WGM10;
Although you can do this, be aware that the 0<< are only “place holders” and don’t actually do anything. So you can use:
TCCR1A = 1<<COM1A1 | 1<<COM1B1 | 1<<WGM11;
If you prefer to add more later instead of editing the 0’s. You can also use the _BV macro:
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
if you prefer the style.
hzrnbgy
September 5, 2020, 4:57pm
#9
Those 0<<XXX are purely mnemonics, and it makes it easier to copy-paste when I need to re-use them.
EduAQA
September 5, 2020, 5:16pm
#10
Thank you all, but I still didn’t realize what a need change to generate the PWM on D10, steady of D9
i double check and it is generating on D9, with this code:
I tired to set both 9 and 10 as output, but it’s generating only in D9 … I have a Scope to measure
If I’m reading correct I’m setting the prescaler and is correct, I can see on the scope
I’m setting all WGM to 1, and should be correct because I’m getting the wave correct on D9
I set the COM1A to toggle and I’m using OCR1A, what seems correct because PWM is on D9
But why only D9 generate the PWM, even when D9 and D10 is set to output?
void setup() {
// put your setup code here, to run once:
pinMode(9, OUTPUT);
//*********************************************************************
//TIMER1 (16 bits) in mode TOP
TCCR1B |= (1 << CS10); //selecting prescaler 0b001 (Tclk/1)
TCCR1B &= ~((1<<CS11) | (1<<CS12)); // turn off CS12 and CS11 bits
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
TCCR1A &= ~(1 << COM1A1);
TCNT1 = 0;
OCR1A = TMR1;
pcbbc
September 5, 2020, 6:04pm
#11
Still missing code tags from your posts.
Notice how everyone else’s replies are neatly formatted?
After 12 posts you really shoul know better.
There’s even a helpful </> bouton on the forum toolbar to add them and a “copy sketch for forum” menu item in the IDE…
EduAQA
September 5, 2020, 6:35pm
#12
Sorry to offend the community. I’m just starting, let me try:
here is the current code, I reduced the speed just to be easy to diagnistocs
#include <avr/interrupt.h>
#include <avr/io.h>
/*
Input Frequency -> CLK=16MHz
Timer Resolution = (1 / (Input Frequency / Prescale)) = (Prescale / Input Frequency)
Prescaler Value | Resolution at 16MHz
1 | 62.5nS
8 | 0.6uS
64 | 4uS
256 | 16uS
1024 | 64uS
Target Timer Count = (1 / (Target Frequency * 2)) / (Prescale / Input Frequency) - 1
= (Target Period / 2) / Timer Resolution - 1
If: Target Period = 125nseg. -> Target frequency = 8MHz
With prescaler = 1
Target Timer Count = (125nS/2)/62.5nS - 1 = 0
*/
#define TMR1 31250 //for 8MHz: TMR1 = 0 , for 10kHz: TMR1 = 799
void setup() {
// put your setup code here, to run once:
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
//*********************************************************************
//TIMER1 (16 bits) in mode TOP
// TCCR1B |= (1 << CS10); //selecting prescaler 0b001 (Tclk/1)
// TCCR1B &= ~((1<<CS11) | (1<<CS12)); // turn off CS12 and CS11 bit
TCCR1B |= (1 << CS12); //selecting prescaler 0b001 (Tclk/1)
TCCR1B &= ~((1<<CS10) | (1<<CS11)); // turn off CS12 and CS11 bit
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
TCCR1A &= ~(1 << COM1A1);
TCNT1 = 0;
OCR1A = TMR1;
//*********************************************************************
}
void loop() {
}
Read reply #6 . Clear the presets from the ide before setting up the timer. The |= preserves them.
hzrnbgy
September 5, 2020, 7:07pm
#14
I’m setting all WGM to 1
As stated from my previous reply, you can’t use OC1A when all the WGM bits are 1
Here’s what the datasheets says
Using the ICR1 register for defining TOP works well when using fixed TOP values. By using ICR1, the OCR1A register is free to be used for generating a PWM output on OC1A
To use both OC1A and OC1B outputs for PWM, you have to change your WGM bits to
1<<WGM13 | 1<<WGM12
1<<WGM11 | 0<<WGM10
The ICR1 then become the TOP value for the PWM timer
EduAQA
September 5, 2020, 8:08pm
#15
Cleared the registers and change WGM10 to 0
Neither D9 nor D10 do the waveform generation with the attached code
#include <avr/interrupt.h>
#include <avr/io.h>
/*
Input Frequency -> CLK=16MHz
Timer Resolution = (1 / (Input Frequency / Prescale)) = (Prescale / Input Frequency)
Prescaler Value | Resolution at 16MHz
1 | 62.5nS
8 | 0.6uS
64 | 4uS
256 | 16uS
1024 | 64uS
Target Timer Count = (1 / (Target Frequency * 2)) / (Prescale / Input Frequency) - 1
= (Target Period / 2) / Timer Resolution - 1
If: Target Period = 125nseg. -> Target frequency = 8MHz
With prescaler = 1
Target Timer Count = (125nS/2)/62.5nS - 1 = 0
*/
#define TMR1 31250 //for 8MHz: TMR1 = 0 , for 10kHz: TMR1 = 799
void setup() {
// put your setup code here, to run once:
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
TCCR1B = 0;
TCCR1A = 0;
//*********************************************************************
//TIMER1 (16 bits) in mode TOP
// TCCR1B |= (1 << CS10); //selecting prescaler 0b001 (Tclk/1)
// TCCR1B &= ~((1<<CS11) | (1<<CS12)); // turn off CS12 and CS11 bit
TCCR1B |= (1 << CS12); //selecting prescaler 0b001 (Tclk/1)
TCCR1B &= ~((1<<CS10) | (1<<CS11)); // turn off CS12 and CS11 bit
TCCR1A |= ((1<<WGM11) | (1<<WGM10)); //Configure timer 1 for TOP mode (with TOP = OCR1A)
TCCR1A &= ~(1<<WGM10); // just add here to not change the previous line
TCCR1B |= ((1<<WGM13) | (1<<WGM12));
TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode
TCCR1A &= ~(1 << COM1A1);
TCCR1A |= (1 << COM1B0); // Enable timer 1 Compare Output channel B in toggle mode
TCCR1A &= ~(1 << COM1B1);
TCNT1 = 0;
OCR1A = TMR1;
OCR1B = TMR1;
//*********************************************************************
}
void loop() {
}
hzrnbgy
September 5, 2020, 9:47pm
#16
You haven't set your TOP value yet, which is now on the ICR1 register
Also, you should only start the Timer after setting all the configuration parameters to avoid glitching