Skocz do zawartości
Keadwen

[C] ADC + algorytm P

Pomocna odpowiedź

Witam,
w wolnej chwili stworzyłem program oparty na algorytmie P. Kod jest stworzony na wzór ostatniego poradnika o PID. Po wgraniu softu robot kompletnie nie klei się linii.

W 99% jestem pewny, że błąd leży po stronie odczytu wartości z ADC.

Na dole znajduje się kod.

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

// ***** CZUJNIKI CNY70 *****
#define czujnik4P	(1<<PA7)
#define czujnik3P	(1<<PA6)
#define czujnik2P	(1<<PA5)
#define czujnik1P	(1<<PA4)
#define czujnik1L	(1<<PA3)
#define czujnik2L	(1<<PA2)
#define czujnik3L	(1<<PA1)
#define czujnik4L	(1<<PA0)

// *** SILNIK 1 (LEWY)
#define moto1_A		(1<<PC0)
#define moto1_B		(1<<PC1)
#define moto1_EN	(1<<PD4)
#define moto1_jazda		PORTC |= moto1_A;	PORTC &= ~(moto1_B)
#define moto1_cofka		PORTC &= ~(moto1_A); PORTC |= moto1_B
#define moto1_stop		PORTC &= ~(moto1_A | moto1_B)

// *** SILNIK 2 (PRAWY)
#define moto2_A		(1<<PC2)
#define moto2_B		(1<<PC3)
#define moto2_EN	(1<<PD5)
#define moto2_cofka		PORTC |= moto2_A;	PORTC &= ~(moto2_B)
#define moto2_jazda		PORTC &= ~(moto2_A); PORTC |= moto2_B
#define moto2_stop		PORTC &= ~(moto2_A | moto2_B)

// *** Wartości dodatkowe
#define wart_graniczna			750
#define predkosc_srodkowa		130
#define wart_regulacji_pwm		30

// *** Deklaracje zmiennych
uint16_t pomiar(uint8_t czujnik);
uint16_t wynik(uint8_t kanal);
uint8_t wart_pomiaru, liczba_czujnikow;
int8_t cz4L, cz3L, cz2L, cz1L, cz1P, cz2P, cz3P, cz4P;
int8_t error, regulacja_pwm;

int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L | czujnik3L | czujnik2L | czujnik1L
		| czujnik1P | czujnik2P | czujnik3P | czujnik4P);
DDRC |= (moto1_A | moto1_B | moto2_A | moto2_B);
DDRD |= (moto1_EN | moto2_EN);

// *** TIMER1
TCCR1A |= (1<<COM1A1)|(1<<COM1B1) ;		// Compare Output Mode, Fast PWM
TCCR1A |= (1<<WGM10) | (1<<WGM11);		// Fast PWM 10bit
	TCCR1B |= (1<<WGM12);
TCCR1B |= (1<<CS10) | (1<<CS11);        // Preksaler = 64 	 //fpwm = 980Hz
TCNT1 = 1024;
moto1_jazda;			// ROZRUCH - SILNIK LEWY
moto2_jazda;			// ROZRUCH - SILNIK PRAWY

// *** ADC
ADMUX |= (1<<REFS0);	// AVCC with external capacitor at AREF pin
ADCSRA |= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Włączenie ADC, preskaler = 128, fp=125kHz

while(1)
{
	// *** PRZETWARZANIE WART. ANALOGOWYCH NA CYFROWE.  NAPIECIE WYJ > 3,4V
	if( pomiar(0) > wart_graniczna )
	{
		cz4L = -30;				// Wartości x10 - ucieczka od zmiennej float
		liczba_czujnikow++;
	}
	if( pomiar(1) > wart_graniczna )
	{
		cz3L = -20;
		liczba_czujnikow++;
	}
	if( pomiar(2) > wart_graniczna )
	{
		cz2L = -10;
		liczba_czujnikow++;
	}
	if( pomiar(3) > wart_graniczna )
	{
		cz1L = 0;
		liczba_czujnikow++;
	}
	if( pomiar(4) > wart_graniczna )
	{
		cz1P = 0;
		liczba_czujnikow++;
	}
	if( pomiar(5) > wart_graniczna )
	{
		cz2P = 10;
		liczba_czujnikow++;
	}
	if( pomiar(6) > wart_graniczna )
	{
		cz3P = 20;
		liczba_czujnikow++;
	}
	if( pomiar(7) > wart_graniczna )
	{
		cz4P = 30;
		liczba_czujnikow++;
	}

	// Suma wartości z odczytanych czujników przekraczających wart_graniczna = 7
	error = cz4L + cz3L + cz2L + cz1L + cz1P + cz2P + cz3P + cz4P;
	// Error podzielony przez ilosc czujników
	error = error / liczba_czujnikow;
	// Wyliczanie wartości regulującej OCR1A/B
	regulacja_pwm = wart_regulacji_pwm * error;
	// Przypisanie nowych wartosci bajtom OCR1A/B
	OCR1A = predkosc_srodkowa + regulacja_pwm;
	OCR1B = predkosc_srodkowa - regulacja_pwm;
	// Reset erroru i wartosci czujników
	error = 0;
	liczba_czujnikow = 0;

}
}

