A single measure of ADC per 1 PWM pulse

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

Hello!

I'm trying to make a single measure of ADC per 1 PWM pulse.

In fact, I use two chanel of ADC, and I need to make a single measure for each of them. But, I thick, it doesn't matter how much chanel I use.

On the picrure I showed the PWM. For ADC initialisation I use the chanel 1: time period - 2.5 ms; pulse width - 2450 ms.

The  ADC has to be measured after rising edge once. The next measure is performed after next rising edge.

This is my code:

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

	if (PORTE5>0)      // PORTE5 is PWM chanel 1
	{
	ADCInit();				//Инициализация АЦП
	}

	while(1) {

	}
}

 

ADC, UART, PWM work, but not the way I want.

How to be sure, that ADC is measured correctry after rising adge?

And how to make the singe measure ADC, not a lot of time measures?

 

I'm sorry if the question is daft. I'm entry-level user

Attachment(s): 

This topic has a solution.
Last Edited: Fri. Dec 27, 2019 - 10:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where's the rest of the code spasibo?

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

Iaz5 wrote:

if (PORTE5>0)  

This is always going to be true -- "PORTE5" is noting but "5".

 

Show the code, at least for PWM init.  Tell which AVR model you are working with.  Then we can see if the PWM setup makes auto-trigger the way to go.

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.

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

The  ADC has to be measured after rising edge

Another question to help sort this out:

 

Within what amount of time after the rising edge do you need to sample the signal?

 

Within ___ microseconds?,  within ___ milliseconds?, …

 

JC 

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

DocJC wrote:
The  ADC has to be measured after rising edge

This maybe a stupid question, but will the ADC not always see a high (5v) level at this time?

 

Jim

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

ki0bk wrote:

This maybe a stupid question, but will the ADC not always see a high (5v) level at this time?

Synchronised with, not measuring...?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Yes, I assumed that the PWM rising edge was the synchronizing signal after which the two ADC channels needed to measure their signals.

My question was how long after the rising edge should / can the ADC perform its measurements.

That impacts how one structures the program, I think.

 

As a separate issue:

Needless to say, with only one ACD channel, and an input signal MUX, the two channel readings won't actually be simultaneously obtained, but will be sequentially obtained, several microseconds apart.

That detail hasn't yet been mentioned as to whether or not it matters. 

 

The PWM signal seems rather slow, compared to some PWM signals, but the OP didn't say what is it driving.

Given that it is measured in mSec, (or 2+ seconds ?), the 13 or so clock cycles for the ADC to read the analog signal and provide the value, after the synch signal, may not matter.

The ADC's sample an hold will capture the signal within 2 clock cycles, (without checking...), after the single shot register bit is triggered.

 

JC

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

Hello!

 

The full 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;


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<<ADEN);
	ADMUX=(1<<MUX2)|(1<<MUX1)|(1<<MUX0)|(1<<REFS0);			//на канал ADC7
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);		//делитель=128
	ADCSRA|=(1<<ADSC);			//Запуск преобразования
	ADCSRA|=(1<<ADATE);
	ADCSRA|=(1<<ADIE);//разрешаем прерывания
	sei();
}

unsigned int adcRead(void) {
	unsigned int adc;
	
	while(!(ADCSRA&(1<<ADIF)));
	adc=(ADCL|ADCH<<8);
	
	ADCSRA|=(1<<ADSC);	//Начать преобразование
	ADCSRA|=(1<<ADIF);	//Сброс флага
	
	return adc;
}


