Skocz do zawartości
Keadwen

[C] ADC + algorytm P

Pomocna odpowiedź

Zatrzymałeś się na zrozumieniu jak działa przerwanie, więc wyjaśnię to dokładniej.

ADIF - to jest flaga przerwania. Gdy ADC zakończy pomiar ustawia automatycznie ADIF.

Jeżeli masz włączone globalne przerwania, wtedy według kolejności dzieje się tak:

1. mikrokontroler przerywa wykonanie programu

2. natychmiast rozpoczyna wykonanie przerwania ISR(ADC_vect) automatycznie gasząc flagę ADIF

Czyli jak widzisz przy tak rozwiązanym ADIF jest poza Twoją kontrolą i nie musisz (ba, wręcz nie możesz) sprawdzać ADIF. Czy teraz to jasne?

Podałem Ci link do http://mikrokontrolery.blogspot.com/2011/04/problemy-c-przerwania.html

nie przeczytałeś go dokładnie lub nie zastosowałeś się do tam podanych informacji. Zamieniłeś czekanie za pomocą

_delay_us(13); 

na czekanie za pomocą

while(!ADIF)

czyli nadal czekasz a to ZABRONIONE, z powodów które opisane są pod wskazanym linkiem.

Prosiłem, abyś zachował dokładnie kolejność czynności w funkcji przerwania:

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.

Ale tego nie zrobiłeś, stąd nadal stoisz w miejscu. Postaraj się to zrobić i NIC WIĘCEJ nie kombinuj w tej funkcji.

Udostępnij ten post


Link to post
Share on other sites

Teraz widzę jak ten brak doświadczenia z programowaniem. Mam wielką nadzieję, że jesteś bardziej cierpliwy i spokojny niż ja Dondu. Powoli zaczyna mnie denerwować to, że rozumiem ale nie umiem napisać 😕

Ale mam kolejną wersję. Przerwanie ADC zrobiłem z opisami punktowymi i pasuje do twojego algorytmu działania.

#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)
{
for(kanal = 0; kanal < 8; kanal++)			// 2.2 Zmiana kanału + 2.3 Inicjacja kolejnego pomiaru
{
	ADMUX |= (ADMUX & 0xF8) | kanal;		// 2.1 Odczyt ADC
	ADCSRA |= (1<<ADSC);					// Zabronione czekanie, ale jak mam to inaczej
	while (ADCSRA & (1<<ADSC))				// poczekać na wynik?!
	pomiar[kanal] = ADCW;					// 2.1 Zapisanie do zmiennej

	if(pomiar[kanal] > wart_graniczna)		//
		pomiar[kanal] = 1;
               else
		pomiar[kanal] = 0;
}
if(kanal>7)	kanal=0;					    // Reset - przygotowanie do następnego przerwania
adc_flag = 1;								// 2.4 Ustawienie flagi dla programu głównego
}

Odpowiedz mi proszę na poniższe pytania:

1. Czy mam usunąć Timer0 czy nie? Jeżeli go usunę to co ma być wyzwalaczem przerwania?

2. Czy Timer0 nie wywołuje przerwania zbyt często?

3. Jeżeli nie mogę używać _delay_ ani pętli oczekujących to w jaki sposób mam mieć pewność, że ADC wykonało odpowiednią ilość cykli i odczytana wartość jest poprawna?

(przeczytałem twoją stronę. Tam jest napisane jak nie robić, a brak informacji jak to zastąpić)

Znalazłem polską wersję karty katalogowej Atmegi16 i doczytałem, że z tym ADIF to rzeczywiście ulega wyczyszczeniu po rozpoczęciu przerwania.

Udostępnij ten post


Link to post
Share on other sites
Teraz widzę jak ten brak doświadczenia z programowaniem. Mam wielką nadzieję, że jesteś bardziej cierpliwy i spokojny niż ja Dondu. Powoli zaczyna mnie denerwować to, że rozumiem ale nie umiem napisać 😕

Ja mam bardzo dużo cierpliwości, pytanie czy Ty masz? Z tego co piszesz to nie bardzo, więc rozważ, zakup książki, bo to przyspieszy kolosalnie Twoje postępy:

http://mikrokontrolery.blogspot.com/p/ksiazki-dla-ciebie.html