uint16_t pomiar(uint8_t czujnik)
{
ADMUX |= czujnik;
ADCSRA |= (1<<ADSC);
while(ADCSRA & ADSC)
return ADCW;
}

Moje pytanie:

1. Czy w line followerze wykorzystać pomiar ADC typu free-run, czy wykorzystać przerwania w celach pomiaru.

Nie posiadam jeszcze doświadczenia w tej materii, więc liczę na pomoc.

Pozdrawiam

Keadwen

Udostępnij ten post


Link to post
Share on other sites

"ADMUX |= czujnik;"

To oznacza, że zaczynając od ustawienia 0 masz:

0 | 1 = 1 (OK)

1 | 10 = 11 (zamiast 10)

11 | 11 = 11 (OK)

11 | 100 = 111 (źle)

111 | 101 = 111 (źle)

itd.

Lepiej wykorzystać przerwania. Przy obecnym rozwiązaniu prawidłowo sprawdzasz tylko góra 2-3 pomiary a potem ciągle ten sam kanał.

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Patrząc w notę katalogową Atmega16 str. 218 Tabla 84 widzę konfigurację bitów MUX4:0 bajtu ADMUX.

Jeżeli wykorzystam zapis:

ADMUX |= czujnik;

Uzyskam na czterech (w moim wypadku trzech) bitach odpowiednie ustawienie, powodujące odczytanie wartości z danego kanału i przypisanie tego do uint16_t pomiar.

Wydaje mi się, że ta linia źródła jest poprawna, a błąd siedzi gdzieś indziej.

Zastanawiam się nad wykorzystaniem przerwania, niestety nie miałem z pomiarem na żądanie styczności, ale sądzę, że podołam zadaniu.

Udostępnij ten post


Link to post
Share on other sites

Oj, podstawy logiki się kłaniają. OldSkull wytłumaczył Ci Twój błąd, więc ja załączę tabelkę odpowiednią do działania które używasz |= czyli OR:

Innymi słowy po paru pomiarach na bitach MUX będziesz miał same jedynki - dlaczego?

Może to Ci pomoże - patrz pkt 1.5: http://mikrokontrolery.blogspot.com/2011/04/problemy-c-ustawianie-i-zerowanie-bitow.html

Przerwanie jest proste - próbuj pomożemy 🙂

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Haha no dobra. Dondu podstawy logiki znam, ale wpadłem teraz w taką śmiechawę, że mnie szczęka boli. Widzę teraz ten błąd, ale dopiero jak sobie to na kartce rozpisałem. Ale ważne, że mamy progress 😅

Jutro z rana, na świeżo biorę się za data sheeta z przerwaniem ADC.

Dobrej nocki życzę.

Udostępnij ten post


Link to post
Share on other sites
Haha no dobra. Dondu podstawy logiki znam ...

To był oczywiście żart, tylko zapomniałem dodać ikonkę 😃

Powodzenia w dalszych pracach!

Udostępnij ten post


Link to post
Share on other sites

Witam ponownie,
przesiedziałem troszkę już nad kartą katalogową i wydaje mi się, że dobrze rozumiem "co autor miał na myśli".

Do kodu wprowadziłem kilka linijek:

// *** TIMER0 - COMPARE MATCH dla ADC
TCCR0 |= (1<<WGM01) | (1<<COM01);	// CTC, Clear OC0 on compare match
TCCR0 |= (1<<CS00);					// preskaler = 1
OCR0 = 160;							// Compare Match (zbocze opadające) na wart. 160
TIMSK |= (1<<OCIE0);

// *** ADC
ADMUX |= (1<<REFS0);							// AVCC with external capacitor at AREF pin
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE);	// Włączenie ADC, Auto Trigger Enable, Interrupt Enable
ADCSRA |= (1<<ADIF);								// ADC Interrupt Flag
//	ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // preskaler = 128, fp=125kHz
SFIOR |= (1<<ADTS1) | (1<<ADTS0);				// Przerwanie - Compare Match Timer0

