PWM library does not work on pin 11

I have a problem with a pwm library and i can't find the reason why.

I found this library on the internet to set the frequency from the timers. For the pin's 9 and 10 it works fine but for pin 11 and 3 it does not work for some reason. Does some one know what is wrong in this library? When it is fixed i will upload the corrected library so others can use it to.

PWM.h (4.76 KB)

ATimerDefs.h (7.67 KB)

BTimerDefs.h (3.85 KB)

Can you please post an example program that exhibits the problem

Sure, no problem.

I am working on this program now:

#include <PWM.h>
int32_t frequency = 40000;


 // Analog inputs connected to the variable resistors
    const int knobPin1 = 1; //LED 1 control 
    const int knobPin2 = 2; //LED 2 control 
    const int knobPin3 = 3; //LED 3 control 
    // PWM outputs connected to LED driver circuits
    const int drivePin1 = 9;//LED 1 drive
    const int drivePin2 = 10;//LED 2 drive
    const int drivePin3 = 11;//LED 3 drive
    // initial value for the variable resistors
    int knobValue1 = 0;
    int knobValue2 = 0;
    int knobValue3 = 0;
    void setup() {

 InitTimersSafe();
SetPinFrequencysafe(9,frequency);
SetPinFrequencysafe(10,frequency);
SetPinFrequencysafe(11,frequency);


       // set the drive pins as output:
       pinMode(drivePin1, OUTPUT);
       pinMode(drivePin2, OUTPUT);
       pinMode(drivePin3, OUTPUT);
    }
    void loop() {
       // read the variable resistors, convert it to 0 - 255
       knobValue1 = analogRead(knobPin1) / 8;
       knobValue2 = analogRead(knobPin2) / 8;
       knobValue3 = analogRead(knobPin3) / 8;
       // use the data to control the drive:
       pwmWrite(9, knobValue1);
       pwmWrite(10, knobValue2);
       pwmWrite(11, knobValue3);
    }
  // read the variable resistors, convert it to 0 - 255
  knobValue1 = analogRead(knobPin1) / 8;

It may not be causing your problem but something is wrong either with the code or the comment.

I can confirm @Dece's problem with pin 11. I have used the library example PWM_lib_example as test code.

The AT328 uses BTimerDefs.h and .cpp

#elif defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
	#include "utility/BTimerDefs.h"

I have found at least two problems, both related to the use of digitalPinToTimer(pin) in BTimerDefs.cpp which look like a problem with Pin 11. There is no return for Timer2A = pin 11.

Settting the frequency.

extern bool SetPinFrequency( int8_t pin, uint32_t frequency )
{
	uint8_t timer = digitalPinToTimer(pin);
	
	if(timer == TIMER0B)
		return Timer0_SetFrequency(frequency);
	else if(timer == TIMER1A || timer == TIMER1B)
		return Timer1_SetFrequency(frequency);
	else if(timer == TIMER2B)
		return Timer2_SetFrequency(frequency);
	else
		return false;
}

bool SetPinFrequencySafe(int8_t pin, uint32_t frequency)
{
	uint8_t timer = digitalPinToTimer(pin);
	
	if(timer == TIMER1A || timer == TIMER1B)
		return Timer1_SetFrequency(frequency);
	else if(timer == TIMER2B)
		return Timer2_SetFrequency(frequency);
	else
		return false;
}

Driving the output pin.

void pwmWrite(uint8_t pin, uint8_t val)
{
	pinMode(pin, OUTPUT);
	
	//casting "val" to be larger so that the final value (which is the partially
	//the result of multiplying two potentially high value int16s) will not truncate
	uint32_t tmp = val;
	
	if (val == 0)
		digitalWrite(pin, LOW);
	else if (val == 255)
		digitalWrite(pin, HIGH);
	else
	{
		uint16_t regLoc16 = 0;
		uint16_t regLoc8 = 0;
		
		uint16_t top;
		
		switch(digitalPinToTimer(pin))
		{
			case TIMER0B:
			sbi(TCCR0A, COM0B1);
			regLoc8 = OCR0B_MEM;
			top = Timer0_GetTop();
			break;
			case TIMER1A:
			sbi(TCCR1A, COM1A1);
			regLoc16 = OCR1A_MEM;
			top = Timer1_GetTop();
			break;
			case TIMER1B:
			sbi(TCCR1A, COM1B1);
			regLoc16 = OCR1B_MEM;
			top = Timer1_GetTop();
			break;
			case TIMER2B:
			sbi(TCCR2A, COM2B1);
			regLoc8 = OCR2B_MEM;
			top = Timer2_GetTop();
			break;
			case NOT_ON_TIMER:
			default:
			if (val < 128)
			digitalWrite(pin, LOW);
			else
			digitalWrite(pin, HIGH);
			return;
		}
		
		if(regLoc16)
			_SFR_MEM16(regLoc16) = (tmp*top)/255;
		else
			_SFR_MEM8(regLoc8) = (tmp*top)/255;
		
	}		
}

