logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[atmega128][c]pseudowątki w avr, obsługa wyświetlacza i i_o

nusch 28 Lis 2009 17:25 1829 12
  • #1 7319863
    nusch
    Poziom 15  
    Używam dwóch przerwań - TCCR0 do obsługi LED displaya i TCCR2 do operacji I/O. Wyświetlacz powinen mięć możliwość wyświetlania napisów które mrugają. A operacje IO są raczej asynchroniczne ale pojedyncza może trwać dłużej niż pojedyncze mrugnięcie wyświetlacza czyli "zamrozi" go w przypadku użycia display_routine() i i_o_routine() jedna po drugiej w jednym przerwaniu.

    więc zrobiłem
    
    ISR(TIMER0_COMP_vect) {
       display_routine();
    }
    
    ISR(TIMER2_COMP_vect) {
      i_o_routine();
    }
    

    operacje I/O zasymulowałem przez
    
    _delas_us(100)  //próbowałem roznych wartoci i _delay_ms
    

    Myślałem że najprościej ustawić to TIMER2_COMP_vect jako ISR_NOBLOCK ale wtedy wyświetlacz zostaje w jednej pozycji. W którym miejscu źle myślę? Czy w przypadku wywołania przerwania w momencie obsługi innego, mam gwarancję że pierwsze zostanie dokończone ? I czy w przypadku długiej obsługi przerwania może ono zostać przerwane przez to samo? tzn czy mysze dodac cos takiego:
    
    void i_o_routine() {
    if(!lock) {
       lock = 1;
       do_stuff();
       lock = 0;
    } 
    }
    
  • Pomocny post
    #2 7319957
    pburczyn
    Poziom 12  
    Błędna wydaje się być sama koncepcja programu. Obsługa przerwań powinna trwać maksymalnie krótko i najlepiej jeśli się ograniczają do ustawiania flag, które są interpretowane w pętli głównej.
    Dla uC 100us to prawie tyle co wieczność. Dlatego też procedury, które zajmują dużo czasu powinieneś wykonywać, dzieląc je na mniejsze fragmenty i przy każdym obiegu pętli wykonywać tylko pewien fragment. Dzięki temu inne "wątki" nie będą zamrażane.
  • #3 7319986
    nusch
    Poziom 15  
    Próbowałem to zaimplementować jedno po drugim, display ale wtedy liczba tych stanów(flag) rośnie w bardzo dużym tempie i na pewnym etapie już się w nich gubię. Do tego w różnym etapie pojedynczego cyklu I/O chciałem użyć funkcji _delay_ms a robiąc to w jednej pętli musiałbym wprowadzać dodatkowe zmienne do podzielenia częstotliwości głółnej pętli + jeszcze więcej stanów + to wszystko dla każdej możliwej wartości _delay_ms i każdego możliwego miejsca jej wywołania.

    EDIT: faktycznie jak spojrzałem na to to cała funkcje i_o_routine mogę przenieść do głównego while(1) {} w main() ale jak wtedy zachowuje się funkcją _delay_ms czy jeżeli podany jako argument jest relatywnie długi to lepiej ja rozbić na kilka mniejszych czy kompilator sobie z tym sam poradzi? I czy czas poświęcony na obsługe przerwania jest uwzględniony w delay_ms czy wydłuży się o tą wartość ?
    Bo w tej chwili _delay_ms(100) w main powoduje opóźnienie około sekundy. Robie to na atmedze128l z zegarem 73728000 próbowałem ustawic F_CPU na tą wartosc i na 7200 ale bez zmian
  • Pomocny post
    #4 7320428
    mirekk36
    Poziom 42  
    Przerwania w takich mikrokontrolerach to nie wątki niestety - całkowicie złe podejście. Ale kombinować warto ;)

    Ja zwykle wykorzystuję jakiś Timer do generowania tzw tyknięć systemowych co jakiś krótki czas np kilkanaście- kilkadziesiąt ms oraz oznaczania tylko kilku flag oznaczających upływ kilku stałych odcinków czasu np Flaga co 40ms, Flaga co 1s itd

    dzięki temu w pętli głównej już łatwo sobie poradzić z opanowaniem takich flag i wykonywaniem programu co określone odcinki czasu (jakie chę) i to bez żadnego używania poleceń _delay_ms , o takich poleceniach wtedy praktycznie zapominam i nie wiem co to jest.

    Wszystkie opóźnienia w pętli głównej realizowane są w oparciu o flagi dzięki czemu daje radę wszystko ładnie zgrać.

    Reasumując pętla główna leci sobie z maksymalną prędkością i jeśli akurat nie ma nic do roboty to biegnie że tak powiem jałowo - a jak tylko przychodzi czas na jakiś pseudo-proces to zostaje on odpalony i zakończony, po nim wykona się kolejny i ew kolejny

    przy czym w tych procesach tak jak w przerwaniach także wszystko musi szybko śmigać i bez żadnych _delay_ms ;) bo po co zresztą? jeśli są wykonywane tylko wtedy kiedy trzeba

    dzięki temu można uzyskiwać hmmm jakąś namiastkę systemu wielowątkowego a do tego wykorzystywać spokojnie nadal nawet wiele różnych przerwań
  • #5 7321564
    pburczyn
    Poziom 12  
    U mnie większość programów wygląda tak:
    
    void main(){
    	//inicjalizacja
    	...
    	while(1){
    		funkcja1();
    		funkcja2();
    		...					
    	}
    }
    
    void funkcja1(void){
    	switch(funkcja1Stan){
    		case 0:
    			//rob cos
    			
    			SetDelay1ms(200);		//czekaj 200ms
    			funkcja1Stan=1;
    			break;
    	
    		case 1:
    			if(GetDelay1ms() break;	//200ms jeszcze nie mineło
    			//rob cos
    			...
    			break;
    			
    		...
    	
    	}
    	return;
    }
    
    unsigned char SetDelay1ms(){
    		return arg;
    }
    
    void GetDelay1ms(unsigned char arg){
    		Timer1ms=arg;
    		return;
    }
    
    //Przerwanie od timera1 wywolywane co 1ms
    void Timer1_Int(){
    	if(Timer1ms) Timer1ms--;
    
    
    }
    


    Dzięki takiej konstrukcji zachowuje pozory wielowątkowości. Chyba powyższy kod nie wymaga objaśnień;)

    O funkcji typu _delay_ms() lepiej zapomnij, chyba że uC zajmuje się tylko 1 rzeczą i w międzyczasie inne zadania nie muszą być wykonywane.
  • #6 7321842
    mirekk36
    Poziom 42  
    pburczyn --> a nie pomyliły ci się funkcje ?

    unsigned char SetDelay1ms(){ 
          return arg; 
    } 
    
    void GetDelay1ms(unsigned char arg){ 
          Timer1ms=arg; 
          return; 
    } 


    nie powinny być zamienione tu nazwami albo zawartościami ? ;)

    Dodano po 6 [minuty]:

    poza tym, tak z ciekawości dopytam odnośnie twojego sposobu - czy u ciebie nie będzie problemu jeśli ta pierwsza funkcja1(); ustawi sobie Timer1ms - to druga funkcja może to zmienić przecież za pomocą SetDelay1ms. Więc troszkę się kołomyja zrobi chyba?
  • #7 7322252
    pburczyn
    Poziom 12  
    Faktycznie powinno być na odwrót. Tak to jest, gdy się pisze coś na szybko:)

    Cytat:
    czy u ciebie nie będzie problemu jeśli ta pierwsza funkcja1(); ustawi sobie Timer1ms - to druga funkcja może to zmienić przecież za pomocą SetDelay1ms. Więc troszkę się kołomyja zrobi chyba?


    Problem oczywiście będzie. Dlatego każda funkcja (funkcja1(); funkcjia2()) powinna korzystać z innej zmiennej do odliczania czasu. Mozesz zrobić np. tak:
    
    //Przerwanie od timera1 wywolywane co 1ms 
    void Timer1_Int(){ 
       //Zaladowanie liczników odpowiednimi wartościami
       ...
    
       if(TimerA_1ms) TimerA_1ms--; 
       if(TimerB_1ms) TimerB_1ms--; 
       if(TimerC_1ms) TimerC_1ms--;
    
       Timer1_100ms++;
       if (Timer1_100ms==100){  //minelo 100ms
          Timer1_100ms=0;
    
          if(TimerA_100ms) TimerA_100ms--; 
          if(TimerB_100ms) TimerB_100ms--; 
          if(TimerC_100ms) TimerC_100ms--;
    
       }
    }
    


    W ten sposób można realizować odmierzanie rożnych wartości czasu w rożnych częściach programu.
  • #8 7322496
    mirekk36
    Poziom 42  
    pburczyn --> ano o tym właśnie myślałem , że w tym sposobie trzeba byłoby dać jeszcze oddzielne dla każdego twojego pseud-wątku zmienne odnośnie czasu - chociaż wtedy będzie zachodził problem z synchronizacją czasu w tych ala-wątkach ;)

    ale można sobie z tym poradzić wprowadzając tak jak np ja to robię jeszcze jedną zmienną, która w przerwaniu timera przekręca się co np 1000ms (czyli co 1sekundę) - a później w kodzie poszczególnych wątków można się wg niej synchronizować tak aby np jeden wątek mógł wykonywać się dokładnie jakiś równy określony czas po drugim. Po prostu za pomocą dzielenia modulo % tej zmiennej można szybko się zorientować w takim wątku - w którym miejscu, że tak powiem czasoprzestrzeni się akurat znajduje ;)
  • #9 7322692
    pburczyn
    Poziom 12  
    Nie bardzo wiem, co chcesz osiągnąć...

    Sposób synchronizacji z tym timerem 1s wydaje się być trochę dziwny. Chyba już lepiej w jednym wątku zakładać timer, a w drugim wątku sprawdzać, czy określony czas już upłynął...

    Jeśli napiszesz, co dokładnie chcesz osiągnąć, to łatwiej będzie mi podsunąć jakąś koncepcje rozwiązania.
  • #10 7322763
    mirekk36
    Poziom 42  
    pburczyn --> no fakt biorąc pod uwagę całość takiej konstrukcji/szkieletu jaką przedstawiłeś to to rzeczywiście lepiej zrobić tak jak piszesz czyli zakładać timer w wątku a w drugim go sprawdzać ;) racja
  • #11 7324514
    janbernat
    Poziom 38  
    Pozwolę się podłączyć.
    Pytanie do mirekk36.
    Czy takie coś:

    
    '$sim
    $regfile = "M168def.dat"
    
    $crystal = 16000000
    
    Config Lcdpin = Pin , Db7 = Portc.0 , Db6 = Portc.1 , Db5 = Portc.2 , Db4 = Portc.3 , E = Portc.4 , Rs = Portc.5
    Cls
    Lcd "OK"
    Waitms 100
    Config Portb = Output
    Set Portb.1
    
    Config Portd = Input
    Portd = 255
    Sw0 Alias Pind.0
    Enable Interrupts
    
    Tccr0b = 1
    Enable Ovf0
    On Ovf0 Klawiatura
    
    Dim Flaga0 As Byte
    Dim Zadana_liczba_krokow As Word
    Dim Aktualna_liczba_krokow As Word
    Dim Temp0 As Byte
    Dim Temp0_incr As Byte
    
    Declare Sub Ustawianie
    Declare Sub Wykonywanie
    
    Dim Czest As Byte
    Czest = 255
    Ocr2a = Czest
    Tccr2a = 66
    Tccr2b = 4
    Timsk2 = 2
    
    
    
    Do                                                             'gdy jest przerwanie
       If Flaga0 = 1 Then                                       'i jest wciśnięty przycisk sw0
          If Sw0 = 0 Then
             If Temp0 = 0 Then
                Toggle Portb.1
                Temp0 = 1
             End If
          Else
             Temp0 = 0
          End If
    
    
          Decr Czest
          Ocr2a = Czest
    
          Cls
          Lcd Czest
          'Waitms 10
    
    
             If Czest = 1 Then
                Czest = 255
             End If
       Else
          Flaga0 = 0
       End If
    Loop
    
    End
    
    
    Klawiatura:
    
       Flaga0 = 1
       Return


    spełnia warunek odptywania co 13 ms?
    Czy jest w tym jakaś pułapka?
    Ogólnie działa.
    Trochę się niepewnie przełącza.
    Przyciski na tej płytce propox-u jeszcze nie zmienione- podejrzewam że wstawili jakieś za 0.001$.
    Ale zanim wszystkie zmienię- może coś głupiego w kodzie.
  • #12 7324939
    mirekk36
    Poziom 42  
    janbernat --> tak na pierwszy rzut oka to w tym rozwiązaniu do obsługi klawiatury nie ma jednak żadnego mechanizmu eliminacji drgań styków i stąd może być to wrażenie, że niepewnie się przełącza itp moim zdaniem.

    Zobacz, naciskamy klawisz - rozpoczynają się drgania (zawsze występują prawda?) u ciebie od razu na pierwszy sygnał drgania styku reaguje przerwanie. Ustawiana jest flaga. Oczywiście cały mechanizm przerwania bardzo ładnie rozwiązany z tą flagą. Jednak bezpośrednio po

    If Flaga0 = 1 Then  


    wstawiłbym chociaż jakiś mały



    czyli odczekać aż zakończą się drgania i dopiero wtedy sprawdzić czy rzeczywiście słicz jest wciśnięty. Myślę że już to może dużo pomóc i może się okazać, że wcale nie będzie trzeba wymieniać słiczy.

    Tak się zastanawiam jednak czy nie byłoby jednak o wiele korzystniejsze zastosowanie w takim przypadku jednak Bascomowego polecenia Debounce z wykorzystaniem skoku do podprogramu w którym to właśnie mógłbyś spokojnie zrobić sobie te wszystkie operacje.

    Z drugiej strony to podziwiam kolegę, że do konfiguracji PWM i nie tylko używasz bezpośrednio rejestrów i do nich wpisujesz wartości zamiast posługiwać się Bascomowymi wynalazkami jak Config Timer ........
    Podobnie z odpalaniem przerwań itp .... wiadomo Bascom to dopuszcza i bardzo dobrze

    ale to już bardzo, krótka droga żeby ten sam program napisać w C gdzie o wiele wygodniej ustawia się takie rejestry i można to robić o wiele bardziej przejrzyście. Ale to tylko tak na marginesie - bo uważam, że przy takim podejściu to kolega łyknąłby C jak nic ;) ..... polecam
  • #13 7328698
    janbernat
    Poziom 38  
    mirekk36- w jednej odpowiedzi namawiasz mnie do złego i do dobrego.
    Nie po to zrobiłem w przerwaniu tylko flagę żeby wkładać tam wait.
    Apage Satanas- Wait w przerwaniu!?
    W głownej pętli zrobiłem przerzutnik za pomocą temp0 aby wyeliminować drgania przycisku.
    Zrobienie przerzutnika programowo- to dla mnie wyczyn.
    Sprzętowo- to proste.
    Tylko 13 ms- może za mało.
    Ale po zmianie słicza-działa.
    Moje podejrzenie na słicza wzięło sie stąd że reset musiałem naciskać 5 razy aby zadziałał.
    Jeszcze próbowałem to zrobić uzywając GPIOR0- z opcją nosawe- bo tam
    Bascom nie sięga- ale chyba jednak sięga- program zgłupiał.
    Jeszcze to sprawdzę.
    A namawianie do dobrego- to nauczyć się C.
    Ale C to nie jest tylko rozumienie rejestrów- to proste.
    Albo rozumienie wskaźników- to oczywiste że każda komórka pamięci ma swój adres i zawartość.
    Zrozumiałe dla mnie jest cos poniżej asm.
    Ale ta składnia...
    I te poziomy optymalizacji...
    Mam taką ksiąźkę- "Wysokie C"- częściowo przeczytaną.
    Ile tam jest tłumaczeń na asm...
    I ile razy z tych tłumaczeń wychodzi "przeklęta" instrukcja goto...
    Ale C się nauczę- jak zdążę.
REKLAMA