Need help with ADC & PWM

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

 

I made a program to adjust the pulse width by adjusting it through ADC.

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>

void adc_init(void)		//Function to initialize the ADC
{
	ADMUX = (1 << ADLAR);		//Result is LEFT adjusted
	ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);		//ADC is enabled with a prescaler of 128
}

int get_adc_val (char ch)		//Function for channel selection
{
	ch &= 0b00000111;		//Masked to restrict the value (0-7)
	ADMUX = (ADMUX & 0x28)|ch;		//Set the channel selection bits
	_delay_us(20);
	ADCSRA = (1 << ADSC);		//Start conversion
	while (!(ADCSRA & (1 << ADIF)));		//Wait for conversion complete
	return (ADC);		//Return the value from ADC data register
	ADCSRA = (1 << ADIF) | (1 << ADEN);		//Clear the conversion flag and re-enable the adc

}

int wayt (unsigned int t)		//Function for dutycycle
{
	TCCR0A = (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);	//Fast PWM mode is used with OC0A clear on Compare match & reset on overflow
	OCR0A = dutycycle;		//ADC data register value stored in Output compare register.
	TCCR0B = (1 << CS02) | (1 << CS00);		//Prescaler of 1024
}

int main ()
{
	int dutycycle;
	DDRB = 0x80;		//OC0A is at PB7
	adc_init();
	while (1)
	{
		dutycycle = get_adc_val(0);		//get ADC value from channel 0link
		_delay_us(10);
		wayt(dutycycle);		//Send the value to OCR0A
	}
}

The LED fails to blink.

Please guide me.

 

I feel that I am not reading the ADC data register properly.

This topic has a solution.
Last Edited: Mon. Jul 22, 2019 - 04:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am surprised that the program compiled.
In the wayt() procedure the variable dutycycle is not declared. (or should that dutycycle really be t ? )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks..Have done the correction but the problem persists.

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>



void adc_init(void)		//Function to initialize the ADC
{
	ADMUX = (1 << ADLAR);		//Result is LEFT adjusted
	ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);		//ADC is enabled with a prescaler of 128
}

int get_adc_val (char ch)		//Function for channel selection
{
	ch &= 0b00000111;		//Masked to restrict the value (0-7)
	ADMUX = (ADMUX & 0x28)|ch;		//Set the channel selection bits
	_delay_us(20);
	ADCSRA = (1 << ADSC);		//Start conversion
	while (!(ADCSRA & (1 << ADIF)));		//Wait for conversion complete
	return (ADC);		//Return the value from ADC data register
	ADCSRA = (1 << ADIF) | (1 << ADEN);		//Clear the conversion flag and re-enable the adc
	
}

int wayt (unsigned int dutycycle)		//Function for dutycycle
{
	TCCR0A = (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);	//Fast PWM mode is used with OC0A clear on Compare match & reset on overflow
	OCR0A = dutycycle;		//ADC data register value stored in Output compare register.
	TCCR0B = (1 << CS02) | (1 << CS00);		//Prescaler of 1024
}

