Comparison of the ADC value with a constant

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

Hi everyone!

I'm trying to compare the ADC value with a constant. 

The problem is that, I cannot to read the value of ADC. It always equals to zero. I cheked it by using the condition:

void PWM_D(void)
{
t4=0; // variable for PWM duty cycle control

float konst=0.5;
	if (ADC0<konst)
	{
		t4=2000;
	}
	else if (ADC0>konst)
	{
	t4=1750;

	}
	else if (ADC0==konst)
	{
	t4=1650;
	}

}

And it's always in the first statement (ADC0<konst). 

In the same time, I use UART to chek the real value of ADC, the value is more then 2 V.   It means, that "t4" should be equal to the 1650. But it is not.

This is the part of my code:


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

#define PortInit DDRB|=(1<<7)
#define LedOn	 PORTB|=(1<<7)
#define LedOff	 PORTB&=~(1<<7)

// Задаем переменные
unsigned int ADC7;
float n;
uint8_t num=0;
float ocr3c;// Регулировк скважности ШИМа "D"
// Переменные для задания энергии
int k=49;
int t;
int t4;

//Переменные для хранениея АЦП
volatile float  ADC0;

int period=2500;
int g=4992;
float time;

void PWM_D(void)
{
t4=0;
float konst=0.5;
	if (ADC0<konst)
	{
		t4=2000;

	}
	else if (ADC0>konst)
	{
	t4=1750;

	}
	else if (ADC0==konst)
	{
	t4=1650;
	}

}

int main(void) {
	DDRB=(1<<PORTB7)|(1<<PORTB6);
	DDRE=(1<<PORTE5)|(1<<PORTE4)|(1<<PORTE3);
	Energy();
    PWM_D();			//Инициализация АЦП
	PWM_ini();				//Инициализация ШИМ
	UART0_Init();			//Инициализация порта на  uART
	ADCInit();	

	sei();

	ADCSRA|=(1<<ADSC);

	while(1) {
PWM_D();			//Инициализация АЦП

	}
}

ISR(TIMER1_OVF_vect)
{

}

ISR(ADC_vect)
{
uint8_t theLOW=ADCL;
uint16_t ADC_ten_bits=ADCH<<8|theLOW;

if(ADMUX==0b01000000)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x0D); //делаем "Enter". "0x0D" - это Enter в ASCII коде
		ADC0=(float) ADC_ten_bits/204.8; // measuring of ADC0
		ADMUX=0b01000001;
	}
else if(ADMUX==0b01000001)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x20); //делаем "Space". "0x0D" - это Пробел в ASCII коде
		ADMUX=0b01000111;
		ADCSRA|=(1<<ADSC);
	}
else if(ADMUX==0b01000111)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x20); //делаем "Space". "0x0D" - это Пробел в ASCII коде
		ADMUX=0b01000000;
		ADCSRA|=(1<<ADSC);
	}

}

What's wrog with that? 

 

This topic has a solution.
Last Edited: Tue. Jan 14, 2020 - 07:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
uint8_t theLOW=ADCL;
uint16_t ADC_ten_bits=ADCH<<8|theLOW;

would be a lot simpler as:

uint16_t ADC_ten_bits= ADC;

You don't need to read ADCl and ADCH separately.

unsigned int ADC7;
volatile float  ADC0;

Two things. Why are these similar things of different types? Also why are you using all uppercase names for your own variables? Most programmers only use all upper case for macro names and you risk name pollution with symbols so similar to values that could be in the io.h file.

 

Also your output code:

		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x20);

would be a whole lot simpler if, once and for all, you implement:

uart0_tr_string(char * str) {
    while (*str) {
        uart0_tr(*str++);
    }
}

then later you can:

char string[10];
sprintf(string, "%f", n);
uart0_tr_string(string);

or something similar.

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

clawson wrote:
You don't need to read ADCl and ADCH separately.