void PWM_ini(void)
{
	//Запускаем таймер 1 в режиме Fast PWM Mode 15
	TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<COM1B0)|(1<<COM1C1)|(1<<WGM11)|(1<<WGM10);	//	неинверсный режим для "А1" и инверсный для "B1"
	TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);		// Устанавливаем делитель=8
	
	
	//Запускаем таймер 3 в режиме Fast PWM Mode 15
	TCCR3A=(1<<COM3A1)|(1<<COM3A0)|(1<<COM3B1)|(1<<COM3B0)|(1<<COM3C1)|(1<<COM3C0)|(1<<WGM31)|(1<<WGM30);	//	инверсный режим для "А3" и инверсный для "B3"
	TCCR3B=(1<<WGM33)|(1<<WGM32)|(1<<CS31);		// Устанавливаем делитель=8
	
	/*
	Частота 16МГц
	Делитель=8
	18МЦг/8=2МГц
	Формула расчета частоты ШИМа приведена на странице 148 в даташите
	Чтобы получить 400 Гц нам надо установить OCR1A в 4999. по факту получилось 4992
	Чтобы задать скважность 40% задаем OCR1C в 40% от значения OCR1A
		*/
	
	// Задаем параметры частоты и скважности для таймера 1
	//ШИМ  A
	OCR1CH=0b00000111;
	OCR1CL=0b11010000;		// меняем скважность ШИМа на ножке PB7
	//ШИМ  D
	OCR1BH=0b00000111;
	OCR1BL=0b00001000;		// меняем скважность ШИМа на ножке PB6
	
	OCR1AH=0b00010011;
	OCR1AL=0b10000000;		//меняем период таймера 1
	
	
	// Задаем параметры частоты и скважности для таймера 3
	//ШИМ  B
	OCR3CH=0b00000000;
	OCR3CL=0b01100100;		// меняем скважность ШИМа на ножке PE5
	//ШИМ  C
	OCR3BH=0b00000111;
	OCR3BL=0b00001000;		// меняем скважность ШИМа на ножке PE4
	
	OCR3AH=0b00010011;
	OCR3AL=0b10000000;		//меняем период таймера 1
	
}



int main(void) {
	DDRB=(1<<PORTB7)|(1<<PORTB6);
	DDRE=(1<<PORTE5)|(1<<PORTE4);
	PWM_ini();				//Инициализация ШИМ 
	UART0_Init();			//Инициализация порта на  uART
	
	//LedOff;
	//PORTB^=0x80;	//Меняем состояние вывода
	
	if (PORTE5>0)
	{
	ADCInit();				//Инициализация АЦП
	}
	
	while(1) {
	
	
		
	}
}
ISR(ADC_vect)
{
uint8_t theLOW=ADCL;
uint16_t ADC_ten_bits=ADCH<<8|theLOW;

	switch (ADMUX)
	{
	case 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;
		break;
		
	case 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=0b01000111;
		break;
	
	default:
		break;
		
	}
	ADCSRA|=(1<<ADSC);
	_delay_ms(100);
}

I'm working with Atmega2560.

 

Dear ki0bk, I measure not the voltage of pwm. I measure the voltage from the middle pin of potenciometer

 

 

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

DocJC wrote:

The  ADC has to be measured after rising edge

Another question to help sort this out:

 

Within what amount of time after the rising edge do you need to sample the signal?

 

Within ___ microseconds?,  within ___ milliseconds?, …

 

JC 

Dear DocJC, as soon as it possible. If it's possible to measure exactly after rising edge it will be excelent

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

As you work with micros it is often helpful to draw a "timing diagram".

This is a one axis graph, time along the X axis, and you mark the timeline when things will take place.

 

As you are reading the voltage of a Pot, and a Pot can only change position very slowly compared to the speed of the micro, it really doesn't matter how many microseconds after the synch pulse's edge the ADC makes the measurement.

 

Remember the micro is (typically) running at millions of instructions per second.

If your micro has a 16 MHz external crystal, for example, then it executes 16 million clock cycles / second, and each instruction uses one, two, or perhaps 3 clock cycles.

Even if it took 50 clock cycles to read the ADC and store it in an array, that would only take 3.125 milliseconds.

 

How much can you turn the Pot in 3 mSec?

 

Bottom line, although you need to synch your readings with the "PWM" signal, it doesn't sound like the time to enter the ADC reading routine, read the value, and then store it, will matter.

 

