I've been trying to make a program that would continuously read analog values from 3 different pins and then make a PWM signal corresponding to each of those 3 pins. So basically, 3 output PWM channels with different duty cycles corresponding to 3 different analog values. The problem is that all the PWM channels share the duty cycle from what i noticed, but if I ignore the ADC and just set duty cycle in code they work well.
Here's the code:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
void selectADCchannel(uint8_t channel);
void initPWM();
void setupADC();
int main(void){
sei();
initPWM();
//start with 0'th channel; no channel selected needed
setupADC();
while(1){
}
}
ISR(ADC_vect){
//get current channel
uint8_t currentChannel = ADMUX & 0x0F;
//set output compare registers for current channel
switch(currentChannel){
case 0x00: OCR0A = ADCH;
break;
case 0x01: OCR0B = ADCH;
break;
case 0x02: OCR2B = ADCH;
break;
}
//loop through channels from 0 to 2
if(currentChannel == 2)
selectADCchannel(0x00);
else
selectADCchannel(currentChannel+1);
//restart conversion
ADCSRA |= 1<<ADSC;
}
void selectADCchannel(uint8_t channel){
//0xE0 is 11100000
//0x1F is 00011111
ADMUX = (ADMUX & 0xE0) | (channel & 0x1F);
}
void setupADC(){
//enable prescaler (128 for 16MHz => 125kHz)
//would probably work with 250kHZ for 8 bits
//set ADPS bits in ADCSRA to 111
ADCSRA |= 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0;
//enable result shift to ADCH to only read ADCH (8 bits)
ADMUX |= 1<<ADLAR;
//enable interrupt
ADCSRA |= 1<<ADIE;
//set voltage reference
//reference is AVcc
ADMUX |= 1<<REFS0;
//turn on ADC
ADCSRA |= 1<<ADEN;
//start conversion
ADCSRA |= 1<<ADSC;
}
void initPWM(){
//TIMER0 (8 bit timer)
//set pins 5D(OC0B) and 6D(OC0A) as output
DDRD |= 1<<DDD5 | 1<<DDD6;
//set fast-pwm mode
TCCR0A |= 1<<WGM00;
//set non-inverting mode for both pins
TCCR0A |= 1<<COM0A1 | 1<<COM0B1;
//set prescaler to 256
TCCR0B |= 1<<CS02;
//TIMER2 (8 bit timer)
//set pins 3D(OC2B) as output
DDRD |= 1<<DDD3;
//set fast-pwm mode
TCCR2A |= 1<<WGM20;
//set non-inverting mode for both pins
TCCR2A |= 1<<COM2B1;
//set prescaler to 256
TCCR2B |= 1<<CS22 | 1<<CS21;
}
So, what the program should do, after initialization, read the value from one of the ADC pins, and then set the PWM compare register of the corresponding pin to the ADCH value.
Thanks.
Best Answer
The code is fine. The problem was that I was only testing the ADC on one pin and floating the other two. I was expecting only the one I was testing to work, but apparently there's a lot of crosstalk between ADC pins if they're floating. Also, a lot of noise, even with capacitors on ARef and AVcc. Thanks and sorry for posting without fully testing before.