Is that allows to read all 10 bits of ADC? I'm going to try this, maybe in helps to solve my problem

clawson wrote:

unsigned int ADC7;

I don't use this variable in the code. Just forgot to delete.

clawson wrote:
Also why are you using all uppercase names for your own variables? Most programmers only use all upper case for macro names and you risk name pollution with symbols so similar to values that could be in the io.h file.

Oh, it's new for me, thank you!

clawson wrote:

would be a whole lot simpler if, once and for all, you implement:

uart0_tr_string(char * str) {
    while (*str) {
        uart0_tr(*str++);
    }
}

But what if I need to convert the result to the ASCII code? 

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

Iaz5 wrote:
Is that allows to read all 10 bits of ADC?
Yes it reads the whole of ADCH+ADCL so you get 10 significant bits. Depending on ADLAR those are either the upper or lower 10 bits of the 16.
Iaz5 wrote:
But what if I need to convert the result to the ASCII code? 
That's why I showed sprintf(). In fact C effectively has two routines for converting integer values to displayable ASCII. One is the simple itoa() which just converts a plain integer to ASCII (in fact i_to_a means integer_to_ascii). But sprintf() (assuming your AVR has enough flash to accommodate it) is a much more feature rich function. It can easily convert float to ASCII as well as doing integers. Use %u to convert integers and %f to convert float.

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

well, I heed an advice, and change my code.

But I still cannot to export the value of ADC from ISR to "int main -> while(1)" cycle.

Why the programm cannot to compare the ADC0 (equals to 2-3 V) and the coefficient "k" ?

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

Iaz5, have you checked Clawson's FAQ#1.... the magic word is 'volatile'.

 

 

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

meslomp wrote:
have you checked Clawson's FAQ#1.... the magic word is 'volatile'.

As I understood, i need to use the volatile, and I did it to measure ADC0:

Iaz5 wrote:

volatile float  adc0;

 But it didn't help.

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

as you have not given the full failing example there still can be a lot of things wrong.

for instance... have you enabled ADC interrupts at all, the ADC init routine is missing.

Does the ADC work when not using interrupts, but polling for a conversion done?

clock settings of the ADC peripheral.......

 

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

meslomp wrote:
as you have not given the full failing example there still can be a lot of things wrong.

That it is:


#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define PortInit DDRB|=(1<<7)
#define LedOn	 PORTB|=(1<<7)
#define LedOff	 PORTB&=~(1<<7)

// Задаем переменные
float n;
uint8_t num=0;
/*
	Частота 16МГц
	Делитель=8
	16МЦг/8=2МГц
	Формула расчета частоты ШИМа приведена на странице 148 в даташите
	Чтобы получить 400 Гц нам надо установить OCR1A в 4999. по факту получилось 4992
	Чтобы задать скважность 40% задаем OCR1C в 40% от значения OCR1A
		*/
int ocr1a=4992;	//Задаем период.
int ocr1b=100;	//Регулировка скважности ШИМа "B"
int ocr1c;	//Регулировка скважности ШИМа "С"
int ocr3b=1997;
int ocr3c;// Регулировк скважности ШИМа "D"

// Переменные для задания энергии
int k=49;
int t;
int t4;

//Переменные для хранениея АЦП
volatile float  adc0;

int period=2500;
int g=4992;
float time;

void UART0_Init(void) {
	UBRR0L = 103;
	UBRR0H = 0;
	UCSR0B=(1<<RXEN0)|(1<<TXEN0);
	UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);
}
//Функция отправки на терминал
void uart0_tr(char value)
{
	while(!(UCSR0A & (1 << UDRE0))); // Ожидаем когда очистится буфер передачи
	UDR0 = value;	
	
}
//Инициализируем АЦП
void ADCInit(void) {
	
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);		//делитель=128
	ADMUX=(1<<REFS0);			//AVCC with external capacitor
	ADCSRA|=(1<<ADATE);// ADC Auto trigger enabled. Запуск измерения АЦП по переднему фронту врерывания
	ADCSRA|=(1<<ADIE);//разрешаем прерывания
	ADCSRA|=(1<<ADEN);//запуск АЦП
	ADCSRB|=(1<<ADTS2)|(1<<ADTS1);//ADC auto trigger source
	DIDR0|=(1<<ADC0D)|(1<<ADC1D)|(1<<ADC7D);
}