sei();		// Globalne zezwolenie na przerwanie

Mam kilka pytań teraz:

1. Wrzuciłem w komentarz linijkę z ustawieniami preskalera. Wydaje mi się, że w przypadku pomiaru na żądanie jest to zbędne. Czy dobrze wynioskowałem?

2. Wybrałem opcję przerwania Timer0 CTC, jednak w kodzie mam wykorzystać TIMER0_COMP_vect czy ADC_vect?

3. Dondu czy na twojej stronie jest to może opisane?

Udostępnij ten post


Link to post
Share on other sites

Jak rozumie Twoim uC jest Atmega16.

Nie podajesz z jaką częstotliwością pracuje uC - zgaduję 16MHz? Jeżeli tak to przekraczasz parametry ADC - za szybko go taktujesz. Wnioskuję stąd, że w kodzie piszesz Fp=125kHz i preskaler 128. Licząc od tyłu daje to 16MHz 🙂

Na blogu w tym temacie możesz znaleźć opracowywany aktualnie przeze mnie pierwszy artykuł dot. Efektywnego Planowania Projektu, a w nim opisany sposób doboru parametrów uC dot. ADC i zegara uC: http://mikrokontrolery.blogspot.com/2011/03/epp-projekt-1.html

Uwaga!

Na dzień gdy piszę ten post, artykuł ten nie jest jeszcze dokończony oraz mogą być w nim błędy.

Jeżeli ktoś chce otrzymywać informacje o nowych artykułach w tym cyklu, to może się wpisać na listę mailingową

Odnośnie wektorów przerwań: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Niestety dopiero wieczorem znajdę czas by przyglądnąć się reszcie - najlepiej gdybyś pokazał cały kod chyba, że jest bardzo obszerny.

Udostępnij ten post


Link to post
Share on other sites

Poniżej znajduje się aktualna wersja zmajstrowanego kodu. Kompiluje się, ale nie działa. Niestety z przerwaniami nie mam dużego doświadczenia. Prawdę mówiąc, z większych projektów C to mój pierwszy, więc ciężko mówić tutaj o jakimkolwiek doświadczeniu bojowym.

Kwarc 16MHz, taktowanie ADC 125kHz.

Dlaczego 125kHz, otóż na str. 207 jest zdanie

"By default, the successive approximation circuitry requires an input clock frequency between 50kHz and 200 kHz to get maximum resolution."

Zmniejszenie częstotliwości, wiąże się ze zmianą częstotliwości kwarca, gdyż preskaler jest na max = 128.

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

// ***** CZUJNIKI CNY70 *****
#define czujnik4P	(1<<PA7)
#define czujnik3P	(1<<PA6)
#define czujnik2P	(1<<PA5)
#define czujnik1P	(1<<PA4)
#define czujnik1L	(1<<PA3)
#define czujnik2L	(1<<PA2)
#define czujnik3L	(1<<PA1)
#define czujnik4L	(1<<PA0)

// *** SILNIK 1 (LEWY)
#define moto1_A		(1<<PC0)
#define moto1_B		(1<<PC1)
#define moto1_EN	(1<<PD4)
#define moto1_jazda		PORTC |= moto1_A;	PORTC &= ~(moto1_B)
#define moto1_cofka		PORTC &= ~(moto1_A); PORTC |= moto1_B
#define moto1_stop		PORTC &= ~(moto1_A | moto1_B)

// *** SILNIK 2 (PRAWY)
#define moto2_A		(1<<PC2)
#define moto2_B		(1<<PC3)
#define moto2_EN	(1<<PD5)
#define moto2_cofka		PORTC |= moto2_A;	PORTC &= ~(moto2_B)
#define moto2_jazda		PORTC &= ~(moto2_A); PORTC |= moto2_B
#define moto2_stop		PORTC &= ~(moto2_A | moto2_B)

// *** Wartości dodatkowe
#define wart_graniczna			750
#define predkosc_srodkowa		130
#define wart_regulacji_pwm		30

// *** Deklaracje zmiennych
volatile uint16_t pomiar(volatile uint8_t czujnik);
uint16_t wynik(uint8_t kanal);
volatile uint8_t wart_pomiaru;
int8_t cz1L, cz2L, cz3L, cz4L, cz1P, cz2P, cz3P, cz4P;
int8_t error, regulacja_pwm, liczba_czujnikow;