Ciągle krążysz wobec niezrozumienia zasady działania ADC i przerwań z nim związanych. teraz zadałeś konkretne pytanie:

1. Czy mam usunąć Timer0 czy nie? Jeżeli go usunę to co ma być wyzwalaczem przerwania?

a konkretną odpowiedź otrzymałeś już wcześniej:

Zatrzymałeś się na zrozumieniu jak działa przerwanie, więc wyjaśnię to dokładniej.

ADIF - to jest flaga przerwania. Gdy ADC zakończy pomiar ustawia automatycznie ADIF.

Jeżeli masz włączone globalne przerwania, wtedy według kolejności dzieje się tak:

1. mikrokontroler przerywa wykonanie programu

2. natychmiast rozpoczyna wykonanie przerwania ISR(ADC_vect) automatycznie gasząc flagę ADIF

Innymi słowy samo ADC wywołuje przerwanie, gdy zakończy pomiar, i nie potrzebuje do tego timera:

1. main() inicjuje ADC, włącza przerwania globalne i startuje pierwszy pomiar ustawiając flagę ADSC

1.1 main() kręci się w kółko w pętli while()

2. gdy ADC zakończy pomiar ustawia flagę ADIF i natychmiast przechodzi do funkcji przerwania gasząc flagę ADIF.

2.1. funkcja przerwania ma wykonać czynności które szczegółowo wypunktowałem wcześniej

2.2. Funkcja przerwania kończy działanie, a mikrokontroler wraca do realizacji głównej pętli main(), w której wykrywa za pomocą flagi, że jest nowy pomiar w buforze w RAM.

Jeżeli nadal nie rozumiesz, to napisz pseudo kod, może będzie łatwiej:

http://pl.wikipedia.org/wiki/Pseudokod

przeczytałem twoją stronę. Tam jest napisane jak nie robić, a brak informacji jak to zastąpić

Tak, bo dotyczy programowego błędu, który może występować nie tylko w ADC ale w dowolnym mikrokontrolerze czy wewnętrznym układzie generującym jakieś przerwanie. Musiałbym w tym miejscu opisać parę milionów rozwiązań 😃

Link dla tych co przeczytali, iż znalazłeś polską dokumentację: http://mikrokontrolery.blogspot.com/2011/04/polska-dokumentacja-atmel.html

Udostępnij ten post


Link to post
Share on other sites

Napisałem na kartce pseudokod.

1. Włączam ADC

2. Włączam zezwolenie na przerwanie ADC

3. Ustawiam częstotliwość pracy ADC

4. Włączam globalne zezwolenie na przerwanie

5. Ustawiami (1<

6. Zakończenie pomiaru (czyli automatyczny clear ADSC i set ADIF)

7. Rozpoczęcie przerwania ADC_vect

8. Odczyt wartości z zmierzonego kanału (bajty ADCW)

8.1. Zmiana kanału pomiarowego ADC

8.2 Zainicjowania nowego pomiaru

9. Ustawienie flagi dla pętli głównej o nowych odczytach

10. Pętla while wylicza sobie coś tam 😋

11. Ustawiamy ADSC i tak w kółko od pkt 6

Idę walczyć z kodem, mam nadzieje, że ten pseudokod pomoże. Co do cierpliwości, to wróciła ;D Książkę mam autora Mirekk36.

EDIT:

#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 adc_flag;
volatile uint16_t pomiar[8];
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;

   // *** ADC
   ADMUX |= (1<<REFS0);                            // 1. Włączenie ADC + napięcie odniesienia
   ADCSRA |= (1<<ADEN) | (1<<ADIE);   				// 2. Interrupt Enable
   ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // 3. Częstotliwość pracy (preskaler = 128, fp=125kHz)


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

sei();											// 4. Włączenie globalnego zezwolenia na przerwanie
ADCSRA |= (1<<ADSC);							// 5. Ustawienie flagi dla pierwszego pomiaru

while(1)
{
	for(adc_flag = 1; adc_flag; adc_flag--)		// 10. Pętla for - "wylicza sobie coś tam"
	{

	}

	// 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;

	ADCSRA |= (1<<ADSC);						// 11. Po wyliczeniach ponownie ustawiamy ADSC by wykonać pomiar
												// 12. Patrz pkt.6
}
}