"Creating custom frequencies (beyond messing with the prescaler) with an 8bit timer requires the sacrifice of one channel. In other words, each 8bit timer that creates a custom frequency loses the ability to perform PWM on one pin (the one connected to the A channel to be more precise). All Arduinos except the Leonardo have two 8bit timers, meaning that setting all timers to a particular frequency will sacrifice a total of two pins on said Ardiuno."

Maybe this has something to do with it?

"Creating custom frequencies (beyond messing with the prescaler) with an 8bit timer requires the sacrifice of one channel. In other words, each 8bit timer that creates a custom frequency loses the ability to perform PWM on one pin (the one connected to the A channel to be more precise). All Arduinos except the Leonardo have two 8bit timers, meaning that setting all timers to a particular frequency will sacrifice a total of two pins on said Ardiuno."

Maybe this has something to do with it?

You have nailed it. Custom frequencies on the 8 bit timers go to a top value set by OCRxA, and that value is not available to set a duty cycle.

UKHeliBob:

  // read the variable resistors, convert it to 0 - 255

knobValue1 = analogRead(knobPin1) / 8;


It may not be causing your problem but something is wrong either with the code or the comment.

No, i had changed the devider from 4 to 8 because i needed a duty cycle from 0 to 50%.

cattledog:
I can confirm @Dece's problem with pin 11. I have used the library example PWM_lib_example as test code.

The AT328 uses BTimerDefs.h and .cpp

#elif defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#include "utility/BTimerDefs.h"



I have found at least two problems, both related to the use of digitalPinToTimer(pin) in BTimerDefs.cpp which look like a problem with Pin 11. There is no return for Timer2A = pin 11.

Settting the frequency.



extern bool SetPinFrequency( int8_t pin, uint32_t frequency )
{
uint8_t timer = digitalPinToTimer(pin);

if(timer == TIMER0B)
	return Timer0_SetFrequency(frequency);
else if(timer == TIMER1A || timer == TIMER1B)
	return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
	return Timer2_SetFrequency(frequency);
else
	return false;

}

bool SetPinFrequencySafe(int8_t pin, uint32_t frequency)
{
uint8_t timer = digitalPinToTimer(pin);

if(timer == TIMER1A || timer == TIMER1B)
	return Timer1_SetFrequency(frequency);
else if(timer == TIMER2B)
	return Timer2_SetFrequency(frequency);
else
	return false;

}




Driving the output pin.


void pwmWrite(uint8_t pin, uint8_t val)
{
pinMode(pin, OUTPUT);

//casting "val" to be larger so that the final value (which is the partially
//the result of multiplying two potentially high value int16s) will not truncate
uint32_t tmp = val;

if (val == 0)
	digitalWrite(pin, LOW);
else if (val == 255)
	digitalWrite(pin, HIGH);
else
{
	uint16_t regLoc16 = 0;
	uint16_t regLoc8 = 0;
	
	uint16_t top;
	
	switch(digitalPinToTimer(pin))
	{
		case TIMER0B:
		sbi(TCCR0A, COM0B1);
		regLoc8 = OCR0B_MEM;
		top = Timer0_GetTop();
		break;
		case TIMER1A:
		sbi(TCCR1A, COM1A1);
		regLoc16 = OCR1A_MEM;
		top = Timer1_GetTop();
		break;
		case TIMER1B:
		sbi(TCCR1A, COM1B1);
		regLoc16 = OCR1B_MEM;
		top = Timer1_GetTop();
		break;
		case TIMER2B:
		sbi(TCCR2A, COM2B1);
		regLoc8 = OCR2B_MEM;
		top = Timer2_GetTop();
		break;
		case NOT_ON_TIMER:
		default:
		if (val < 128)
		digitalWrite(pin, LOW);
		else
		digitalWrite(pin, HIGH);
		return;
	}
	
	if(regLoc16)
		_SFR_MEM16(regLoc16) = (tmp*top)/255;
	else
		_SFR_MEM8(regLoc8) = (tmp*top)/255;
	
}		

}

Is this the corrected script or the problem part?

alka:
"Creating custom frequencies (beyond messing with the prescaler) with an 8bit timer requires the sacrifice of one channel. In other words, each 8bit timer that creates a custom frequency loses the ability to perform PWM on one pin (the one connected to the A channel to be more precise). All Arduinos except the Leonardo have two 8bit timers, meaning that setting all timers to a particular frequency will sacrifice a total of two pins on said Ardiuno."

Maybe this has something to do with it?

So it is not possible with pin 11? I have now used pin 5 instead of 11 and now it works.