dondu Sierpień 21, 2011 Udostępnij Sierpień 21, 2011 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. Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 21, 2011 Autor tematu Udostępnij Sierpień 21, 2011 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. Cytuj Link do komentarza Share on other sites More sharing options...
dondu Sierpień 21, 2011 Udostępnij Sierpień 21, 2011 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 Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 21, 2011 Autor tematu Udostępnij Sierpień 21, 2011 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! } Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
dondu Sierpień 21, 2011 Udostępnij Sierpień 21, 2011 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ę. Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 21, 2011 Autor tematu Udostępnij Sierpień 21, 2011 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. Cytuj Link do komentarza Share on other sites More sharing options...
dondu Sierpień 21, 2011 Udostępnij Sierpień 21, 2011 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. Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 21, 2011 Autor tematu Udostępnij Sierpień 21, 2011 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; } Cytuj Link do komentarza Share on other sites More sharing options...
dondu Sierpień 21, 2011 Udostępnij Sierpień 21, 2011 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. 1 Cytuj Link do komentarza Share on other sites More sharing options...
mog123 Sierpień 22, 2011 Udostępnij Sierpień 22, 2011 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. 1 Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 24, 2011 Autor tematu Udostępnij Sierpień 24, 2011 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 Cytuj Link do komentarza Share on other sites More sharing options...
dondu Sierpień 24, 2011 Udostępnij Sierpień 24, 2011 Niestety jakoś nadal nie działa i niezbyt wiem dlaczego. Opisz na czym to "nie działanie" polega. Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 25, 2011 Autor tematu Udostępnij Sierpień 25, 2011 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 😋 Cytuj Link do komentarza Share on other sites More sharing options...
dondu Sierpień 25, 2011 Udostępnij Sierpień 25, 2011 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. Cytuj Link do komentarza Share on other sites More sharing options...
Keadwen Sierpień 25, 2011 Autor tematu Udostępnij Sierpień 25, 2011 #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! } Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
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!