int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L | czujnik3L | czujnik2L | czujnik1L
		| czujnik1P | czujnik2P | czujnik3P | czujnik4P);
DDRC |= (moto1_A | moto1_B | moto2_A | moto2_B);
DDRD |= (moto1_EN | moto2_EN);

// *** TIMER1
TCCR1A |= (1<<COM1A1)|(1<<COM1B1) ;		// Compare Output Mode, Fast PWM
TCCR1A |= (1<<WGM10) | (1<<WGM11);		// Fast PWM 10bit
	TCCR1B |= (1<<WGM12);
TCCR1B |= (1<<CS10) | (1<<CS11);        // Preksaler = 64 	 //fpwm = 980Hz
TCNT1 = 1024;
moto1_jazda;			// ROZRUCH - SILNIK LEWY
moto2_jazda;			// ROZRUCH - SILNIK PRAWY

// *** TIMER0 - dla ADC
TCCR0 |= (1<<WGM01) | (1<<COM01);	// CTC, Clear OC0 on compare match
TCCR0 |= (1<<CS00);					// preskaler = 1
OCR0 = 160;							// Compare Match (zbocze opadające) na wart. 160
TIMSK |= (1<<OCIE0);

// *** ADC
ADMUX |= (1<<REFS0);							// AVCC with external capacitor at AREF pin
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE);	// Włączenie ADC, Auto Trigger Enable, Interrupt Enable
ADCSRA |= (1<<ADIF);								// ADC Interrupt Flag
//	ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // preskaler = 128, fp=125kHz
SFIOR |= (1<<ADTS1) | (1<<ADTS0);				// Przerwanie - Compare Match Timer0

sei();		// Globalne zezwolenie na przerwanie

while(1)
{

	// *** PRZETWARZANIE WART. ANALOGOWYCH NA CYFROWE.  NAPIECIE WYJ > 3,4V
	if( cz4L )
	{
		cz4L = -30;				// Wartości x10 - ucieczka od zmiennej float
		liczba_czujnikow++;
	}
	if( cz3L )
	{
		cz3L = -20;
		liczba_czujnikow++;
	}
	if( cz2L )
	{
		cz2L = -10;
		liczba_czujnikow++;
	}
	if( cz1L )
	{
		cz1L = 0;
		liczba_czujnikow++;
	}
	if( cz1P )
	{
		cz1P = 0;
		liczba_czujnikow++;
	}
	if( cz2P )
	{
		cz2P = 10;
		liczba_czujnikow++;
	}
	if( cz3P )
	{
		cz3P = 20;
		liczba_czujnikow++;
	}
	if( cz4P )
	{
		cz4P = 30;
		liczba_czujnikow++;
	}


	// Suma wartości z odczytanych czujników przekraczających wart_graniczna = 7
	error = cz4L + cz3L + cz2L + cz1L + cz1P + cz2P + cz3P + cz4P;
	// Error podzielony przez ilosc czujników
	error = error / liczba_czujnikow;
	// Wyliczanie wartości regulującej OCR1A/B
	regulacja_pwm = wart_regulacji_pwm * error;
	// Przypisanie nowych wartosci bajtom OCR1A/B
	OCR1A = predkosc_srodkowa + regulacja_pwm;
	OCR1B = predkosc_srodkowa - regulacja_pwm;
	// Reset erroru i wartosci czujników
	error = 0;
	liczba_czujnikow = 0;

}
}

//uint16_t pomiar(uint8_t czujnik)
//{
//	ADMUX |= (ADMUX & 0xF8) | czujnik;		// maskowanie + ustawienie bitów MUX4:0
//	ADCSRA |= (1<<ADSC);					// ustawienie flagi startu pomiaru
//	while(ADCSRA & ADSC)					// oczekiwanie na zakończenie pomiaru
//	return ADCW;							// przepisz wynik do uint16_t pomiar
//}

