Problem with using sprintf and ADC in continuous conversion.

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

Hello everyone!

I am trying to display the results of ADC on LCD. The ADC is supposed to operate in a continuous mode. However, I can obtain only the first result of ADC, as afterwards it stops and the results on LCD do not change while I change the value of input voltage. In order to obtain changed  results in is necessary to restart microcontroller.  The microcontroller is Atmega16. 

In order to determine the exact line in the program which causes stuck of ADC results I have also added eight LEDs to B port and send the results of ADC to this port. As a result, when I exclude lines with sprintf and printf in the following code LEDs connected to B port blink continuously while I change the value of input voltage, therefore in this case ADC operates continuously. However, when I include 

lines with sprintf and printf LEDs blink only once, when I start simulation in Proteus, and do not follow the changes of input voltage. The numbers on LED also show only the first result of ADC conversion. 

Where can be my mistake? (mistakes more likely).

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

void ADC_INIT ()
{
	ADCSRA |= (1<<7);
	ADCSRA |= (1<<0)| (1<<1)| (1<<0);
	ADCSRA |= (1<<5); // ADATE = 1;
	SFIOR &= ~(1<<7) &~ (1<<6) &~ (1<<5);
	ADMUX &= ~(1<<7) &~ (1<<6);
	ADMUX &= ~(1<<0);

}

void send_command_to_LCD (unsigned char comm)
{
	PORTC &= ~(1<<0);
	PORTD = comm;
	PORTC |= (1<<1);
	_delay_us(50);
	PORTC &= ~(1<<1); //
	_delay_us(80);
}

void send_data_to_LCD (unsigned char data)
{
	PORTC |= (1<<0);
	PORTD = data;
	PORTC |= (1<<1);
	_delay_us(50);
	PORTC &= ~ (1<<1);
	_delay_us(80);
}

void LCD_Init ()
{
	_delay_ms(60);
	send_command_to_LCD (0x38);
	_delay_us(80);
	send_command_to_LCD (0x0F);
	_delay_us(80);
	send_command_to_LCD (0x01);
	_delay_ms(4);
	send_command_to_LCD (0x06);
	_delay_us(80);
}

static FILE stdout_LCD = FDEV_SETUP_STREAM(send_data_to_LCD, NULL, _FDEV_SETUP_WRITE);

int main(void)
{
	stdout = &stdout_LCD;
    DDRC |= (1<<1)|(1<<0);
	PORTC &= ~(1<<1) &~ (1<<0);
	DDRD = 0xff;
	DDRB = 0xff;
	PORTD = 0b00000000;
	LCD_Init ();
	ADC_INIT ();
	ADCSRA |= (1<<6);
	for (;;)
	{
		if (ADCSRA & (1<<4))
	{
		 unsigned int adc_result=ADCW;
		 PORTB = adc_result;
		 double adc_result_volts=(double)(adc_result*5)/1024;
		 char mystr[5];
		 sprintf(&mystr, "%0.2f", adc_result_volts);
		 printf("%s:",&mystr);

         ADCSRA|=(1<<4);
			}
}
}

 

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

(1 << 4). What does the 4 mean? Can you use bit names as it makes it much easier for us to read rather than having to reverse engineer your code?

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

Ok. Understood. I will reload the program with bit names.

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

In a situation like this, I assume that the MCU is going too slow.  Or, the printf() and sprintf() {when using floats} functions are taking so much bandwidth (especially if the ATmega16 is running at 1 MHz) that ADC flags are being overwritten.  It also may be due to using the simulator instead of a real chip.

 

Try only starting a new ADC conversion after all the displaying and conversions have finished.   And, try to not use floats or doubles.  Multiply the 10-bit ADC result by 5 (assuming 5V = Vcc) and displaying these three to five integer digits as the ADC result in milliVolts.

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

double adc_result_volts=(double)(adc_result*5)/1024; will produce 25.0 if AVCC is 5.0 volts,
the sprintf(&mystr, "%0.2f", adc_result_volts); will put "25.00" and a trailing null into the (too small) mystr

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

Yes. AVCC was chosen equal to 5 volts.

How to determine the appropriate size of mystr?

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

I mean, which is supposed to fit the size of adc_result_volts?

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

MIke, i think your maths is a bit off. The adc FS is 1023 times 5 divided by 1024 gives less than 5.00. So i’d suggest the array is just big enough, but personally i’d have made it a bit bigger.

Ivo, avr-gcc doesn’t have double. You only get single precision.

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

Sorry everyone for the incorrect #5 reply ....
not enough coffee this morning. :(

Last Edited: Sun. Dec 1, 2019 - 10:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
double adc_result_volts=(double)(adc_result*5)/1024;

Surely the "(double)" cast is misplaced? Personally I would simply do:

double adc_result_volts = (adc_result * 5.0F) / 1024;

The conversion of adc_result from "unsigned int" (which should probably be "uint16_t" in fact) to float happens as soon as it is multiplied by 5.0F (The .0F suffix means it is "float"). As the result of the mutiplication is then a float so the division will also be done in float.