Elektroda.pl
Elektroda.pl
X
Relpol przekaźniki
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[Atmega8][C/winavr]Dziwne zachowanie zmiennej

02 Lut 2011 16:43 1810 12
  • Poziom 10  
    Witam! Skonstruowałem sobie urządzenie, które ma służyć do optymalnego ustawienia sterownika kotła. Mierzy temperatury na zewnątrz i wewnątrz, liczy ich średnie, minima i maksima, jest podłączone do sterownika kotła pod przekaźnik (przez transoptor) i zlicza ilość załączeń podajnika w różnych trybach pracy i sumaryczny czas. Od dłuższego czasu walczę z dziwnym zachowaniem jednej ze zmiennych w moim programiku i nie mogę dojść dlaczego tak się dzieje. Oto fragment programu:

    Code:

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include "delay.h"
    #include "lcd.h"
    #include "ds18x20.h"

    volatile uint16_t   LED_time,
                   avg_time,
                   on_time,
                   temp_time,
                   ust_time,
                   menu_time;
              
    volatile uint8_t    pomiar,
                   wynik_pomiaru_sygn,
                   up_button,
                   down_button, 
                   LED_on;
                
    void init(void)
    {
       PORTB |= (1 << PB3);    // nieużywane porty jako wejścia i pull-up
       PORTB |= (1 << PB4);
       PORTB |= (1 << PB5);
       PORTB |= (1 << PB6);
       PORTB |= (1 << PB7);
       PORTC |= (1 << PC0);
       PORTC |= (1 << PC1);
       PORTC |= (1 << PC2);
       PORTC |= (1 << PC3);
       
       //========== PRZYCISKI DÓŁ/GÓRA ===============
       //DDRD &=~ (1 << PD2);  // pin PD2 jako wejście
        PORTD |= (1 << PD2);     // wewnętrzny pull-up
       //DDRD &=~ (1 << PD3);  // pin PD3 jako wejście
        PORTD |= (1 << PD3);     // wewnętrzny pull-up
       
       //======= WEJŚCIE SYGNAŁU Z PODAJNIKA =========
       //DDRB &=~ (1 << PB0);  // pin PB0 jako wejście
       
       //=========== PODŚWIETLENIE LCD ===============
       DDRC |= (1 << PC5);     // pin PC5 jako wyjście
       PORTC &= ~(1 << PC5);  // stan niski(podświetlenie włączone) na PC5
       
       //=========== PRZYCISK USTAWIEŃ ===============
       //DDRC &= ~(1 << PC4);  // pin PC4 jako wejście
       PORTC |= (1 << PC4);     // wewnętrzny pull-up na PC4
       
       LCD_init();
       LCD_PL_chars_init();
       LED_on=1;
       
       OCR2=245;            // ustawienia do CTC dla timera2 - regulacja dokladnosci - zwiększ aby spowolnic (liczenie czasu działania od reset/power-on)
       TIMSK = (1<<OCIE2)|(1<<TOIE0)|(1<<TICIE1);             // włączenie przerwań od przepełnienie timera0 + włączenie przerwań dla ICP1;
       TCCR2 = (1<<WGM21)|(1<<CS22)|(1<<CS21)|(1<<CS20);       // start timera2, preskaler 1024
       MCUCR = (1<<ISC01)|(0<<ISC00)|(1<<ISC11)|(0<<ISC10);    // przerwania na INT0 oraz INT1 wyzwalane przez zbocze opadające
       GICR  = (1<<INT0)|(1<<INT1);                           // załączenie przerwania na INT0 i INT1
       TCCR1B = (1<<ICNC1)|(0<<ICES1);                    // Input Capture Noise Canceler+wyzwalanie na zbocze opadające
       TIFR = (1<<ICF1);
       TCCR0=0x05;                                  // włączenie timera0 8-bitowego (prescaler 1024)
       GIFR=0xC0;                                    // wyczyszczenie flag przerwań INT0 i INT1 przed włączeniem przerwań globalnie
       sei();
    }

    int main( void )
    {    
       int32_t         suma_temp_in=0,
                   suma_temp_out=0,
                   licznik=0;
                   
       uint16_t       s_przep=0,
                   m_przep=0,
                   s_dogrz=0,
                   m_dogrz=0,
                   przepalanie_licznik=0,              // przechowuje ilość włączeń podajnika w trybie przepalania
                   dogrzewanie_licznik=0,                // przechowuje ilość włączeń podajnika w trybie dogrzewania
                   przepalanie_24h=0,                   // przechowuje stan ilości włączeń podajnika w trybie przepalania na 24. godzinę
                   dogrzewanie_24h=0,
                   przepalanie_12h=0,
                   dogrzewanie_12h=0,
                   czas_podawania_dogrz=0,
                   czas_podawania_przep=0;      
                      
       int16_t         decicelsius1=0,
                   decicelsius2=0,
                   temp1=0,
                   temp2=0,
                   temp2temp=0,
                   sredniaIN=0,
                   sredniaOUT=0,
                   sredniaIN12h=0,
                   sredniaOUT12h=0,
                   sredniaIN24h=0,
                   sredniaOUT24h=0,
                   minIN=999,
                   minOUT=999,
                   maxIN=-999,
                   maxOUT=-999;
                
       uint8_t       subzero1,
                   cel1,
                   cel_frac_bits1,
                   subzero2,
                   cel2,
                   cel_frac_bits2,
                   temp_read_done=1,           // umożliwienie rozpoczęcia pomiaru temperatur
                   start=1,               // umożliwienie rozpoczęcia obliczeń średnich temperatur
                   synchr=0,
                   bylem_w_menu=0,
                   ust=0,
                   menu=0,
                   save_menu=0,
                   sekundy=0,
                   minuty=0,
                   godziny=0,
                   dni=0,
                   podajnik_przepalanie=5,      //ustawienie ze sterownika kotła: czas pracy podajnika przy przepalaniu
                   podajnik_dogrzewanie=15;   //ustawienie ze sterownika kotła: czas pracy podajnika przy dogrzewaniu
                   
       init();

       while(1)
       {
          // ============== Pomiar temperatur IN, OUT ===============
          
          if(temp_read_done)
          {
             OW_set_bus(&PORTB,2);                                        //szyna nr 1 termometru IN aktywna
             DS18X20_start_meas( DS18X20_POWER_PARASITE, NULL );                //komenda rozpoczęcia pomiaru termometru IN
             OW_set_bus(&PORTB,1);                                       //szyna nr 2 termometru OUT aktywna
             DS18X20_start_meas( DS18X20_POWER_PARASITE, NULL );                //komenda rozpoczęcia pomiaru termometru OUT
             temp_read_done=0;
             temp_time=0;
          }
          //delayms(DS18B20_TCONV_12BIT);                                  //delay potrzebny do wykonania pomiaru dla metody pasożytniczej
          if(temp_time>6)
          {
             OW_set_bus(&PORTB,2);                                        //szyna nr 1 termometru IN aktywna
             DS18X20_read_meas_single(0x28, &subzero1, &cel1, &cel_frac_bits1);     //odczyt pomiaru dla termometru IN
             decicelsius1 = DS18X20_temp_to_decicel(subzero1, cel1, cel_frac_bits1);
             OW_set_bus(&PORTB,1);                                        //szyna nr 2 termometru OUT aktywna
             DS18X20_read_meas_single(0x28, &subzero2, &cel2, &cel_frac_bits2);    //odczyt pomiaru dla termometru OUT
             decicelsius2 = DS18X20_temp_to_decicel(subzero2, cel2, cel_frac_bits2);
             temp_read_done=1;
          }
          
          
          // ===== liczenie czasu jaki uplynal od resetu/startu =====

          if (on_time>7)                        
          {
             on_time-=8;
             sekundy++;
             if(sekundy==60)
             {
                minuty++;
                sekundy=0;
                if(minuty==60)
                {
                   godziny++;
                   minuty=0;
                   if(godziny==24)
                   {
                      dni++;
                      godziny=0;
                   }
                }
             }
          }
          
          // ====== Obliczanie minimalnych, maksymalnych oraz średnich temperatur ==========
          
          if (avg_time>30)                        // temp do sredniej co ok 5 sekund
          {   
             avg_time=0;
             if(((abs(abs(temp1)-decicelsius1)<50)&&(abs(abs(temp2)-decicelsius2)<50))||start)  // do średniej tylko wtedy, gdy poprzedna właściwa różna od obecnej o maks 5 stopni (eliminacja błędów)
             {
                if(subzero1)
                   temp1=-decicelsius1;
                else
                   temp1=decicelsius1;
                if(subzero2)
                   temp2=-decicelsius2;
                else
                   temp2=decicelsius2;
                licznik++;
                suma_temp_in+=temp1;
                suma_temp_out+=temp2;
                sredniaIN=suma_temp_in/licznik;
                sredniaOUT=suma_temp_out/licznik;
                temp2temp=decicelsius2;
                if(temp1<minIN)
                   minIN=temp1;
                if(temp1>maxIN)
                   maxIN=temp1;
                if(temp2<minOUT)
                   minOUT=temp2;
                if(temp2>maxOUT)
                   maxOUT=temp2;
                start=0;
             }
             
             if(dni==0)
             {
                sredniaIN24h=sredniaIN;
                sredniaOUT24h=sredniaOUT;
             }
             if(godziny<12&&dni==0)
             {
                sredniaIN12h=sredniaIN;
                sredniaOUT12h=sredniaOUT;
             }
          }
          
          // ====== Obliczanie ilości i czasów załączeń podajnika kotła w zależności od trybu =======
          
          if(wynik_pomiaru_sygn==1)            // jeśli wynik pomiaru długości sygnału gotowy
          {
             wynik_pomiaru_sygn=0;
             if((ICR1*0.512/1000)<6)
                przepalanie_licznik++;
             else
                dogrzewanie_licznik++;

             if(dni<1)
             {
                przepalanie_24h=przepalanie_licznik;
                dogrzewanie_24h=dogrzewanie_licznik;
             }
             if(godziny<12&&dni==0)
             {
                przepalanie_12h=przepalanie_licznik;
                dogrzewanie_12h=dogrzewanie_licznik;
             }
             czas_podawania_dogrz=dogrzewanie_licznik*podajnik_dogrzewanie;   // całkowity czas podawania węgla w trybie dogrzewania w sekundach
             czas_podawania_przep=przepalanie_licznik*podajnik_przepalanie;
             s_przep=czas_podawania_przep%60;
             m_przep=czas_podawania_przep/60;
             s_dogrz=czas_podawania_dogrz%60;
             m_dogrz=czas_podawania_dogrz/60;
          }
          
          // ======== Sterowanie podświetleniem wyświetlacza LCD ========
          
          if(LED_time>=210)                  // czas świecenia się podświetlenia po wciśnięciu przycisku
          {
             LED_on=0;                     // wyłącz liczenie czasu podświetlenia
             LED_time=0;
             PORTC |= (1 << PC5);              // stan wysoki na PC5 (wył podświetlenie)
          }
          
          // =============== Obsługa menu ==================
       
          if(menu_time>=2)
          {
             menu_time=0;
             if(down_button==1)               // jeśli został wciśnięty przycisk w dół (INT0 interrupt)
             {
                LED_time=0;                  // wyzeruj licznik czasu działania podświetlenia
                LED_on=1;                  // włącz licznik czasu podświetlenia
                bylem_w_menu=1;
                if(menu<10)                  // jeśli da się pójść w menu niżej
                {
                   menu++;
                   LCD_clear();
                }
                else if(menu==11)
                   podajnik_dogrzewanie--;      // czas podawania w trybie dogrzewania (ustawienie z kotła) -> zmniejsz
             }
             else if(up_button==1)            // jeśli został wciśnięty przycisk w górę (INT1 interrupt)
             {
                LED_time=0;
                LED_on=1;                  
                bylem_w_menu=1;
                if(menu>0&&menu<=10)         // jeśli da się pójść w menu wyżej
                {
                   menu--;
                   LCD_clear();            
                }
                else if(menu==11)
                   podajnik_dogrzewanie++;
             }
             switch(menu)
             {
                case 0:   
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("IN      OUT"));
                   LCD_xy(0,1);
                   LCD_putchar((subzero1)?'-':'+');
                   LCD_putint( (decicelsius1/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (decicelsius1%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putchar((subzero2)?'-':'+');
                   LCD_putint( (decicelsius2/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (decicelsius2%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;
                case 1:
                   LCD_xy(0,0);
                   //LCD_putstr_P(PSTR("średIN  średOUT"));
                   LCD_putint(((abs(abs(temp1)-decicelsius1)<50)&&(abs(abs(temp2)-decicelsius2)<50)),10);
                   LCD_putchar(' ');
                   LCD_putint(temp2,10);
                   LCD_putchar(' ');
                   LCD_putint(temp2temp,10);
                   //LCD_putchar(' ');
                   //LCD_putint(tempb,10);
                   //LCD_putchar(' ');
                   LCD_xy(0,1);
                   //LCD_putint(licznik,10);
                   LCD_putchar((sredniaIN<0)?'-':'+');
                   LCD_putint( (abs(sredniaIN)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaIN)%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C'); //licznik=1305, temp2=1012, temp1=285, srtempout=-3stopnie
                   LCD_putchar(' ');            
                   LCD_xy(8,1);
                   LCD_putchar((sredniaOUT<0)?'-':'+');
                   LCD_putint( (abs(sredniaOUT)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaOUT)%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;
                case 2:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("minIN   maxIN"));
                   LCD_xy(0,1);
                   LCD_putchar((minIN<0)?'-':'+');
                   LCD_putint( (abs(minIN)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(minIN)%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putchar((maxIN<0)?'-':'+');
                   LCD_putint( (abs(maxIN)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(maxIN)%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;
                case 3:   
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("minOUT  maxOUT"));
                   LCD_xy(0,1);
                   LCD_putchar((minOUT<0)?'-':'+');
                   LCD_putint( (abs(minOUT)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(minOUT)%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putchar((maxOUT<0)?'-':'+');
                   LCD_putint( (abs(maxOUT)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(maxOUT)%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;
                case 4:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("Przepal Dogrzew"));
                   LCD_xy(0,1);
                   LCD_putint( przepalanie_licznik,10);
                   LCD_xy(8,1);
                   LCD_putint( dogrzewanie_licznik,10);
                   break;
                case 5:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("Przep[t]Dogrz[t]"));
                   LCD_xy(0,1);
                   LCD_putint( m_przep,10);
                   LCD_putchar('m');
                   LCD_putint( s_przep,10);
                   LCD_putchar('s');
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putint( m_dogrz,10);
                   LCD_putchar('m');
                   LCD_putint( s_dogrz,10);
                   LCD_putchar('s');
                   LCD_putchar(' ');
                   break;
                case 6:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("Czas pomiaru"));   
                   LCD_xy(0,1);
                   LCD_putint( dni,10);
                   LCD_putchar('d');
                   LCD_putchar(' ');
                   LCD_putint( godziny,10);
                   LCD_putchar('h');
                   LCD_putchar(' ');
                   LCD_putint( minuty,10);   
                   LCD_putchar('m');
                   LCD_putchar(' ');
                   LCD_putint( sekundy,10);   
                   LCD_putstr_P(PSTR("s   "));
                   break;   
                case 7:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("Stan na 12.h"));   
                   LCD_xy(0,1);
                   LCD_putchar('P');
                   LCD_putchar('=');   
                   LCD_putint( przepalanie_12h,10);
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putchar('D');
                   LCD_putchar('=');
                   LCD_putint( dogrzewanie_12h,10);
                   LCD_putchar(' ');
                   break;
                case 8:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("śrIN12h śrOUT12h"));
                   LCD_xy(0,1);
                   LCD_putchar((sredniaIN12h<0)?'-':'+');
                   LCD_putint( (abs(sredniaIN12h)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaIN12h)%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');            
                   LCD_xy(8,1);
                   LCD_putchar((sredniaOUT12h<0)?'-':'+');
                   LCD_putint( (abs(sredniaOUT12h)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaOUT12h)%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;   
                case 9:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("Stan na 24.h"));   
                   LCD_xy(0,1);
                   LCD_putchar('P');
                   LCD_putchar('=');
                   LCD_putint( przepalanie_24h,10);
                   LCD_putchar(' ');
                   LCD_xy(8,1);
                   LCD_putchar('D');
                   LCD_putchar('=');   
                   LCD_putint( dogrzewanie_24h,10);
                   LCD_putchar(' ');
                   break;
                case 10:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("śrIN24h śrOUT24h"));
                   LCD_xy(0,1);
                   LCD_putchar((sredniaIN24h<0)?'-':'+');
                   LCD_putint( (abs(sredniaIN24h)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaIN24h)%10) + '0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');            
                   LCD_xy(8,1);
                   LCD_putchar((sredniaOUT24h<0)?'-':'+');
                   LCD_putint( (abs(sredniaOUT24h)/10) ,10);
                   LCD_putchar(',');
                   LCD_putchar( (abs(sredniaOUT24h)%10) +'0');
                   LCD_putchar(0xdf);
                   LCD_putchar('C');
                   LCD_putchar(' ');
                   break;
                case 11:
                   LCD_xy(0,0);
                   LCD_putstr_P(PSTR("RetorUstawionyNa"));   
                   LCD_xy(0,1);
                   LCD_putint( podajnik_dogrzewanie,10);
                   LCD_putchar('s');   
                   LCD_putchar(' ');
                   break;
             }
          }
          
          // ====== Obsługa dla przycisku do zmiany wartości zmiennej 'podajnik_dogrzewanie' =======
          if(ust_time>50)
          {
             ust_time=0;
             if(bit_is_clear(PINC,PC4)&&ust==0)
             {
                PORTC &= ~(1 << PC5);
                LCD_init();
                LCD_PL_chars_init();
                LED_time=0;
                LED_on=1;
                LCD_clear();
                save_menu=menu;
                menu=11;
                ust=1;
             }
             else if(bit_is_set(PINC,PC4)&&ust==1)
             {
                LCD_clear();
                menu=save_menu;
                ust=0;
             }
          }
          
          // ===== Obsługa przycisków góra/dół ======
          
          if((up_button||down_button)&&bylem_w_menu)
          {
             up_button=0;
             down_button=0;
             GIFR=0xC0;                     //skasuj flagę przerwań dla INT0 i INT1  (wpisując jeden!!!)
             if((menu!=10)&&(menu!=0))
                GICR |= (1<<INT0)|(1<<INT1);   //włącz przerwania dla INT0 i INT1
             else if(menu==0)
                GICR |= (1<<INT0);            //włącz przerwania dla INT0
             else if(menu==10)
                GICR |= (1<<INT1);            //włącz przerwania dla INT1
             bylem_w_menu=0;
          }
       }
    }

    ISR(TIMER0_OVF_vect, ISR_NOBLOCK)
    {
       ust_time++;
       avg_time++;
       temp_time++;
       if(LED_on)
          LED_time++;
       menu_time++;
    }

    ISR(TIMER1_CAPT_vect)
    {
       if (pomiar==0)
       {
          TCNT1 = 0;                                       // zerowanie timera
          TCCR1B |=(1<<ICES1);                        // przerwanie na zbocze narastające
          TCCR1B |= ((1 << CS10)|(1 << CS12))&~(1 << CS11);   // preskaler 1024 (załączenie timera)
          pomiar = 1;
       }
       else
       {
          TCCR1B &= ~((1<<CS10)|(1<<CS11)|(1<<CS12));      // Wylaczenie timera1
          TCCR1B &= ~(1<<ICES1);                      // przerwanie na zbocze opadające
          pomiar = 0;
          wynik_pomiaru_sygn=1;
       }
    }

    ISR(TIMER2_COMP_vect, ISR_NOBLOCK)
    {
       on_time++;
    }

    ISR(INT0_vect, ISR_NOBLOCK)
    {
       PORTC &= ~(1 << PC5);
       GICR &= ~((1<<INT0)|(1<<INT1));   //wyłączenie przerwań na INT
       down_button=1;
    }

    ISR(INT1_vect, ISR_NOBLOCK)
    {
       PORTC &= ~(1 << PC5);
       GICR &= ~((1<<INT0)|(1<<INT1));    //wyłączenie przerwań na INT
       up_button=1;
    }


    Wszystko chodzi w miarę dobrze przez kilka godzin, po czym zmienna temp2 (czujnik z zewnątrz, a więc temperatury ujemne, dla temp1 czyli wartości z czujnika z wewnątrz wszystko jest OK) przybiera dziwną wartość typu np. 1010 (podczas gdy temperatura na zewnątrz wynosi np. -2,5, czyli, dla subzero2=1 oraz decicelsius2=25, powinno być -25 w tej zmiennej), automatycznie, po kolejnym przejściu nieskończonej pętli głównej, nie spełnia się warunek wejścia w powyższy fragment kodu i przestają mi się liczyć średnie, minima i maksima. Dla sprawdzenia dodałem tam zmienną temp2temp, którą również wyświetlam pod koniec pętli głównej programu i, o dziwo, w zmiennej temp2temp, w chwili wykrzaczenia zmiennej temp2, wartość zatrzaśnięta po ostatnim wejściu programu w ten warunek jest poprawną wartością temperatury (25), czyli zmienna decicelsius2, przypisywana zarówno do temp2 jak i do temp2temp, jest poprawna w chwili ostatniego wejścia w ten warunek. Skończyły mi się pomysły na znalezienie przyczyny, może Wam się uda?

    Zauważyłem też (podczas kombinacji ze zmniejszeniem kodu wynikowego, gdy nie chciał mi się zmieścić już na atmegę), że kolejność deklaracji zmiennych różnych typów przekłada się na rozmiar pliku hex.. po ułożeniu deklaracji typów od największego do najmniejszego dało w rezultacie najmniejszy rozmiar kodu wynikowego. Dlaczego tak się dzieje?
  • Relpol przekaźniki
  • Pomocny post
    Poziom 38  
    A jednak w pełnym kodzie masz kilka przerwań.
    I dwa z nich są NOBLOCK.
    To zobaczmy co piszą na ten temat:

    #define ISR_NOBLOCK

    # include <avr/interrupt.h>

    ISR runs with global interrupts initially enabled. The interrupt enable flag is activated by the compiler as early as possible within the ISR to ensure minimal processing delay for nested interrupts.

    This may be used to create nested ISRs, however care should be taken to avoid stack overflows, or to avoid infinitely entering the ISR for those cases where the AVR hardware does not clear the respective interrupt flag before entering the ISR.

    Use this attribute in the attributes parameter of the ISR macro.
  • Poziom 10  
    Dzięki za wskazanie błędu :) Chciałem dla jednego z przerwań zrobić wyższy priorytet od pozostałych poprzez dodanie do tych pozostałych opcji ISR_NOBLOCK, co powodowało najprawdopodobniej opisane wyżej przepełnianie stosu. Nie rozumiem tylko dlaczego tak się dzieje, ponieważ problemy ze zmienną pojawiają się w czasie, gdy możliwe są wystąpienia następujących przerwań (przycisków nie przyciskam w czasie pracy, więc te przerwania nie występują): jeden timer robi przerwania od przepełnienia, drugi dla CTC=245 (dla obu preskaler ustawiony na 1024) oraz co kilkanaście sekund załącza się przerwanie od ICP1 w momencie włączenia podajnika i wyłączenia. Zatem czasy pomiędzy kolejnymi przerwaniami są bardzo duże, czyli teoretycznie, w najgorszym przypadku, w jednym momencie mogą być uruchomione maksymalnie trzy przerwania, które po tym mają wystarczająco dużo czasu by się wykonać, nawet jeśli są zagnieżdżone. Tak z ciekawości.. ile takich przerwań może być maksymalnie zagnieżdżonych? Po usunięciu atrybutu ISR_NOBLOCK ze wszystkich procedur problem nie występuje.
  • Relpol przekaźniki
  • Poziom 38  
    No teraz w Twoim kodzie to masz cztery ISR_NOBLOCK.
    Priorytetu przerwań nie jesteś w stanie zmienić w avr.
    Aż takim znawcą C to nie jestem- na razie wiem czego unikać- wszystkiego czego do końca nie rozumiem.
    To wydaje się działać tak:
    Jeśli przerwanie o wyższym priorytecie jest NOBLOCK i wewnątrz niego pojawi się przerwanie o niższym priorytecie i nie zdąży się zakończyć przed zakończeniem tego pierwszego przerwania to stopniowo będzie zapełniać stos.
    Bo najpierw wykona się powtórnie przerwanie o wyższym prirytecie- to pierwsze.
    A jeśli się to powtórzy wielokrotnie to stos powolutku się przepełni.
    Mam nadzieję że jest choć trochę zrozumiałe- nie mam dużej wprawy w C.
    Ale może ktoś to bardziej precyzyjnie wyjaśni.
    Ja w takich sytuacjach używam diagramu http://meesoft.logicnet.dk/
  • Poziom 10  
    janbernat napisał:

    Jeśli przerwanie o wyższym priorytecie jest NOBLOCK i wewnątrz niego pojawi się przerwanie o niższym priorytecie i nie zdąży się zakończyć przed zakończeniem tego pierwszego przerwania to stopniowo będzie zapełniać stos.


    Gdy w trakcie obsługi przerwania pojawi się następne, drugie przerwanie, to to pierwsze zatrzymuje się, wykonuje się obsługa tego drugiego, a dopiero po wykonaniu tego drugiego powracamy do wykonywania pierwszego przerwania i kontynuujemy od miejsca, w którym wystąpiło przerwanie drugie, więc to pierwsze nie może się chyba skończyć szybciej niż to zagnieżdżone drugie.
  • Poziom 38  
    Wiedziałem że coś poplączę.
    A co będzie jeśli to drugie przerwanie (wewnątrz pierwszego) bedzie się wykonywało na tyle długo- nie długo w ogóle ale zostanie wywołane pod koniec pierwszego- że pojawi sie już ponowne wywołanie pierwszego przerwania?
  • Poziom 10  
    W takiej sytuacji wywoła się chyba kolejne zagnieżdżone przerwanie. Zgodzę się, że to jest możliwe, że jedna i ta sama procedura obsługi przerwania może zostać przerwana przez to samo przerwanie jeśli jeszcze się nie zakończyło wykonywać. Jest to możliwe wtedy, kiedy przerwania wywoływane są w małych odstępach czasowych (z perspektywy mikrokontrolera (MHz), a więc jakieś mikrosekundy). Ale u mnie pierwsze z trzech przerwań w najgorszym przypadku wywołuje się raz na kilkanaście sekund, a pozostałe dwa (dla preskalera 1024) co około 0,1s, w więc teoretycznie przerwania mają mnóstwo i jeszcze więcej czasu na zakończenie się, nawet w przypadku zagnieżdżenia, są to przecież krótkie procedury.
  • Poziom 24  
    No ale dlaczego kolega do temp2temp kopiuje wartość decicośtam, a nie temp2 ? To decicośtam podlega ewentualnie modyfikacji przy zapisywaniu do temp2 w zależności od subcośtam2. Analiza w tym wypadku jest zatem błędna. Po drugie, tak sobie oglądam, i zmienna licznik nigdy nie jest równana do zera poza uruchamianiem programu. Oznacza to, że przekręca się sama w nazwijmy to losowym momencie. A to tylko początek problemów, które udało mnie się wyłapać. Resztę sobie odpuszczam ze względu na nieczytelność kodu.

    Dodano po 1 [minuty]:

    A i jeszcze nowość. Zmienna licznik jest ze znakiem.
  • Poziom 10  
    temp2temp jest tylko do sprawdzenia, wykluczenia tego, że to zmienna decicelsius2 jest winna wykrzaczaniu temp2, co pokazało, że nie, bowiem w temp2 i w temp2temp przy wykrzaczeniu są inne wartości. Co do zmiennej licznik, to ona się ma nie zerować, ponieważ liczę średnią temperaturę przez cały czas działania urządzenia, a więc potrzebuję sumę wszystkich temperatur i ich liczbę, by policzyć średnią. Do średniej biorę temperaturę co pięć sekund, więc licznik się nie przepełni tak szybko (32 bity, prędzej się zdarzy, że się zresetuje bo prądu braknie w domu ;) ). Zmienna licznik była na początku bez znaku, ale okazało się, że dzielenie liczby ujemnej (signed) przez unsigned daje złe wyniki, ponieważ dokonuje się niejawna konwersja typu i przekręca mi się zmienna suma_temp_out, gdy jest wartością ujemną, do jakiejś kosmicznej liczby (pod koniec zakresu). Mogę oczywiście zastosować rzutowanie, ale tymczasowo zmieniłem licznik na signed.
  • Poziom 24  
    Liczenie średniej przez cały okres działania urządzenia jest błędem. Licznik przepełni się po <46h.
  • Poziom 10  
    Dlaczego? Z moich wyliczeń wychodzi, że licznik przepełni się po ponad 300... latach :) Licznik zwiększany co 5 sekund, a jest on 32 bitowy (Być może zmylił kolegę załączony fragment kodu, teraz wkleiłem całość). Program działa mi już poprawnie po usunięciu z procedur obsługi przerwań opcji ISR_NOBLOCK, działa któryś dzień z kolei i wszystko jest ok. Tylko nie rozumiem dlaczego miałby się z tą opcją przepełniać stos i wykrzaczać zmienne, kiedy dla każdego z przerwań każde kolejne jego wywołanie następuje po długim okresie czasu.
  • Poziom 24  
    Ja policzyłem nieprawidłowo, bo dla 16 bit, a tam są 32.
  • Poziom 38  
    Wklejaj nowe wersje kodu w kolejnych postach.
    Bo tak to rozumiem że kod działa bez NOBLOCK a pierwszy kod zmieniony 4 razy jest z NOBLOCK i co?
    Działa czy nie?
    Poza tym sprawdza się stara zasada że napisanie kodu to jest 10% pracy a odpluskwianie to pozostałe 90%.
    Analizowanie programu przy czterech przerwaniach NOBLOCK to jest katorżnicza praca.
    Przecież przerwanie jest to coś co pojawia się w programie niespodziewanie.
    Przy jednym NOBLOCK należy przeprowadzić dokładną analizę programu i wszystko sprawdzić.
    Przy czterech NOBLOCK to ja bym się bał.