// ***** OBSŁUGA PRZERWANIA ADC *****
ISR(ADC_vect)										// 6. Zakończenie pomiaru (set ADIF, clear ADSC)
{													// 7. Reset ADIF, rozpoczęcie przerwania
pomiar[kanal] = ADCW;							// 8. Odczytanie wartości z bajtów ADCW
kanal++;										// 8.1. Zmiana kanału pomiarowego ADC
if(kanal < 8)
	ADCSRA |= (1<<ADSC);						// 8.2. Zainicjowania nowego pomiaru
else
{
	kanal = 0;
	adc_flag = 1;								// 9. Ustawienie flagi dla programu głównego
}												// ->  ale dopiero po wykonaniu 8 pomiarów!
}

Udostępnij ten post


Link to post
Share on other sites

Widzę, że chcesz zrobić nieco inaczej, czyli mikrokontroler ma zająć się przeliczaniem, dopiero gdy odczyta wszystkie kanały.

Czy tak? Od tego zależy jakie uwagi napiszę.

Udostępnij ten post


Link to post
Share on other sites

Wydaje mi się, że najbardziej optymalne z punktu widzenia line followera jest to by najpierw odczytał 8 kanałów i dopiero potem obliczył error i OCR1A/B.

Udostępnij ten post


Link to post
Share on other sites
Wydaje mi się, że najbardziej optymalne z punktu widzenia line followera jest to by najpierw odczytał 8 kanałów i dopiero potem obliczył error i OCR1A/B.

OK, zapewne tak. Za 10 min napisze uwagi.

[ Dodano: 21-08-2011, 17:43 ]

No to po kolei:

1. Ustawiasz "AVCC with external capacitor at AREF pin", czyli kondensator masz podłączony? Swoją drogą do pełni szczęścia przydałby się schemat.

2. ADC zainicjowany prawidłowo.

3. Do czego to ma służyć i dlaczego wykorzystuje flagę ustawianą przez przerwanie?:

       for(adc_flag = 1; adc_flag; adc_flag--)        // 10. Pętla for - "wylicza sobie coś tam" 
       { 

       } 

4. Obliczenia i start pomiaru w main powinny być tylko gdy adc_flag jest ustawiona przez przerwanie. Powinieneś więc cały ten kod objąć IF-em. W IF-ie tym powinno także być zerowanie flagi adc_flag.

5. w przerwaniu zmieniasz nr kanału w buforze RAM czyli tablicy, ale zapomniałeś zmienić także kanał w ADC, czyli MUX. Dodaj odpowiedni kod, który już miałeś napisany wcześniej.

Nie sprawdzałem algorytmu P.

Udostępnij ten post


Link to post
Share on other sites

Działa, jeździ ale ma jedną wadę 😋 Dość ostro przestrzeliwuje zakręty, wręcz w 90% jedzie prosto, a w 10% jak już skręci to i tak ucieka potem prosto. Sądzę, że to może być wina zbyt długiego przerwania i podczas wykonywania go, następne się już dobija.

Zamieszczam poniżej aktualny kod. Dzisiaj jeszcze dużo będę ekperymentował bo mam wolnego czasu nadmiar 😋

EDIT: Poprawiony kod. Póki co nie działa. Wszelkie zmiany będę tutaj wprowadzał, bez tworzenia nowego postu.

#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			670
#define predkosc_srodkowa		100
#define wart_regulacji_pwm		35

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


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;

   // *** ADC
   ADMUX |= (1<<REFS0);                            // 1. Włączenie ADC + napięcie odniesienia
   ADCSRA |= (1<<ADEN) | (1<<ADIE);   				// 2. Interrupt Enable
   ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // 3. Częstotliwość pracy (preskaler = 128, fp=125kHz)


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

sei();											// 4. Włączenie globalnego zezwolenia na przerwanie
ADCSRA |= (1<<ADSC);							// 5. Ustawienie flagi dla pierwszego pomiaru

