I thought it was time that I got my head round using registers to control PWM as I may want to use it for something that I have in mind. Because of existing hardware I need to control the PWM duty cycle on pin 9 of a Nano. Reading around I find that PWM on pins 9 and 10 are controlled by timer1
Looking further I found this example
void setup()
{
Serial.begin(115200);
while (!Serial);
DDRB |= (1 << DDB1) | (1 << DDB2);
// PB1 and PB2 is now an output
ICR1 = 0xFFFF;
// set TOP to 16bit
OCR1A = 0x3FFF; //is this pin 9 ?
// set PWM for 25% duty cycle @ 16bit
OCR1B = 0xBFFF; //is this pin 10 ?
// set PWM for 75% duty cycle @ 16bit
TCCR1A |= (1 << COM1A1) | (1 << COM1B1);
// set none-inverting mode
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM12) | (1 << WGM13);
// set Fast PWM mode using ICR1 as TOP
TCCR1B |= (1 << CS10);
// START the timer with no prescaler
// we have a working Fast PWM
}
void loop()
{
}
to which I have added questions about which pin is being controlled. The other comments are part of the original example
If the above code is correct then I would expect PWM output on pins 9 and 10 but that does not appear to be occurring. Changing the values of OCR1A or OCR1B does not change the output on pins 9 and 10. 9 appears permanently LOW whilst 10 appears to be permanently HIGH
Where have I gone wrong ?
There are timer presets from the ide which need to be cleared before doing your setup.
It is a good habit to always start your custom timer register code with
TCCR1A = 0;
TCCR1B =0;
I think in your case, the bit in WGM10 is still set from the default pwm mode of the ide and your mode is winding up as Fast pwm to OCR1A.
Then, with OCR1A as TOP value, the output setting makes it always on, and the compare match on OCR1B is never seen as its value is higher than OCR1A.
There is no need to use ICR1 for TOP if you are not using it for variable frequencies. There are several PWM modes with a fixed TOP value. Using ICR1 does give a wider selection of tradeoffs between resolution (bits) and frequency.
Here is an example I wrote of 16-bit PWM on Timer1:
/*
Demonstration of 16-bit PWM on Timer1
Written by John Wasser
PWM16Begin(): Set up Timer1 for PWM.
PWM16EnableA(): Start the PWM output on Pin 9
PWM16EnableB(): Start the PWM output on Pin 10
PWM16A(unsigned int value): Set the PWM value for Pin 9.
PWM16B(unsigned int value): Set the PWM value for Pin 10.
*/
// Set 'TOP' for PWM resolution. Assumes 16 MHz clock.
// const unsigned int TOP = 0xFFFF; // 16-bit resolution. 244 Hz PWM
// const unsigned int TOP = 0x7FFF; // 15-bit resolution. 488 Hz PWM
// const unsigned int TOP = 0x3FFF; // 14-bit resolution. 976 Hz PWM
// const unsigned int TOP = 0x1FFF; // 13-bit resolution. 1953 Hz PWM
// const unsigned int TOP = 0x0FFF; // 12-bit resolution. 3906 Hz PWM
const unsigned int TOP = 0x07FF; // 11-bit resolution. 7812 Hz PWM
// const unsigned int TOP = 0x03FF; // 10-bit resolution. 15624 Hz PWM
void PWM16Begin()
{
// Stop Timer/Counter1
TCCR1A = 0; // Timer/Counter1 Control Register A
TCCR1B = 0; // Timer/Counter1 Control Register B
TIMSK1 = 0; // Timer/Counter1 Interrupt Mask Register
TIFR1 = 0; // Timer/Counter1 Interrupt Flag Register
ICR1 = TOP;
OCR1A = 0; // Default to 0% PWM
OCR1B = 0; // Default to 0% PWM
// Set clock prescale to 1 for maximum PWM frequency
TCCR1B |= (1 << CS10);
// Set to Timer/Counter1 to Waveform Generation Mode 14: Fast PWM with TOP set by ICR1
TCCR1A |= (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << WGM12) ;
}
void PWM16EnableA()
{
// Enable Fast PWM on Pin 9: Set OC1A at BOTTOM and clear OC1A on OCR1A compare
TCCR1A |= (1 << COM1A1);
pinMode(9, OUTPUT);
}
void PWM16EnableB()
{
// Enable Fast PWM on Pin 10: Set OC1B at BOTTOM and clear OC1B on OCR1B compare
TCCR1A |= (1 << COM1B1);
pinMode(10, OUTPUT);
}
inline void PWM16A(unsigned int PWMValue)
{
OCR1A = constrain(PWMValue, 0, TOP);
}
inline void PWM16B(unsigned int PWMValue)
{
OCR1B = constrain(PWMValue, 0, TOP);
}
void setup()
{
Serial.begin(9600);
PWM16Begin();
// On the Arduino UNO T1A is Pin 9 and T1B is Pin 10
// PWM16A(0); // Set initial PWM value for Pin 9
// PWM16EnableA(); // Turn PWM on for Pin 9
PWM16B(0); // Set initial PWM value for Pin 10
PWM16EnableB(); // Turn PWM on for Pin 10
}
void loop()
{
int analogValue = analogRead(A0);
unsigned int PWMValue = map(analogValue, 0, 1023, 0, TOP);
PWM16B(PWMValue); // Update the PWM at the end of the current cycle
}
Thanks John, I have that example working. Now to experiment with it and understand what it is doing should I need to use it in a project