8-bit AVR® A More Complete PWM Driver
In this video
- Create functions: PWM_Increase_duty(), PWM_Decrease_duty(), PWM_BrightDim()
- Use enumerators to keep track of the direction of PWM duty cycle change.
- Declare our function prototypes.
Will will expand the options available for lighting our LED with the code changes outlined below. We will include the ability to not only increase the brightness but to dim it as well. We will clean up our code a bit by declaring function prototypes and using enumerators to keep track of our PWM duty cycle changes.
Procedure
Increase the LED Brightness
Take the logic that brightens the LED which is currently inside an ISR and copy and then delete it. Create a new function called PWM_Increase_duty just below the ISR and paste the copied logic into it. It should look like the accompanying code.
{
uint16_t period = OCR1A;
uint16_t duty = OCR1B;
if (duty < period)
{
duty++;
}
else
{
duty = 0;
}
OCR1B = duty;
}
Lower the LED Brightness
Copy this entire function and paste just below it and rename it PWN_Decrease_duty. Now modify the logic so that it tests whether duty is greater than 0 and decrements Duty is true. It should be like the accompanying code.
{
uint16_t period = OCR1A;
uint16_t duty = OCR1B;
if (duty > 0)
{
duty--;
}
else
{
duty = 0;
}
OCR1B = duty;
}
Determine the Direction of the Duty Cycle
Now we create a new function called PWM_BrightDim with the following logic and place it into the TIMER0 ISR. The code allows us to assign the direction of the duty cycle. UP and DOWN will be declared as enums toward the top of the code which will be shown in the final code version.
{ uint16_t period = OCR1A;
uint16_t duty = OCR1B;
static uint8_t direction;
switch (direction)
{
case UP:
if (++duty == (period - 1))
direction = DOWN;
break;
case DOWN:
if (--duty == 2)
direction = UP;
break;
}
OCR1B = duty;
}
Declare the Functions
You may notice that MPLAB® X is showing errors in our code. We need to declare functions toward the beginning to make the errors go away. Place the following code below the #defines which will declare our enums and our functions.
void PWM_Decrease_duty(void);
void PWM_Decrease_duty(void);
void PWM_BrightDim(void);
void Timer_Frequency(uint8_t freq);
void PWM_Init(void);
void milliS_timer(uint8_t milliS);
Final Code
Here is the final code that contains the elements shown above.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define LED_ON PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE PINB |= (1<<PINB5)
enum {UP, DOWN};
void PWM_Decrease_duty(void);
void PWM_Decrease_duty(void);
void PWM_BrightDim(void);
void Timer_Frequency(uint8_t freq);
void PWM_Init(void);
void milliS_timer(uint8_t milliS);
ISR(TIMER1_COMPA_vect)
{
LED_ON;
}
ISR(TIMER1_COMPB_vect)
{
LED_OFF;
}
ISR(TIMER0_COMPA_vect)
{
PWM_BrightDim();
}
void PWM_Increase_duty(void)
{
uint16_t period = OCR1A;
uint16_t duty = OCR1B;
if (duty < period)
{
duty++;
}
else
{
duty = 0;
}
OCR1B = duty;
}
void PWM_Decrease_duty(void)
{
uint16_t period = OCR1A;
uint16_t duty = OCR1B;
if (duty > 0)
{
duty--;
}
else
{
duty = 0;
}
OCR1B = duty;
}
void PWM_BrightDim(void)
{ uint16_t period = OCR1A;
uint16_t duty = OCR1B;
static uint8_t direction;
switch (direction)
{
case UP:
if (++duty == (period - 1))
direction = DOWN;
break;
case DOWN:
if (--duty == 2)
direction = UP;
break;
}
OCR1B = duty;
}
void Timer_Frequency(uint8_t freq)
{
TCCR1B |= (1 << CS12) | (1 << WGM12); //Set clock source & set mode to CTC
TIMSK1 |= (1 << OCIE1A); //Enable the CTC interrupt
OCR1A = (F_CPU/(freq*2*256)-1);
}
void PWM_Init(void)
{
TCCR1B |= (1 << CS10) | (1 << WGM12); //No Prescaler & set mode to CTC
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B); //Enable the CTC interrupt
OCR1A = 800;
OCR1B = 400;
}
void milliS_timer(uint8_t milliS)
{
TCCR0A |= (1 << WGM01); //set to CTC mode
TCCR0B |= (1 << CS02) | (1 << CS00); //set prescaler to 1024
OCR0A = milliS * 7.8125 - 1;
TIMSK0 |= (1 << OCIE0A); //enable the interrupt
}
int main(void) {
DDRB |= (1 << PB5); // set PB5 as output pin
DDRB &= ~(1<<DDB7); //set PB7 as an input pin
milliS_timer(2);
PWM_Init();
sei(); //Enable global interrupts
while (1) {
}
}
Program the Device
Press the Make and Program Device Main Project button at the top of the MPLAB X IDE.
You should observe the device increasing and decreasing brightness.