while(1)
{
	if(adc_flag)// 10. Pętla for - "wylicza sobie coś tam"
	{
		if(pomiar[0] > wart_graniczna) {
			cz4L = -30;
			licznik++;
		}
		else	cz4L = 0;

		if(pomiar[1] > wart_graniczna) {
			cz3L = -20;
			licznik++;
		}
		else	cz3L = 0;

		if(pomiar[2] > wart_graniczna) {
			cz2L = -10;
			licznik++;
		}
		else	cz2L = 0;

		if(pomiar[3] > wart_graniczna) {
			cz1L = 0;
			licznik++;
		}
		else	cz1L = 0;

		if(pomiar[4] > wart_graniczna) {
			cz1P = 0;
			licznik++;
		}
		else	cz1P = 0;

		if(pomiar[5] > wart_graniczna) {
			cz2P = 10;
			licznik++;
		}
		else	cz2P = 0;

		if(pomiar[6] > wart_graniczna) {
			cz3P = 20;
			licznik++;
		}
		else	cz3P = 0;

		if(pomiar[7] > wart_graniczna) {
			cz4P = 30;
			licznik++;
		}
		else	cz4P = 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 / licznik;
		// Wyliczanie wartości regulującej OCR1A/B
		regulacja_pwm = (wart_regulacji_pwm * error)/10;
		// Przypisanie nowych wartosci bajtom OCR1A/B
		OCR1A = predkosc_srodkowa + regulacja_pwm;
		OCR1B = predkosc_srodkowa - regulacja_pwm;
		// Reset erroru i wartosci czujników
		error = 0;
		licznik = 0;
		adc_flag = 0;
		ADCSRA |= (1<<ADSC);						// 11. Po wyliczeniach ponownie ustawiamy ADSC by wykonać pomiar
													// 12. Patrz pkt.6
	}
}
}

// ***** OBSŁUGA PRZERWANIA ADC *****
ISR(ADC_vect)										// 6. Zakończenie pomiaru (set ADIF, clear ADSC)
{
pomiar[kanal] = ADCW;  							// 7. Reset ADIF, rozpoczęcie przerwania
kanal++;										// 8. Odczytanie wartości z bajtów ADCW
												// 8.1. Zmiana kanału pomiarowego ADC
if(kanal < 8)
	ADCSRA |= (1<<ADSC);						// 8.2. Zainicjowania nowego pomiaru
else
{
	kanal = 0;
	adc_flag = 1;								// 9. Ustawienie flagi dla programu głównego
}												// ->  ale dopiero po wykonaniu 8 pomiarów!
ADMUX |= (ADMUX * 0xF8) | kanal;
}

Udostępnij ten post


Link to post
Share on other sites

Czyli kierunek działań jest dobry, ale dużo jeszcze musisz popracować nad sobą 😃

Miałeś wstawić zmianę kanału zgodnie z algorytmem, a gdzie ją wstawiłeś?

Zauważyłem, że często przestawiasz kolejność pewnych czynności, a to może znacznie zmienić efekt pracy programu.

Sądzę, że to może być wina zbyt długiego przerwania i podczas wykonywania go, następne się już dobija.

No to policzmy tak na szybko:

Taktujesz ADC z prędkością 125kHz

Każdy pomiar trwa 13 taktów zegara ADC, stąd:

125.000 / 13 = 9615 cykli zegara CPU

Co to oznacza? Że pomiar ADC trwa 9615 taktów zegara CPU, czyli w tym czasie CPU może wykonać 9kB kodu pomiędzy dwoma pomiarami ADC.

Czyli masz moc obliczeniową z ogromną nadwyżką do potrzeb.

Co więcej wartość ta powinna być pomnożona x8 ponieważ obliczenia w main() wykonujesz dopiero po 8 przerwaniach.

Co do szybkości wykonywania funkcji przerwania to jest ona kosmicznie krótka 🙂

Na oko niech będzie z naddatkiem 100 bajtów, co oznacza 6,25µs.

Dla porównania pomiar ADC trwa 104µs.

Reasumując, nie występuje zjawisko które podejrzewasz czyli zachodzenie przerwania jednego na drugie.

Uważam, że objawy które opisujesz są winą algorytmu P i/lub sterowania Timerem.

Ale na tym nie za bardzo się znam, więc może ktoś inny sprawdzi.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Twój kod jest dla mnie w ogóle nieczytelny ;D nabazgroliłeś.