// ***** OBSŁUGA PRZERWANIA TIMER/COUNTER0 CTC *****
ISR(TIMER0_COMP_vect)
{
static int cnt;
       cnt = 0;

for(cnt=0; cnt<8 ; cnt++)
{

	ADMUX |= (ADMUX & 0xF8) | cnt;			// maskowanie + ustawienie bitów MUX4:0
	ADCSRA |= (1<<ADSC);					// ustawienie flagi startu pomiaru
	while(ADCSRA & ADSC)					// oczekiwanie na zakończenie pomiaru
	wart_pomiaru = ADCW;
	if(wart_pomiaru > wart_graniczna)
	{
		wart_pomiaru = 1;
		if(cnt==0) cz4L=wart_pomiaru;
		if(cnt==1) cz3L=wart_pomiaru;
		if(cnt==2) cz2L=wart_pomiaru;
		if(cnt==3) cz1L=wart_pomiaru;
		if(cnt==4) cz1P=wart_pomiaru;
		if(cnt==5) cz2P=wart_pomiaru;
		if(cnt==6) cz3P=wart_pomiaru;
		if(cnt==7) cz4P=wart_pomiaru;
	}
}
}

Udostępnij ten post


Link to post
Share on other sites

Oj, źle zinterpretowałem Twój komentarz. Oczywiście 125kHz jako sygnał zegarowy ADC jest prawidłowe.

Co do kodu, a właściwie wersji podejścia do sprawy ADC.

ADC możesz ustawić tak, że:

1. inicjujesz ADC

2. gdy ADC zakończy pomiar zgłasza przerwanie, które wykorzystujesz do:

2.1 odczytania ADC i zapisania do zmiennej w RAM

2.2 zmiany kanału pomiarowego ADC

2.3 zainicjowania nowego pomiaru

2.3 ustawienia flagi dla programu głównego, że w RAM są nowe dane.

W ten sposób nie musisz używać, żadnego timera i czekać w pętli while() na zgaszenie flagi ADSC.

Zwalniasz w ten sposób moc obliczeniową uC, którą możesz wykorzystać do innych celów. I program staje się bardziej przejrzysty.

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Rozumiem działanie twojej rozpiski, jednak mam problem z przekształceniem tego w kod. Powalczę jeszcze kilka godzin.

Na tą chwilę doszedłem do tego, że bit ADIF ustawia się na 1, gdy zakończy się pomiar.

Czyli "gdy ADC zakończy pomiar zgłasza przerwanie"

to wykorzystuje ISR(ADC_vect)?

Udostępnij ten post


Link to post
Share on other sites
Rozumiem działanie twojej rozpiski, jednak mam problem z przekształceniem tego w kod. Powalczę jeszcze kilka godzin.

Na tą chwilę doszedłem do tego, że bit ADIF ustawia się na 1, gdy zakończy się pomiar.

Czyli "gdy ADC zakończy pomiar zgłasza przerwanie"

to wykorzystuje ISR(ADC_vect)?

ADC_vect - tak.

ADIF - tak, ale Ciebie nie interesuje, ponieważ od razu wywoła się przerwanie, które go zgasi.

Rozpisałem Ci algorytm - spróbuj i wrzuć co Ci wyszło tutaj 🙂

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

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

// ***** CZUJNIKI CNY70 *****
#define czujnik4P	(1<<PA7)
#define czujnik3P	(1<<PA6)
#define czujnik2P	(1<<PA5)
#define czujnik1P	(1<<PA4)
#define czujnik1L	(1<<PA3)
#define czujnik2L	(1<<PA2)
#define czujnik3L	(1<<PA1)
#define czujnik4L	(1<<PA0)

// *** SILNIK 1 (LEWY)
#define moto1_A		(1<<PC0)
#define moto1_B		(1<<PC1)
#define moto1_EN	(1<<PD4)
#define moto1_jazda		PORTC |= moto1_A;	PORTC &= ~(moto1_B)
#define moto1_cofka		PORTC &= ~(moto1_A); PORTC |= moto1_B
#define moto1_stop		PORTC &= ~(moto1_A | moto1_B)

// *** SILNIK 2 (PRAWY)
#define moto2_A		(1<<PC2)
#define moto2_B		(1<<PC3)
#define moto2_EN	(1<<PD5)
#define moto2_cofka		PORTC |= moto2_A;	PORTC &= ~(moto2_B)
#define moto2_jazda		PORTC &= ~(moto2_A); PORTC |= moto2_B
#define moto2_stop		PORTC &= ~(moto2_A | moto2_B)

// *** Wartości dodatkowe
#define wart_graniczna			750
#define predkosc_srodkowa		130
#define wart_regulacji_pwm		30

// *** Deklaracje zmiennych
volatile uint8_t wart_pomiaru;
int8_t cz1L, cz2L, cz3L, cz4L, cz1P, cz2P, cz3P, cz4P;
int8_t error, regulacja_pwm, liczba_czujnikow;


int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L | czujnik3L | czujnik2L | czujnik1L
		| czujnik1P | czujnik2P | czujnik3P | czujnik4P);