unsigned int adcRead(void) {
	unsigned int adc;
	
	while(!(ADCSRA&(1<<ADIF)));
	adc=(ADCL|ADCH<<8);
	ADCSRA|=(1<<ADIF);	//Сброс флага
	return adc;
}

void PWM_ini(void)
{
	time=(float)g/period;
	ocr1c=(float)(t-2*k)*0.8138/2500*4992;
	ocr3c=t4*time;

	//Запускаем таймер 1 в режиме Fast PWM Mode 15
	//Сигналы B и C
	TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)|(1<<COM1C1)|(1<<COM1C0)|(1<<WGM11)|(1<<WGM10);	//	Инверсный "В", инверсный "С"
	TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);		// Устанавливаем делитель=8
	
	//Запускаем таймер 3 в режиме Fast PWM Mode 15
	//Сигналы A и D
	TCCR3A=(1<<COM3A1)|(1<<COM3B1)|(1<<COM3C1)|(1<<WGM31)|(1<<WGM30);	//	инверсный режим для "А3" и инверсный для "B3"
	TCCR3B=(1<<WGM33)|(1<<WGM32)|(1<<CS31);		// Устанавливаем делитель=8
	// Задаем параметры частоты и скважности для таймера 1
	//ШИМ  B
	OCR1B=ocr1b;	// меняем скважность ШИМа на ножке PB6. Запуск АЦП.
	//ШИМ  C
	OCR1C=ocr1c;	// меняем скважность ШИМа на ножке PB7(вывод 13).
	OCR1A=ocr1a;	//меняем период таймера 1
// Задаем параметры частоты и скважности для таймера 3
	//ШИМ  A
	OCR3B=ocr3b;		// меняем скважность ШИМа на ножке PE4
	//ШИМ  D
	OCR3C=ocr3c;
	//Period
	OCR3A=ocr1a;	//меняем период таймера 3
	//Подключаем прерывания по переполнению через TimerOverflow1
	TIMSK1=(1<<TOIE1);
	TIFR1=(1<<TOV1);
}
void PWM_D_ini(void)
{

t4=0;
float konst=1.5;

	if (0<adc0)
	{
		t4=2000;
	}
	else if (0>adc0)
	{
	t4=1750;
	}
	else if (0==adc0)
	{
	t4=1650;// Ничего не делаем.
	}
}

int main(void) {
	DDRB=(1<<PORTB7)|(1<<PORTB6);
	DDRE=(1<<PORTE5)|(1<<PORTE4)|(1<<PORTE3);
	PWM_ini();				//Инициализация ШИМ 
	UART0_Init();			//Инициализация порта на  uART
	ADCInit();	
	 
	sei();
	
	ADCSRA|=(1<<ADSC);
	
	
	while(1) {
	PWM_D_ini(); 		
	}
}

ISR(TIMER1_OVF_vect)
{

   	
}

ISR(ADC_vect)
{
//uint8_t theLOW=ADCL;
//uint16_t ADC_ten_bits=ADCH<<8|theLOW;
uint16_t ADC_ten_bits=ADC;

if(ADMUX==0b01000000)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x0D); //делаем "Enter". "0x0D" - это Enter в ASCII коде
		
		ADMUX=0b01000001;
	}	
else if(ADMUX==0b01000001)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.');
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x20); //делаем "Space". "0x0D" - это Пробел в ASCII коде
		ADMUX=0b01000111;
		adc0=(float) ADC_ten_bits/204.8;
		ADCSRA|=(1<<ADSC);
	}