Jedna rada - czasem jest łatwiej zacząć na papierze, rozrysować algorytm a potem dopiero zabrać się za pisanie.

"Pisanie bez myślenia - sensu nie ma" - mog123

Odsyłam do literatury:

Pierwszy link z google: http://www.algorytm.org/kurs-algorytmiki/schematy-blokowe.html

Wiem że chcesz "żeby już działało" ale czasem lepiej wszystko zescrapować i zacząć od nowa.

Napisanie 90% kodu zajmuje 90% czasu. Pozostałe 10% zajmuje kolejne 90% czasu.

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Poprawiłem kod, na bardziej czytelny w sposób przez Ciebie Mog podrzucony. Wyrzuciłem wszystko co nie było związane z ADC i rozrysowałem blokowy schemat działania ogólnego.

Niestety jakoś nadal nie działa i niezbyt wiem dlaczego.

Zasada działania mojego programu wygląda tak:

1. Wykonuję odczyt z 8 czujników (10bit rozdzielczość) i zapisuje wartości do tabeli pomiar[] // opis odczytu kilka postów u góry

2. Za pomocą pętli if sprawdzam czy pomiar dla poszczególnych czujników przekroczył wartość graniczną np. 700.

2.a. Jeżeli tak, to zmienna czujnikXX przyjmuje wartość zależną od położenia czujnika (czujnik po lewej ma wart -4, potem -3, -2, -1. Natomiast czujniki od prawej 1, 2, 3, 4)

Inkrementuję również zmienną "ilość czujników" którą wykorzystam w późniejszych obliczeniach.

2.b Jeżeli nie przekroczyła wart_granicznej, wartość czujnika = 0

3. Po wykonaniu wszystkich odczytów sumuję wartości ich wszystkich i dzielę przez ilość gdzię wystąpiła linia. (mnożę też x10 by uniknąć wartości po przecinku)

4. Wyliczam rozrzut (error) względem docelowej pozycji

5. Wyliczam wartość którą będę regulował OCR1A i OCR1B

6. Wyznaczam nowe wartości OCR1A/B

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

// ***** CZUJNIKI CNY70 *****
#define czujnik4P_PIN	(1<<PA7)
#define czujnik3P_PIN	(1<<PA6)
#define czujnik2P_PIN	(1<<PA5)
#define czujnik1P_PIN	(1<<PA4)
#define czujnik1L_PIN	(1<<PA3)
#define czujnik2L_PIN	(1<<PA2)
#define czujnik3L_PIN	(1<<PA1)
#define czujnik4L_PIN	(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)

// *** Deklaracje zmiennych
volatile uint8_t adc_flag;
volatile int16_t pomiar[8];
volatile uint8_t kanal = 0;

// Zmienne do regulacji algorytmu P
uint16_t docelowa_pozycja = 0;
uint16_t wart_graniczna = 700;
uint16_t predkosc_silnikow = 150;
uint8_t wart_regulacji_pwm = 35;

// Zmienne pomocne w algorytmie
int16_t aktualna_pozycja;
int16_t error;
int16_t zmiana_pwm;
uint8_t ilosc_czujnikow = 1;			// Zabezpieczenie przed dzieleniem przez 0
int8_t czujnik4L, czujnik3L, czujnik2L, czujnik1L, czujnik1P, czujnik2P, czujnik3P, czujnik4P;
int16_t v_ocr1a, v_ocr1b;


int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L_PIN | czujnik3L_PIN | czujnik2L_PIN | czujnik1L_PIN
		| czujnik1P_PIN | czujnik2P_PIN | czujnik3P_PIN | czujnik4P_PIN);
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;

   // *** ADC
   ADMUX |= (1<<REFS0);                            // 1. Włączenie ADC + napięcie odniesienia
   ADCSRA |= (1<<ADEN) | (1<<ADIE);   				// 2. Interrupt Enable
   ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // 3. Częstotliwość pracy (preskaler = 128, fp=125kHz)


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


sei();											// 4. Włączenie globalnego zezwolenia na przerwanie
ADCSRA |= (1<<ADSC);							// 5. Ustawienie flagi dla pierwszego pomiaru
ADMUX |= (ADMUX * 0xF8);