DDRC |= (moto1_A | moto1_B | moto2_A | moto2_B);
DDRD |= (moto1_EN | moto2_EN);

// *** TIMER1
TCCR1A |= (1<<COM1A1)|(1<<COM1B1) ;		// Compare Output Mode, Fast PWM
TCCR1A |= (1<<WGM10) | (1<<WGM11);		// Fast PWM 10bit
	TCCR1B |= (1<<WGM12);
TCCR1B |= (1<<CS10) | (1<<CS11);        // Preksaler = 64 	 //fpwm = 980Hz
TCNT1 = 1024;


// *** TIMER0 - dla ADC
TCCR0 |= (1<<WGM01) | (1<<COM01);	// CTC, Clear OC0 on compare match
TCCR0 |= (1<<CS00);					// preskaler = 1
OCR0 = 160;							// Compare Match (zbocze opadające) na wart. 160
TIMSK |= (1<<OCIE0);

// *** ADC
ADMUX |= (1<<REFS0);							// AVCC with external capacitor at AREF pin
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE);	// Włączenie ADC, Auto Trigger Enable, Interrupt Enable
ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // preskaler = 128, fp=125kHz
SFIOR |= (1<<ADTS1) | (1<<ADTS0);				// Przerwanie - Compare Match Timer0


moto1_jazda;			// ROZRUCH - SILNIK LEWY
moto2_jazda;			// ROZRUCH - SILNIK PRAWY

sei();		// Globalne zezwolenie na przerwanie

while(1)
{
	// *** PRZETWARZANIE WART. ANALOGOWYCH NA CYFROWE.  NAPIECIE WYJ > 3,4V
	if( cz4L )
	{
		cz4L = -30;				// Wartości x10 - ucieczka od zmiennej float
		liczba_czujnikow++;
	}
	if( cz3L )
	{
		cz3L = -20;
		liczba_czujnikow++;
	}
	if( cz2L )
	{
		cz2L = -10;
		liczba_czujnikow++;
	}
	if( cz1L )
	{
		cz1L = 0;
		liczba_czujnikow++;
	}
	if( cz1P )
	{
		cz1P = 0;
		liczba_czujnikow++;
	}
	if( cz2P )
	{
		cz2P = 10;
		liczba_czujnikow++;
	}
	if( cz3P )
	{
		cz3P = 20;
		liczba_czujnikow++;
	}
	if( cz4P )
	{
		cz4P = 30;
		liczba_czujnikow++;
	}


	// Suma wartości z odczytanych czujników przekraczających wart_graniczna = 7
	error = cz4L + cz3L + cz2L + cz1L + cz1P + cz2P + cz3P + cz4P;
	// Error podzielony przez ilosc czujników
	error = error / liczba_czujnikow;
	// Wyliczanie wartości regulującej OCR1A/B
	regulacja_pwm = wart_regulacji_pwm * error;
	// Przypisanie nowych wartosci bajtom OCR1A/B
	OCR1A = predkosc_srodkowa + regulacja_pwm;
	OCR1B = predkosc_srodkowa - regulacja_pwm;
	// Reset erroru i wartosci czujników
	error = 0;
	liczba_czujnikow = 0;

}
}

// ***** OBSŁUGA PRZERWANIA TIMER/COUNTER0 CTC *****
ISR(ADC_vect)
{
static uint8_t kanal;				        // zmienna dla bitów MUX4:0
ADMUX |= (ADMUX & 0xF8) | kanal;		// Maskowanie bitów + ustawienie kanału
_delay_us(13);							// Przerwa na odczyt
wart_pomiaru = ADCW;					// Przepisanie wartosci ADCW do zmiennej
if(wart_pomiaru > wart_graniczna)		// Przyrownanie pomiaru do wart_granicznej = 750
{
	wart_pomiaru = 1;					// Przekszałcenie wyniku na wartość binarną
	if(kanal==0) cz4L=wart_pomiaru;		// Wybór zmiennej w zależności od mierzonego kanału
	if(kanal==1) cz3L=wart_pomiaru;
	if(kanal==2) cz2L=wart_pomiaru;
	if(kanal==3) cz1L=wart_pomiaru;
	if(kanal==4) cz1P=wart_pomiaru;
	if(kanal==5) cz2P=wart_pomiaru;
	if(kanal==6) cz3P=wart_pomiaru;
	if(kanal==7) cz4P=wart_pomiaru;
}
if(kanal<7) kanal=0;					// Reset kanalu po przekroczeniu ADC7
}