int main ()
{
	int dutycycle;
	DDRB = 0x80;		//OC0A is at PB7
	adc_init();
	while (1)
	{
		dutycycle = get_adc_val(0);		//get ADC value from channel 0
		_delay_us(10);
		wayt(dutycycle);		//Send the value to OCR0A
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which AVR8 are you using ?


ADMUX = (ADMUX & 0x28)|ch; //Set the channel selection bits is 'odd', but
ADCSRA = (1 << ADSC); //Start conversion will disable the ADC (ADEN is 0 ). Try ADCSRA |= (1 << ADSC);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ATmega2560

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tried but in vain..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ADMUX = (ADMUX & 0x28)|ch; should really be ADMUX = (ADMUX & 0xE0)|ch;


Put the return (ADC); at the very end of the get_adc_val() procedure, otherwise the ADIF flag will not be reset.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have done the changes but nothing happened dear.

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>



void adc_init(void)		//Function to initialize the ADC
{
	ADMUX |= (1 << ADLAR);		//Result is LEFT adjusted
	ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);		//ADC is enabled with a prescaler of 128
}

int get_adc_val (char ch)		//Function for channel selection
{
	ch &= 0b00000111;		//Masked to restrict the value (0-7)
	ADMUX |= (ADMUX & 0xE0)|ch;		//Set the channel selection bits
	_delay_us(20);
	ADCSRA |= (1 << ADSC);		//Start conversion
	while (!(ADCSRA & (1 << ADIF)));		//Wait for conversion complete
			//Return the value from ADC data register
	ADCSRA = (1 << ADIF) | (1 << ADEN);		//Clear the conversion flag and re-enable the adc
	return (ADC);
}

int wayt (unsigned int dutycycle)		//Function for dutycycle
{
	TCCR0A = (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);	//Fast PWM mode is used with OC0A clear on Compare match & reset on overflow
	OCR0A = dutycycle;		//ADC data register value stored in Output compare register.
	TCCR0B = (1 << CS02) | (1 << CS00);		//Prescaler of 1024
}

int main ()
{
	int dutycycle;
	DDRB = 0x80;		//OC0A is at PB7
	adc_init();
	while (1)
	{
		dutycycle = get_adc_val(0);		//get ADC value from channel 0
		_delay_us(10);
		wayt(dutycycle);		//Send the value to OCR0A
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	ADCSRA |= (1 << ADSC);		//Start conversion
	while (!(ADCSRA & (1 << ADIF)));		//Wait for conversion complete
			//Return the value from ADC data register
	ADCSRA = (1 << ADIF) | (1 << ADEN);		//Clear the conversion flag and re-enable the adc

simpler is:

	ADCSRA |= (1 << ADSC);		//Start conversion
	while (ADCSRA & (1 << ADSC));	//Wait for conversion complete

There's no need to mess with ADIF when ADSC itself is "self clearing".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you checked wayt(myvalue) with some different values---do you see the expected PWM waveforms being generated on the proper pin?

 

 

The LED fails to blink.

What are you talking about???  The LED shouldn't blink, this is PWM you are trying. 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW why is wayt():

int wayt (unsigned int dutycycle)		//Function for dutycycle
{
	TCCR0A = (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);	//Fast PWM mode is used with OC0A clear on Compare match & reset on overflow
	OCR0A = dutycycle;		//ADC data register value stored in Output compare register.
	TCCR0B = (1 << CS02) | (1 << CS00);		//Prescaler of 1024
}

2 of those 3 lines are something you'd normally only do once at the top of the program. Something like:

int timer_init ()
{
	TCCR0A = (1 << COM0A0) | (1 << WGM01) | (1 << WGM00);	//Fast PWM mode is used with OC0A clear on Compare match & reset on overflow
	OCR0A = 128;		//Or some other "sensible" starting value
	TCCR0B = (1 << CS02) | (1 << CS00);		//Prescaler of 1024
}

int main ()
{
	int dutycycle;
	DDRB = 0x80;		//OC0A is at PB7
	adc_init();
        timer_init();
	while (1)
	{
		dutycycle = get_adc_val(0);		//get ADC value from channel 0
		_delay_us(10);                  // No idea what this delay is trying to achieve??
		OCR0A = dutycycle;		//Send the value to OCR0A
	}
}

In fact it might as well be:

	while (1)
	{
		OCR0A = get_adc_val(0);		//get ADC value from channel 0
	}

 

Last Edited: Mon. Jul 22, 2019 - 08:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Same opinion from my side, the init of the timer for the pwm should be done once outside the main loop when the uC starts.

Then you should sample with  the ADC at some rate and just update the duty_cycle.

And I guess what you expect from the LED is to fade in/fade out based on some potentiometer connected to the ADC?

For sure it will not blink, unless some strange behaviour in the program.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is not working either.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The problem seems with the ADC part..

Since the LED is fading as per the constant defined in OCR0A.

But as the constant is replaced with the get_adc_val(0); function, the LED does not blink.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I notice that the only upper bits you set in ADMUX is the ADLAR bit. So you are leaving REFS1:0 at 00. Was this a conscious decision or an oversight? By leaving the bits at 00 you are choosing:

 

 

Which then leads to the question of what you have connected to AREF? That is pin 98. Did you really mean to use AVcc perhaps? 

 

PS by the way that chip has FOUR UARTs. Would you not consider connecting at least one of them to your PC (using USB-TTL cable for $2 from ebay)? That way you could print out the values being read by your get_adc function.

 

Alternatively that chip has JTAG so you could connect a JTAG debugger and see what the ADC is actually reading that way.

 

Last Edited: Mon. Jul 22, 2019 - 03:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try to consider what clawson mentioned, and also find a way to debug the values coming from the adc, and of course the physical values that go to the adc pins.

If you have a jtag debugger, this is easy.

If not then you need to find a way to output some values from your uC, as clawson said serial would be the easiest way probably ?

If serial is too difficult, then also just connecting some leds and outputting the values from the adc in binary would be not so difficult, even if you need 10 leds for 5V :D

Last Edited: Mon. Jul 22, 2019 - 03:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But as the constant is replaced with the get_adc_val(0); function, the LED does not blink.

Again, please explain yourself...you program has nothing to do with making an led blink...why do you think it should be blinking?? 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is my bad..I was giving the signal to PIN97 (i.e. ADC0).

Giving signal to PIN98, resolved the issue.

 

Now one question. how can I receive adc value from channel ZERO i.e. ADC0. since I am selecting that channel in the function.

while (1)
	{
		dutycycle = get_adc_val(0);		//get ADC value from channel 0
		PORTB ^= (1 << led);
		wayt(dutycycle);		//Send the value to OCR0A
	}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for guiding me so far. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks..you pin pointed my mistake.. :)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks everyone...for your continuous support...you people are very responsive...this forum is really helping me grow my skills at embedded programming. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since I was trying an alternative way to check whether I have rightly configured the ADC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Zarrar wrote:
Since I was trying an alternative way to check whether I have rightly configured the ADC.

Have you considered using one of the worked examples in the Tutorials forum?

 

BTW, "that doesn't work" is not very helpful.  Neither is posting a test program that "doesn't work" which doesn't compile without syntax errors.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.