while(1)
{
	if(adc_flag)// 10. Pętla for - "wylicza sobie coś tam"
	{
		if(pomiar[0] > wart_graniczna) {
			czujnik4L = -4;
			ilosc_czujnikow++;
		}
		else	czujnik4L = 0;

		if(pomiar[1] > wart_graniczna) {
			czujnik3L = -3;
			ilosc_czujnikow++;
		}
		else	czujnik3L = 0;

		if(pomiar[2] > wart_graniczna) {
			czujnik2L = -2;
			ilosc_czujnikow++;
		}
		else	czujnik2L = 0;

		if(pomiar[3] > wart_graniczna) {
			czujnik1L = -1;
			ilosc_czujnikow++;
		}
		else	czujnik1L = 0;

		if(pomiar[4] > wart_graniczna) {
			czujnik1P = 1;
			ilosc_czujnikow++;
		}
		else	czujnik1P = 0;

		if(pomiar[5] > wart_graniczna) {
			czujnik2P = 2;
			ilosc_czujnikow++;
		}
		else	czujnik2P = 0;

		if(pomiar[6] > wart_graniczna) {
			czujnik3P = 3;
			ilosc_czujnikow++;
		}
		else	czujnik3P = 0;

		if(pomiar[7] > wart_graniczna) {
			czujnik4P = 4;
			ilosc_czujnikow++;
		}
		else	czujnik4P = 0;


		aktualna_pozycja = (((czujnik4L + czujnik3L + czujnik2L + czujnik1L + czujnik1P +
							  czujnik2P + czujnik3P + czujnik4P)* 10)/ilosc_czujnikow);
		// Dodałem wszystkie czujniki gdzie występuje linia i pomnożyłem x10 by nie robić zmiennej
		// typu float. Podkoniec podzieliłem przez ilośc czujników w który wystąpiła linia.

		error = docelowa_pozycja - aktualna_pozycja;
		// Wyliczam w którym miejscu "mniej więcej" znajduje się linia

		zmiana_pwm = ((wart_regulacji_pwm * error)/10);
		// Wyliczam wartość która będzie zmieniać OCR1A/B. Dzielę też przez 10, bo wcześniej mnoż.

		v_ocr1a = predkosc_silnikow + zmiana_pwm;
		if(v_ocr1a > 255)	v_ocr1a = 255;
		v_ocr1b = predkosc_silnikow - zmiana_pwm;
		if(v_ocr1b > 255)	v_ocr1b = 255;

		OCR1A = v_ocr1a;
		OCR1B = v_ocr1b;

		// Resety wartości
		ilosc_czujnikow = 1;
		error = 0;
		zmiana_pwm = 0;
		aktualna_pozycja = 0;

		// Reset flagi
		adc_flag = 0;

		ADCSRA |= (1<<ADSC);						// 11. Po wyliczeniach ponownie ustawiamy ADSC by wykonać pomiar
													// 12. Patrz pkt.6
	}

}
}

// ***** OBSŁUGA PRZERWANIA ADC *****
ISR(ADC_vect)										// 6. Zakończenie pomiaru (set ADIF, clear ADSC)
{
pomiar[kanal] = ADCW;  							// 7. Reset ADIF, rozpoczęcie przerwania
kanal++;										// 8. Odczytanie wartości z bajtów ADCW
												// 8.1. Zmiana kanału pomiarowego ADC
if(kanal < 8)
	ADCSRA |= (1<<ADSC);						// 8.2. Zainicjowania nowego pomiaru
else
{
	kanal = 0;
//		adc_flag = 1;								// 9. Ustawienie flagi dla programu głównego
}												// ->  ale dopiero po wykonaniu 8 pomiarów!
ADMUX |= (ADMUX * 0xF8) | kanal;
}

Podsumowując, chciałbym powiedzieć, że wzoruje się na ostatnim artykul i jego kodzie

Kp = 10 //Współczynnik Kp                             
Docelowa = 0 //Pozycja docelowa
Tp = 50 //Docelowa prędkość robota

Do //Pętla nieskończona
Odczytaj pod, którymi czujnikami jest linia
Policz aktualną pozycję

Błąd = Docelowa + Aktualna pozycja
Zmiana = Kp * błąd //Liczymy wartość zmiany PWM