Nie mam pomysłu jak wprowadzić flagę. To jest ostatnia wersja na dzisiaj, gdyż mój umysł odmawia posłuszeństwa :-> Mam wrażenie, że utknąłem w szablonie myślowym i ciągle piszę to samo. Postęp minimalny, ale zawsze jakiś.

P.S. Piwko za pomoc, mam nadzieje, że jutro też łykniesz 😃

Udostępnij ten post


Link to post
Share on other sites
Mam wrażenie, że utknąłem w szablonie myślowym i ciągle piszę to samo.

Tak właśnie jest, ale to nie jest dziwne ponieważ pisałeś że z przerwaniami dopiero pierwsze kroki robisz 🙂

Ale progress jest - to widać!

Najpierw procedura przerwania:

1. Podałem Ci dokładnie w jakiej kolejności co ma zrobić. Ty zrobiłeś inną kolejność.

2. Z powodu innej kolejności musiałeś się ratować wstawiając w procedurzez przerwania opóźnienie - NIGDY TAK NIE RÓB: http://mikrokontrolery.blogspot.com/2011/04/problemy-c-przerwania.html

Ten link mogłem Ci podać wcześniej - nie pomyślałem - sorry.

3. Zamiast IF-ów zastosuj tablicę indeksowaną zmienną KANAL. To także wpłynie na pozostałą część Twojego kodu, więc masz pracę na trochę więcej niż 5 minut.

Pozostałe uwagi:

Czy aby na pewno nie masz warningów podczas kompilacji: http://mikrokontrolery.blogspot.com/2011/04/bledy-kompilacji-programu.html

Po co Timer0 wraz z przerwaniem ustawiasz skoro funkcji przerwania nie masz zdefiniowanej w kodzie? W takim przypadku wynik już pierwszego przerwania może, a nawet na pewno puści program w maliny.

Już jesteś bardzo blisko rozwiązania Twojego problemu 🙂

Po gruntownych zmianach wrzuć znowu całość.

Udostępnij ten post


Link to post
Share on other sites
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// ***** CZUJNIKI CNY70 *****
#define czujnik4P	(1<<PA7)
#define czujnik3P	(1<<PA6)
#define czujnik2P	(1<<PA5)
#define czujnik1P	(1<<PA4)
#define czujnik1L	(1<<PA3)
#define czujnik2L	(1<<PA2)
#define czujnik3L	(1<<PA1)
#define czujnik4L	(1<<PA0)

// *** SILNIK 1 (LEWY)
#define moto1_A		(1<<PC0)
#define moto1_B		(1<<PC1)
#define moto1_EN	(1<<PD4)
#define moto1_jazda		PORTC |= moto1_A;	PORTC &= ~(moto1_B)
#define moto1_cofka		PORTC &= ~(moto1_A); PORTC |= moto1_B
#define moto1_stop		PORTC &= ~(moto1_A | moto1_B)

// *** SILNIK 2 (PRAWY)
#define moto2_A		(1<<PC2)
#define moto2_B		(1<<PC3)
#define moto2_EN	(1<<PD5)
#define moto2_cofka		PORTC |= moto2_A;	PORTC &= ~(moto2_B)
#define moto2_jazda		PORTC &= ~(moto2_A); PORTC |= moto2_B
#define moto2_stop		PORTC &= ~(moto2_A | moto2_B)

// *** Wartości dodatkowe
#define wart_graniczna			750
#define predkosc_srodkowa		130
#define wart_regulacji_pwm		30

// *** Deklaracje zmiennych
uint8_t czujnik;
volatile uint8_t adc_flag;
volatile uint16_t pomiar[];
volatile uint8_t kanal;
int8_t cz1L, cz2L, cz3L, cz4L, cz1P, cz2P, cz3P, cz4P;
int8_t error, regulacja_pwm, liczba_czujnikow;


int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L | czujnik3L | czujnik2L | czujnik1L
		| czujnik1P | czujnik2P | czujnik3P | czujnik4P);
DDRC |= (moto1_A | moto1_B | moto2_A | moto2_B);
DDRD |= (moto1_EN | moto2_EN);

// *** TIMER1
TCCR1A |= (1<<COM1A1)|(1<<COM1B1) ;		// Compare Output Mode, Fast PWM
TCCR1A |= (1<<WGM10) | (1<<WGM11);		// Fast PWM 10bit
	TCCR1B |= (1<<WGM12);
