PWM (Part 2)

Part 1234

The standard analogWrite() function fails to provide the choice for any base frequency. On the other hand, we may like to set any pin from any port as an output to the driving element (e.g transistor, MOS FET). The proposed code offers this flexibilty while keeping very simple. It comprises two functions: one for initializing the PWM port and an other for setting the duty cycle at any time after successfull initialization.

Timer 2 shall be used to generate the appropriate wave, leaving timer 0 in peace with the standard Arduino functions (e.g. delay) and leaving timer 1 which has 16 bits of resolution free for more demanding functions.

Within the various modes available for timer 2 (which are # from the other timers), there is one which fits perfectly our requirement and this is mode 5.

‘Phase correct’ is related to the option of choosing the TOP limit of the count-up process before counting down, back to 0. In this way, it is possible to set the frequency for each cycle. In more technical words, one cycle start with TCNT2 (register that contains the counts) equaling 0 and. Starting from there, timer 2 adds +1 for each clock cycle. TCNT2 content is compared to OCR2A (register that contains the upper counting limit). When TCNT2 equals OCR2A, the timer 2 adds -1 at each clock cycle until TCNT2 equals 0. And so on, and so on. While OCR2A is an 8 bit register, the longest cycle shall consist in 255 clock cycles for the count-up and 255 clock cycles for the count-down (2 ramps, thus the factor ‘2’). Arduino CPU are clocked at 16 Mhz, which means that the minimal achievable frequency cannot be less than 16000000 / 2 / 255 = 31.372 kHz, unless…

Unless we use the prescaler feature. Roughly, the prescaler divides the base clock frequency by a factor of 1 (no prescaling), 8, 32, 64, 128, 256 or 1024.

Now has come the time to make choice for one of them! The routine I designed is very simple. Starting from the least prescaling factor, it computes the prescaler which leaves us with the maximum resolution, lower than 255 counts. In the same time the upper counting limit is calculated. Here is a sample from this code:

void PlainPWM::SetFrequency(uint32_t frequency)
/* Set timer for the specified frequency */
{
	/* Set global parameter */
	_frequency = frequency;
	/* Constrain frequency from 35 to 350000 Hz */
	if (_frequency < 35) {
		_frequency = 35;
	} else if (_frequency > 350000) {
		_frequency = 350000;
	}
	/* Compute clock timer prescaler */
	uint16_t preScalers[] = {1, 8, 32, 64, 128, 256, 1024};
	uint8_t prescaler = 0;
	do {
		uint16_t factor = preScalers[prescaler++];
		_upperCount = uint32_t((F_CPU >> 1) / (_frequency * factor)) - 1 ;		
	} while (_upperCount > 255);
	/* Set Timer/Counter Control Register */
	/* Clear previous prescaler */
	TCCR2B &= ~((1 << CS22) | (1 << CS21) | (1 << CS20)); 
	/* Set prescaler */
	TCCR2B |= prescaler;
	/* Set Output Compare Register A */
	OCR2A = _upperCount;
};

At this stage we have properly set our base frequency. Next step will consist in generating a variable pulse width which is synchronised to this base frequency.

Next post on same subject

Leave a Reply

You must be logged in to post a comment.