Silnik_L = Tp + Zmiana // Przekazujemy do silnika lewego nową prędkość
Silnik_p = Tp - Zmiana // Przekazujemy do silnika prawego nową prędkość
Loop //Koniec pętli

Udostępnij ten post


Link to post
Share on other sites
Niestety jakoś nadal nie działa i niezbyt wiem dlaczego.

Opisz na czym to "nie działanie" polega.

Udostępnij ten post


Link to post
Share on other sites

Jedzie przed siebie, czujniki nie reagują na linię. Sądzę, że to może być wina tych żałosnych kabelków dlatego je dzisiaj wymieniam na porządne, estetyczne wielożyłowe drucisze 😋

Udostępnij ten post


Link to post
Share on other sites

Znowu nie robisz zgodnie z algorytmem podanym przeze mnie na początku tego tematu.

Przełączasz kanał ADC po starcie pomiaru, a nie przed:

// ***** OBSŁUGA PRZERWANIA ADC ***** 
ISR(ADC_vect)                                        // 6. Zakończenie pomiaru (set ADIF, clear ADSC) 
{ 
   pomiar[kanal] = ADCW;                              // 7. Reset ADIF, rozpoczęcie przerwania 
   kanal++;                                        // 8. Odczytanie wartości z bajtów ADCW 
                                                   // 8.1. Zmiana kanału pomiarowego ADC 
   if(kanal < 8) 
       ADCSRA |= (1<<ADSC);                        // 8.2. Zainicjowania nowego pomiaru 
   else 
   { 
       kanal = 0; 
//        adc_flag = 1;                                // 9. Ustawienie flagi dla programu głównego 
   }                                                // ->  ale dopiero po wykonaniu 8 pomiarów! 
   ADMUX |= (ADMUX * 0xF8) | kanal;  <===== ZA PÓŹNO!!!
} 

Z tej części dokumentacji:

wynika, że zmiana kanału następuje w momencie startu pomiaru, na bazie aktualnej wartości MUX, czego potwierdzeniem jest:

2 prośby:

1. Zacznij lub uważniej czytaj datasheet mikrokontrolera.

2. Pisanie w kółko tego samego parę razy, zniechęca do pomagania (przynajmniej mnie). Staraj się dokładniej stosować porady, by nie tracić swojego i naszego czasu.

Poza tym ponawiam:

Swoją drogą do pełni szczęścia przydałby się schemat.

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_PIN	(1<<PA7)
#define czujnik3P_PIN	(1<<PA6)
#define czujnik2P_PIN	(1<<PA5)
#define czujnik1P_PIN	(1<<PA4)
#define czujnik1L_PIN	(1<<PA3)
#define czujnik2L_PIN	(1<<PA2)
#define czujnik3L_PIN	(1<<PA1)
#define czujnik4L_PIN	(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)

// *** Deklaracje zmiennych
volatile uint8_t adc_flag;
volatile int16_t pomiar[8];
volatile uint8_t kanal = 0;

// Zmienne do regulacji algorytmu P
uint16_t docelowa_pozycja = 0;
uint16_t wart_graniczna = 700;
uint16_t predkosc_silnikow = 150;
uint8_t wart_regulacji_pwm = 35;

// Zmienne pomocne w algorytmie
int16_t aktualna_pozycja;
int16_t error;
int16_t zmiana_pwm;
uint8_t ilosc_czujnikow = 1;			// Zabezpieczenie przed dzieleniem przez 0
int8_t czujnik4L, czujnik3L, czujnik2L, czujnik1L, czujnik1P, czujnik2P, czujnik3P, czujnik4P;
int16_t v_ocr1a, v_ocr1b;