That is very different from say reading a Ramp Voltage at a certain time.

In that case the voltage might change significantly from the time of the synch trigger signal and the time the ADC's sample and hold actually grabbed the voltage to measure it.

 

I don't use C, so I'll let others help with your code.

I'd start, however, by reading the ADC and outputting the ADC count so you can read it.

You could display it on an LCD, or send the value by USART to a PC Terminal program.

 

Then move the wiper and watch the ADC value run from 0000 to the Full Range Value, (1023 dec for a 10-Bit ADC).

 

Then add a conversion from ADC value to volts, and measure the wiper voltage with a voltmeter, and see what your display says the voltage is.

 

When that works, then work on synchronizing the ADC readings to the PWM signal.

 

JC

 

 

 

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

You cannot measure exactly after because of the delay between the assertion of the conversion start and when the sample/hold switch finally opens, storing the input voltage on an internal capacitor. This time, if I remember for a Mega328, is 1.5 ADC clock periods after starting the conversion. The value will not be available until 11.5 more ADC clocks after that.

 

For an M328, the HIGHEST ADC clock frequency for full resolution is 200KHz.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

DocJC wrote:

Remember the micro is (typically) running at millions of instructions per second.

If your micro has a 16 MHz external crystal, for example, then it executes 16 million clock cycles / second, and each instruction uses one, two, or perhaps 3 clock cycles.

Even if it took 50 clock cycles to read the ADC and store it in an array, that would only take 3.125 milliseconds.

 

How much can you turn the Pot in 3 mSec?

Actually, my final aim is to measure the analog signal with the frequency close to the frequency of the PWM first chanel. I don't have the PCB with this sygnal right now, because of this, I measure the Pot voltage just to be sure, that ADC is work.

DocJC wrote:
I'd start, however, by reading the ADC and outputting the ADC count so you can read it.

I will try! 

ka7ehk wrote:
You cannot measure exactly after because of the delay between the assertion of the conversion start and when the sample/hold switch finally opens, storing the input voltage on an internal capacitor.

I should to measure the signals after rising edge and before trailing edge one time. If I can't  measure it exactly after rising edge - it's ok, just before trailing edge.

 

The main problem - I don't understand the logic of single measurement. Now I use the following code for ADC initialisation:

if (PORTE5>0)
	{
	ADCInit();				//Инициализация АЦП
	}

But how to be sure, that the signal will be measure just once?

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

To measure just once:

 

ADEN = 1

ADATE = 0

ADLAR = 0 for normal right adjust. Set to one only if you intend to read only the high 8 bits.

ADIE = 1 ONLY if use ADC interrupt

ADTS bits don't care since ADATE = 0

 

If you are using a port pin that has both ADC input and digital function, do not forget to disable the digital part using the DIDR0 register.

 

When you want to start the conversion:

ADSC = 0  <- ERROR - improper attempt to edit on an alien Windoze machine (All Windoze machines are alien) resulted in a new post. Sorry

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Dec 25, 2019 - 06:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

To measure just once:

 

ADEN = 1

ADATE = 0

ADLAR = 0 for normal right adjust. Set to one only if you intend to read only the high 8 bits.

ADIE = 1 ONLY if use ADC interrupt

ADTS bits don't care since ADATE = 0

 

If you are using a port pin that has both ADC input and digital function, do not forget to disable the digital part using the DIDR0 register.

 

When you want to start the conversion:

ADSC = 1

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:

To measure just once:

 

ADEN = 1

ADATE = 0

ADLAR = 0 for normal right adjust. Set to one only if you intend to read only the high 8 bits.

ADIE = 1 ONLY if use ADC interrupt

ADTS bits don't care since ADATE = 0

 

If you are using a port pin that has both ADC input and digital function, do not forget to disable the digital part using the DIDR0 register.

 

When you want to start the conversion:

ADSC = 1

Dear ka7ehk, I take into account your comments.

Cheched the code, and that it is what I have right now:

//Инициализируем АЦП
void ADCInit(void) {
	ADCSRA|=(1<<ADEN);
	ADMUX=(1<<MUX2)|(1<<MUX1)|(1<<MUX0)|(1<<REFS0);			//на канал ADC7
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);		//делитель=128
	//ADCSRA|=(1<<ADSC);			//Запуск преобразования
	//ADCSRA|=(1<<ADATE);
	ADCSRA|=(1<<ADIE);//разрешаем прерывания
	DIDR0|=(1<<ADC0D)|(1<<ADC7D);
	sei();
}

Just to be sure, that the program makes a single measure, I changed also the frequency of PWM chanel 1 from 400 Hz  near to 1 Hz (You can see the picture below). 

//Запускаем таймер 3 в режиме Fast PWM Mode 15
	TCCR3A=(1<<COM3A1)|(1<<COM3A0)|(1<<COM3B1)|(1<<COM3B0)|(1<<COM3C1)|(1<<WGM31)|(1<<WGM30);	//	инверсный режим для "А3" и инверсный для "B3"
	TCCR3B=(1<<WGM33)|(1<<WGM32)|(1<<CS32);		// Устанавливаем делитель=256

	// Задаем параметры частоты и скважности для таймера 3
	//ШИМ  B
	OCR3CH=0b11101111;
	OCR3CL=0b01000001;		// меняем скважность ШИМа на ножке PE5
	//ШИМ  C
	OCR3BH=0b00000111;
	OCR3BL=0b00001000;		// меняем скважность ШИМа на ножке PE4

	OCR3AH=0b11110100;
	OCR3AL=0b00100011;		//меняем период таймера 3

}

 

The program still makes a lot of measurement per pulse of PWM width(Chanel 1).

I think, that the proplem in this condition:

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

	if (PORTE5>0)
	{
	ADCSRA|=(1<<ADSC);			//Запуск преобразования
	}
	else
	{
	ADCSRA|=(0<<ADSC);			//Запуск преобразования
	}
	while(1) {
	}
}

After a single measure of ADC , the program check the condition. And PORTE5 is still high level. And again and again.

Ofcourse, I can make a delay after this. But, does it possible to make it in another way?

Attachment(s): 

Last Edited: Wed. Dec 25, 2019 - 09:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
This is always going to be true -- "PORTE5" is noting but "5".

But why? PORTE is output of PWM and in some moments it is in Low level, as far as I know.

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

PINE5 is simply NOT the way you access a port pin. In a standard AVR, there are three port control registers, PORTn, DDRn, and PINn, for each port.

 

To control whether pin 3 in port E is an input or output, you would do

 

DDRE |= (1<<DDRE5);  //make output

DDRE &= ~(1<<DDRE5);  //make input 

 

If pin3 of port E has been set as an output, then you would do

 

PORTE |= (1<<PORTE5);  //make output high

PORTE &= ~(1<<PORTE5); //make output low

 

To read the current logic state if the pin (hint, it is NOT always controlled by DDRn and PORTn), then

 

uint8_t PortE_State;

PortE_State = (1<PINE5) & PINE;

 

So, the short answer is that your statement (PINE5 > 0) does nothing. PINE5 is a constant which has a value of 5 so this statement is ALWAYS true. It provides zero information about the current state of the 5th pin in PORTE. However, (1<<PINE5) can be used as a mask to filter the contents of the register named PINE. THAT will tell you the logical state of the 5th pin of PORTE.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Thu. Dec 26, 2019 - 05:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to take a close look to your answer, I can't to understand this right now.

By the way, thank you! This is useful pointer!

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

Should have added (for standard Mega & Tiny AVRs):

 

1) AVR port registers are ALWAYS 8 bits

 

2) AVR ports always have 8 or fewer pins

 

3) AVR port pins are ALWAYS numbered 0 through 7 with the possibility that some may be missing.

 