else if(ADMUX==0b01000111)
	{
	n=(float) ADC_ten_bits/204.8;// 1024/5=204,8
		uart0_tr((unsigned char)n+0x30);
		uart0_tr('.'); 
		uart0_tr((unsigned char)(n*10)%10+0x30);
		uart0_tr((unsigned char)(n*100)%10+0x30);
		uart0_tr((unsigned char)(n*1000)%10+0x30);
		uart0_tr(0x20); //делаем "Space". "0x0D" - это Пробел в ASCII коде
		ADMUX=0b01000000;
		ADCSRA|=(1<<ADSC);
	}
PWM_D_ini();
PWM_ini();				//Инициализация ШИМ 
}


I think, I understood where is the fail...

A little description: for measure ADC, I need to initialize timers to interrupt turning on.

The problem in initialisation sequences, I do it in wrong way. For initialisation of PWM_D I should to write individual function. Am I right?

 

 

 

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

I think you really need to go back to the drawing board......

for a start.... have you enabled floating point support in the environment? As these are 'heavy'  functions on an 8 bit controller they are normally not enabled by default.

I am not sure if the compiler by default will actually support it in calculating constant values as I try to avoid them as much as possible.

 

tip rewrite your program such that it uses uint32 or uint64 instead of float that will make your program a lot better understandable.

 

also not that IIRC int is a 16bit value also in the 8bit world. And you load a number of 16bit values in 8bit registers..... that might cause trouble.

I always deliberately use 'uint8_t'and 'int8_t' (and the 16 bit 32 bit and 64 bit variants of that) for defining variables, that way you can never be confused about the range of a variable, and also can easily double check if you want to write to registers.

now a side note to that is that there are some registers that are 16bit addressable like for instance the adc conversion result. as Clawson already said better use those as then the compiler will always ensure the right writing/reading order is used. ADC & UBRR are just 2 examples I quickly saw in your code.

 

I am not sure what steps you have taken to debug your code or in what way you have actually written your code.

I would suggest you first get your uart up and running and have a number of support functions for that available for debugging.

functions that will be very handy ( not only now but also in the future ) are a write of a byte in hex value so if a register or variable has the hex value of 0x5C  then the uart will spit out '5C' such that you can see at a given point that your variables or registers hold the value you think they have or expect to have. In analogy with that you sometimes also want to write a int (for instance the ADC register as it is 16 bit) you can then easily just 2 times call the byte function and have a int dumped on the uart.

later you might want to also have a way to print a text string to clarify your output, but that is not a direct need if you do not spit out to much data at a time.

 

When you have your UART up and running you could get your ADC up, and see what happens if you manually start a conversion, wait for it to complete and then send the data tot he PC over the uart. then start a new conversion and so on. Perhaps add a little delay to not overflow your mind with numbers as it will al runn very fast on the teminal. Then you can move things from polling base to interrupt base and see it it keeps acting like you want to.

 

then if that all works you can add the timers to actually make the ting run on intervals that you want.

 

looks like a lot of work, but you have done a lot already, so getting things up and working decently should not be to much work.

 

 

 

 

 

 

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

Well, I rewrite the initiasosatopn for PWM chanel D (ocr3c) and now it works!  The problem, as I mentioned before,  was in the sequences of function initiasolation. The initialisaton of PWM chanel D must be after ADC_ini. 

meslomp wrote:

I think you really need to go back to the drawing board......

for a start.... have you enabled floating point support in the environment? As these are 'heavy'  functions on an 8 bit controller they are normally not enabled by default.

I am not sure if the compiler by default will actually support it in calculating constant values as I try to avoid them as much as possible.

=//=

=//=

looks like a lot of work, but you have done a lot already, so getting things up and working decently should not be to much work.

Dear meslops, thank you a lot for yout advice, it's really useful for me, as for beginner!