int main(void)
{
// *** DATA DIRECTION REGISTER
DDRA &= ~(czujnik4L_PIN | czujnik3L_PIN | czujnik2L_PIN | czujnik1L_PIN
		| czujnik1P_PIN | czujnik2P_PIN | czujnik3P_PIN | czujnik4P_PIN);
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;

   // *** ADC
   ADMUX |= (1<<REFS0);                            // 1. Włączenie ADC + napięcie odniesienia
   ADCSRA |= (1<<ADEN) | (1<<ADIE);   				// 2. Interrupt Enable
   ADCSRA |= (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // 3. Częstotliwość pracy (preskaler = 128, fp=125kHz)


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


sei();											// 4. Włączenie globalnego zezwolenia na przerwanie
ADMUX |= (ADMUX * 0xF8);
ADCSRA |= (1<<ADSC);							// 5. Ustawienie flagi dla pierwszego pomiaru


while(1)
{
	if(adc_flag)// 10. Pętla for - "wylicza sobie coś tam"
	{
		if(pomiar[0] > wart_graniczna) {
			czujnik4L = -4;
			ilosc_czujnikow++;
		}
		else	czujnik4L = 0;

		if(pomiar[1] > wart_graniczna) {
			czujnik3L = -3;
			ilosc_czujnikow++;
		}
		else	czujnik3L = 0;

		if(pomiar[2] > wart_graniczna) {
			czujnik2L = -2;
			ilosc_czujnikow++;
		}
		else	czujnik2L = 0;

		if(pomiar[3] > wart_graniczna) {
			czujnik1L = -1;
			ilosc_czujnikow++;
		}
		else	czujnik1L = 0;

		if(pomiar[4] > wart_graniczna) {
			czujnik1P = 1;
			ilosc_czujnikow++;
		}
		else	czujnik1P = 0;

		if(pomiar[5] > wart_graniczna) {
			czujnik2P = 2;
			ilosc_czujnikow++;
		}
		else	czujnik2P = 0;

		if(pomiar[6] > wart_graniczna) {
			czujnik3P = 3;
			ilosc_czujnikow++;
		}
		else	czujnik3P = 0;

		if(pomiar[7] > wart_graniczna) {
			czujnik4P = 4;
			ilosc_czujnikow++;
		}
		else	czujnik4P = 0;


		aktualna_pozycja = (((czujnik4L + czujnik3L + czujnik2L + czujnik1L + czujnik1P +
							  czujnik2P + czujnik3P + czujnik4P)* 10)/ilosc_czujnikow);
		// Dodałem wszystkie czujniki gdzie występuje linia i pomnożyłem x10 by nie robić zmiennej
		// typu float. Podkoniec podzieliłem przez ilośc czujników w który wystąpiła linia.

		error = docelowa_pozycja - aktualna_pozycja;
		// Wyliczam w którym miejscu "mniej więcej" znajduje się linia

		zmiana_pwm = ((wart_regulacji_pwm * error)/10);
		// Wyliczam wartość która będzie zmieniać OCR1A/B. Dzielę też przez 10, bo wcześniej mnoż.

		v_ocr1a = predkosc_silnikow + zmiana_pwm;
		if(v_ocr1a > 255)	v_ocr1a = 255;
		v_ocr1b = predkosc_silnikow - zmiana_pwm;
		if(v_ocr1b > 255)	v_ocr1b = 255;

		OCR1A = v_ocr1a;
		OCR1B = v_ocr1b;

		// Resety wartości
		ilosc_czujnikow = 1;
		error = 0;
		zmiana_pwm = 0;
		aktualna_pozycja = 0;

		// Reset flagi
		adc_flag = 0;

		ADMUX |= (ADMUX * 0xF8);					// Zerowanie kanalu i jego start poniżej
		ADCSRA |= (1<<ADSC);						// 11. Po wyliczeniach ponownie ustawiamy ADSC by wykonać pomiar
													// 12. Patrz pkt.6
	}

}
}

// ***** OBSŁUGA PRZERWANIA ADC *****
ISR(ADC_vect)										// 6. Zakończenie pomiaru (set ADIF, clear ADSC)
{
pomiar[kanal] = ADCW;  							// 7. Reset ADIF, rozpoczęcie przerwania
kanal++;										// 8. Odczytanie wartości z bajtów ADCW
ADMUX |= (ADMUX * 0xF8) | kanal;
												// 8.1. Zmiana kanału pomiarowego ADC
if(kanal < 8)
	ADCSRA |= (1<<ADSC);						// 8.2. Zainicjowania nowego pomiaru
else
{
	kanal = 0;
	adc_flag = 1;								// 9. Ustawienie flagi dla programu głównego
}												// ->  ale dopiero po wykonaniu 8 pomiarów!

}

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...