Updating PWM Duty Cycle from ADC Sensor Reading
AVR®: Updating PWM Duty Cycle from ADC Sensor Reading
In this video:
- Update the PWM duty cycle based upon the ADC result register.
- Open AVR_PWM.c from a previous video.
- Copy relevant PWM_init_LED() function, defines, and interrupt service routines back into this project.
- Verify that we can change the LED brightness by varying the POT connected to the ADC.
- Find the user guide and schematics for the I/O1 Xplained Pro (ATIO1-XPRO) from the product page.
- Connecting the I/O1 Xplained Pro to the ATmega328P Xplained Mini.
- Verifying that we are now able to change the LED brightness as the light sensor reading changes.
Now that we are familiar with some of the AVR® peripherals, it is time to start pulling them together. In this lesson, we will combine the ADC with the PWM so that we can control the duty cycle with a light sensor. The Light Sensor will be an from the I/01 Xplained Pro that we will connect to the Xplained Mini. You can add these changes to the previous lesson's main.c file or create a new project and copy/pasted the entire contents of the AVR_ADC main.c file into it.
Procedure
Update the Interrupt Service Routine
Remove the LED_TOGGLE macro from the ISR(ADC_vect) and add the following.
OCIE1B = duty;
We are going to use the LED again so we need to set that as an output. Place the following line in your main loop above the ADC_init() function.
Copy Over CPU frequency from Previous Lesson
Open the AVR_PWM main.c file or open the AVR_PWM project in MPLAB® X IDE. Now we can start pulling out the pieces that we need. Starting with
Copy over the F_CPU declaration and put it at the top of our new main.c file.
Copy the PWM_Init() Function
Copy the PWM_Init() function from the previous PWM lesson and paste it just below the Interrupt Service Routine. The PWM_Init() looks like this...
{
TCCR1B |= (1 << CS10) | (1 << WGM12); //No Prescaler & set mode to CTC
TIMSK1 |= (1 << OCIE1A) | (1 << OCIE1B); //Enable the CTC interrupt
OCR1A = 800;
OCR1B = 400;
}
Call it from the main while loop like this...
Move over the ISRs from the PWM main.c file
Move these Interrupt Service Routines to our new project. Place them just above the ISR for the ADC.
{
LED_ON;
}
ISR(TIMER1_COMPB_vect)
{
LED_OFF;
}
Full Code Listing
Review the code below to make sure it matches your main.c file. Connect the potentiometer with the wiper on PC1 and the other two connections on VCC and GND.
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define LED_ON PORTB |= (1<<PORTB5)
#define LED_OFF PORTB &= ~(1<<PORTB5)
#define LED_TOGGLE PINB |= (1<<PINB5)
ISR(TIMER1_COMPA_vect)
{
LED_ON;
}
ISR(TIMER1_COMPB_vect)
{
LED_OFF;
}
ISR(ADC_vect)
{
uint16_t duty = ADC;
OCR1B = duty;
}
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 ADC_init(void)
{
ADMUX |= (1 << REFS0) | (1 << MUX0); //AVCC equal to VCC and ADC1 input
ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
sei();
}
int main(void) {
DDRB |= (1 << DDB5);
ADC_init();
PWM_Init();
while (1) {
}
}
Program the Device
Press the Make and Program Device button.
You should observe that the LED gets dimmer and brighter depending on which direction the potentiometer is turned.
Connect the PWM to a Light Sensor
Locate the documentation for the light sensor within the I/01 Xplained User Guide. From the schematic we see that the light sensor is connected to Pin 3. We can access GND through PIN 2 and VCC through Pin 20. Remove the potentiometer and make these connections to the light sensor.
You should observe that when you wave your hand over the light sensor and and block the light, the LED get brighter.