4) AVR ports registers always have the same arrangement. They come in sets of three, PORTn, DDRn, and PINn. The 0 bit of PORTn controls pin 0 for that port. 0 bit of DDRn control the direction of pin 0 for that port. 0 bit of PINn reads the state of pin 0 for that port. This carries through all 8 bits of all 8 registers, unless a pin is not implemented.

 

There is a whole section of the M2560 manual that describes this in great detail. It is to your advantage to READ it!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Iaz5 wrote:

But why? PORTE is output of PWM and in some moments it is in Low level, as far as I know.

Sort of.

 

PORTE is a macro which references the 'PORT' I/O register for port 'E'.

 

PORTE5 is a macro which evaluates to the bit number 5, not its current value.

 

In order to manipulate the value of a specific bit in a register (or indeed, in any variable), you must use a bit mask.  A mask can be constructed from a bit number:

(1 << PORTE5)

Since PORTE5 is just a macro which evaluates to 5, this becomes:

(1 << 5)

... which just shifts the value '1' to the left by 5 bits, so this becomes:

0b00100000

... which you'll note is bit position 5 (note also that I've used the 0b prefix for binary numbers, which is a non-standard GCC extension).

 

When you want to test a bit in an I/O register, you use bit-wise 'and' against the mask constructed from that bit number:

PORTE & (1 << PORTE5)

If bit 5 in PORTE is set (i.e. '1'), then the value of that expression will be non-zero i.e. true.  If bit 5 is clear (i.e. '0'), the value will be zero i.e. false.

 

 

When you want to set that bit in an I/O register, you use bit-wise 'or' with the mask:

PORTE |= (1 << PORTE5);

When you want to clear that bit in an I/O register, you use bit-wise 'and' against the one's complement of the mask:

PORTE &= ~(1 << PORTE5);

 

In fact, we see you correctly using these constructs in your code in the OP:

	DDRB=(1<<PORTB7)|(1<<PORTB6);
	DDRE=(1<<PORTE5)|(1<<PORTE4);

 

However, later you try:

	if (PORTE5>0)      // PORTE5 is PWM chanel 1

Remember that PORTE5 is just a macro that evaluates to 5, so this becomes:

	if (5>0)

... and since 5 is always greater than 0, the 'if' will always be true.

 

To test a bit, you must use a mask as described above:

	if (PORTE & (1 << PORTE5))

 

The 'if' will succeed if bit 5 in PORTE is 1.

 

However, that's not the only thing wrong with that line.  PORTE is the I/O register which controls the port's output, it does not reflect the level on the pin, and is not associated in any way with the PWM module of the TIMER controlling that pin.

 

You want the input register for port 'E', which is PINE:

	if (PINE & (1 << PINE5))

This will read the level on the pin, regardless of whether that level is due to an external signal, or the PWM module, or any other internal driver (i.e. UART, etc.)

 

This is all just basic bit manipulation and standard C, and the AVR stuff is right out of the datasheet.  If you're struggling with this, try the bit manipulations tutorial here on AVR Freaks.

 

EDIT:  Typos and clarification

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Dec 26, 2019 - 07:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

joeymorin wrote:
If you're struggling with this, try the bit manipulations tutorial here on AVR Freaks.

Here: https://www.avrfreaks.net/forum/...

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

This is a straigghtforward information, thank you! Now I see the difference.

Just for note:

I read in datasheet, that I can use the interrupt for rising edge detection. As I understood, it is possible to use Timer/Counter Overflow Flag. May be, using of interrups is more correct for this task..

By the way, thank you again!

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

Why not use that timer to auto-trigger the ADC? That is what triggering is all about and the ADATE flag plus ADTS bits.

 

Then you could use the A/D Conversion Complete interrupt to signal when the new value is ready.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:
That is what triggering is all about and the ADATE flag plus ADTS bits.

It works!

But I absolutely befuddled.