TCCR1B |= (1<<CS10) | (1<<CS11);        // Preksaler = 64 	 //fpwm = 980Hz
TCNT1 = 1024;
OCR1A = 150;
OCR1B = 150;

// *** TIMER0 - dla ADC
TCCR0 |= (1<<WGM01) | (1<<COM01);	// CTC, Clear OC0 on compare match
TCCR0 |= (1<<CS00);					// preskaler = 1
OCR0 = 160;							// Compare Match (zbocze opadające) na wart. 160
TIMSK |= (1<<OCIE0);

// *** ADC
ADMUX |= (1<<REFS0);							// AVCC with external capacitor at AREF pin
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADIE);	// Włączenie ADC, Auto Trigger Enable, Interrupt Enable
ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // preskaler = 128, fp=125kHz
SFIOR |= (1<<ADTS1) | (1<<ADTS0);				// Przerwanie - Compare Match Timer0


moto1_jazda;			// ROZRUCH - SILNIK LEWY
moto2_jazda;			// ROZRUCH - SILNIK PRAWY

sei();		// Globalne zezwolenie na przerwanie

while(1)
{
	if(adc_flag)
	{
		czujnik = kanal;							//		//

		switch (czujnik)							// Przypisanie wyniku cyfrowego z dokonanego
		{											// pomiaru do zmiennej odpowiadającej za
		case 0:										// swój czujnik
			cz4L = pomiar[czujnik];
			break;

		case 1:
			cz3L = pomiar[czujnik];
			break;

		case 2:
			cz2L = pomiar[czujnik];
			break;

		case 3:
			cz1L = pomiar[czujnik];
			break;

		case 4:
			cz1P = pomiar[czujnik];
			break;

		case 5:
			cz2P = pomiar[czujnik];
			break;

		case 6:
			cz3P = pomiar[czujnik];
			break;

		case 7:
			cz4P = pomiar[czujnik];
			break;
		}
		adc_flag = 0;
	}


	// Suma wartości z odczytanych czujników przekraczających wart_graniczna = 7
	error = cz4L + cz3L + cz2L + cz1L + cz1P + cz2P + cz3P + cz4P;
	// Error podzielony przez ilosc czujników
	error = error / liczba_czujnikow;
	// Wyliczanie wartości regulującej OCR1A/B
	regulacja_pwm = wart_regulacji_pwm * error;
	// Przypisanie nowych wartosci bajtom OCR1A/B
	OCR1A = predkosc_srodkowa + regulacja_pwm;
	OCR1B = predkosc_srodkowa - regulacja_pwm;
	// Reset erroru i wartosci czujników
	error = 0;
	liczba_czujnikow = 0;

}
}

// ***** OBSŁUGA PRZERWANIA ADC *****
ISR(ADC_vect)
{
ADMUX |= (ADMUX & 0xF8) | kanal;		// wybór kanału
while(!ADIF)							// oczekiwanie na koniec pomiaru (automatycznie ADIF = 1)
ADCSRA |= (0<<ADIF);					// ręczny reset ADIF po zakończonym pomiarze
if(kanal<7)	kanal=0;					// sprawdzenie mierzonego kanału
pomiar[kanal] = ADCW;					// przepisanie ADCW do zmiennej pomiar
if(pomiar[kanal] > wart_graniczna)		// porównanie z wartością graniczną
	pomiar[kanal] = 1;					//
kanal++;								// przygotowanie zmiennej do następnego pomiaru
adc_flag = 1;							// ustawienie flagi sygnalizującej pobranie danych
}

To jest nowy kod, napisany dzisiaj rano.

Mam nadal kilka pytań:

1. Dlaczego mam usuwać Timer0? Przecież jakiś sygnał musi zboczem narastającym wyzwolić przerwanie. W bajcie SFIOR ustawiłem Timer/Counter 0 CTC.

2. Cytuję "ADIF - tak, ale Ciebie nie interesuje, ponieważ od razu wywoła się przerwanie, które go zgasi. " - niestety wykorzystałem ADIF, gdyż nie widzę jeszcze innego sposobu przeczekania w pętli na zakońcenie pomiaru, skoro nie wykorzystuję bitu ADSC.

Program się kompiluje, jednak są warning przy deklaracji tablicy, mówiący "array 'pomiar' assumed to have one element"

EDIT:

Po przerwie i ponownym spojrzeniu na kod widzę, że nie potrzebnie dodałem zmienną czujnik oraz zastanawiam się czy na początku przerwania ustawić adc_flag = 0, żeby nie wykonywała się pętla if(adc_flag) w pętli głównej programu.

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...