This is the 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;

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<<ADEN);
	ADMUX=(1<<MUX2)|(1<<MUX1)|(1<<MUX0)|(1<<REFS0);			//на канал ADC7
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);		//делитель=128
	//ADCSRA|=(1<<ADSC);			//Запуск преобразования
	ADCSRA|=(1<<ADATE);
	ADCSRA|=(1<<ADIE);//разрешаем прерывания
	DIDR0|=(1<<ADC0D)|(1<<ADC7D);
	
}

unsigned int adcRead(void) {
	unsigned int adc;
	
	while(!(ADCSRA&(1<<ADIF)));
	adc=(ADCL|ADCH<<8);
	
	//ADCSRA|=(1<<ADSC);	//Начать преобразование
	ADCSRA|=(1<<ADIF);	//Сброс флага
	
	return adc;
}


void PWM_ini(void)
{
	/*
	Функции ШИМ-сигналов
	ШИМ B - запускает АЦП по переднему фронту.
	
	*/
	
	//Запускаем таймер 1 в режиме Fast PWM Mode 15
	TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<COM1B0)|(1<<COM1C1)|(1<<WGM11)|(1<<WGM10);	//	неинверсный режим для "А1" и инверсный для "B1"
	TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS12);		// Устанавливаем делитель=8
	
	
	//Запускаем таймер 3 в режиме Fast PWM Mode 15
	TCCR3A=(1<<COM3A1)|(1<<COM3A0)|(1<<COM3B1)|(1<<COM3B0)|(1<<COM3C1)|(1<<WGM31)|(1<<WGM30);	//	инверсный режим для "А3" и инверсный для "B3"
	TCCR3B=(1<<WGM33)|(1<<WGM32)|(1<<CS32);		// Устанавливаем делитель=256
	
	/*
	Частота 16МГц
	Делитель=8
	18МЦг/8=2МГц
	Формула расчета частоты ШИМа приведена на странице 148 в даташите
	Чтобы получить 400 Гц нам надо установить OCR1A в 4999. по факту получилось 4992
	Чтобы задать скважность 40% задаем OCR1C в 40% от значения OCR1A
		*/
	
	// Задаем параметры частоты и скважности для таймера 1
	//ШИМ  A
	OCR1CH=0b11101111;
	OCR1CL=0b01000001;		// меняем скважность ШИМа на ножке PB7
	//ШИМ  D
	OCR1BH=0b00000111;
	OCR1BL=0b00001000;		// меняем скважность ШИМа на ножке PB6
	
	OCR1AH=0b11110100;
	OCR1AL=0b00100011;		//меняем период таймера 1
	
	
	// Задаем параметры частоты и скважности для таймера 3
	//ШИМ  B
	OCR3BH=0b11101111;
	OCR3BL=0b01000001;		// меняем скважность ШИМа на ножке PE4
	//ШИМ  C
	OCR3CH=0b00000111;
	OCR3CL=0b00001000;		// меняем скважность ШИМа на ножке PE5
	
	OCR3AH=0b11110100;
	OCR3AL=0b00100011;		//меняем период таймера 3
	
	//Подключаем прерывания для канала B 
	TIMSK1=(1<<TOIE1);
	TIFR1=(1<<TOV1);

}



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

ISR(TIMER1_OVF_vect)
{
ADCSRA|=(1<<ADSC);
uint8_t theLOW=ADCL;
uint16_t ADC_ten_bits=ADCH<<8|theLOW;

	switch (ADMUX)
	{
	case 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;
		break;
		
	case 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=0b01000111;
		break;
	
	default:
		break;
		
	}
	ADCSRA|=(1<<ADSC);
	//_delay_ms(100);
}

ISR(ADC_vect)
{

}


ADC is triggered by rising edge of PWM signal. And that's about right. BUT:

I didn't touch the ADTS bits, and it means, that I use  "free runing mode", not the "Timer/Counter1 Overflow" as trigger source. I use exactly this timer for interrupt. HOW DOES IT WORK?:D

 

And one more question: Now it makes a single measure of ADC per period, but just one chanel of ADC. How to measure ALL ADC chanel once per period?