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

[ATtiny13][c] int0 i timer0 - liczenie czasu

bkilian 03 Lis 2009 22:14 4094 4
  • #1 03 Lis 2009 22:14
    bkilian
    Poziom 9  

    Witam.
    To mój pierwszy post na forum. Mam na Imię Bartek i, jako samouk, elektroniką zajmuję się czysto amatorsko i hobbystycznie.
    Konstruuję spowalniacz do serw modelarskich, lecz napotkałem problem, z którym borykam się od dłuższego czasu. Aktualnie, aby zidentyfikować istotę problemu, "zdegradowałem" swoje docelowe rozwiązanie do następującej postaci (zmontowane na płytce uruchomieniowej):
    [ATtiny13][c] int0 i timer0 - liczenie czasu
    Otóż problem jest następujący:

    Za pomocą timera0 w ATtiny13 chcę mierzyć czas dodatniego impulsu pojawiającego się na nóżce odpowiadającej za INT0 mikrokontrolera. Aby podnieść rozdzielczość mierzonego czasu, timer wykonuje po kilka cykli. Docelowo będę mierzył ilość tych cykli, lecz na razie za każdym przepełnieniem timera przełączam stan na PB2. Także gdy występuje przerwanie na INT0 stan pinu PB2 jest przełączany (na przemian stan wysoki - stan niski). Ponieważ nie mam oscyloskopu, przebiegi rejestruję za pomocą wejścia LINE IN karty dźwiękowej komputera (wiem że powinienem podłączyć układ przez dzielnik 1:4, lecz myślę że obecne rozwiązanie nie ma wpływu na pracę układu i rezystory 1k w szeregu wystarczają). Niestety z zarejestrowanego przebiegu wynika, że timer nie zawsze się uruchamia, a gdy się już uruchamia to z dużym opóźnieniem (jako załącznik do tego postu dodałem pełny zarejestrowany przebieg w formacie wav):
    [ATtiny13][c] int0 i timer0 - liczenie czasu

    Co może być tego przyczyną? Pewnie popełniłem jakiś "czeski" błąd lecz do tej pory go nie wychwyciłem i liczę na Waszą pomoc.

    Oto plik Makefile którego używam:

    Code:

    # makefile written by Bartosz Kilian
    MCU=attiny13

    CC=avr-gcc
    OBJCOPY=avr-objcopy

    # options for programmer
    PARTNO=t13
    PROGRAMMER=stk200
    FUSEBIT_L=0x79
    FUSEBIT_H=0xFF

    # optimize for size:
    INCLUDEDIR=/usr/local/atmel/avr/include
    CFLAGS=-mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues -I$(INCLUDEDIR)
    #-------------------
    all: main.hex
    #-------------------
    main.hex : main.out
       $(OBJCOPY) -R .eeprom -O ihex main.out main.hex
    main.out : main.o
       $(CC) $(CFLAGS) -o main.out -Wl,-Map,main.map main.o




    main.o : main.c
       $(CC) $(CFLAGS) -Os -c main.c
    main.asm:
       $(CC) $(CFLAGS) main.c -S -o main.s
    # you need to erase first before loading the program.
    # load (program) the software into the eeprom:
    load: main.hex
       avrdude -p $(PARTNO) -c $(PROGRAMMER) -P /dev/parport0 -i 2 -U flash:w:main.hex -U lfuse:w:$(FUSEBIT_L):m -U hfuse:w:$(FUSEBIT_H):m

    clean:
       rm -f *.o *.map *.out
    #-------------------


    oraz wsad do mikrokontrolera:
    Code:

    /*********************************************
      File                : main.c
      Author              : Bartosz Kilian bkilian(malpa)interia.pl           
      Chip type           : ATtiny13
      Clock frequency     : 4.8 MHz (set the value of define F_CPU)
     *********************************************/
    // defines section
    // definitions for MCU Clock Frequency; adapt the MCU clock frequency in Hz to your target
    #ifndef F_CPU
    #define F_CPU 4800000UL 
    #endif
    // includes section
    // interrupts
    #include <avr/interrupt.h>
    // function bodies
    /*************************************************************************
    Interrupt: External Interrupt Request 0
    Purpose:   called when a change occurs on INT0 pin depending on ISC01 and ISC00 bits in MCUCR
    **************************************************************************/
    ISR(INT0_vect)
    {
       if (MCUCR & (1 << ISC00))
       {   // The rising edge generated an interrupt request
          // Reset counter register
          TCNT0 = 0;
          // Start counting
          // set prescaler division factor
          // CS00, CS01: select clock from prescaler: frequency = F_CPU / 8;
          // at F_CPU=4.8MHz Tmin=0,00166ms Tmax = Tmin * 2^8 = 0,4266...ms
          // T=1.0ms : TCNT0 = 2 timer cycles + 88
          // T=1.5ms : TCNT0 = 3 timer cycles + 133
          // T=2.0ms : TCNT0 = 4 timer cycles + 177
          TCCR0B |= (1 << CS01);
          
          //Set The falling edge of INT0 generates an interrupt request.
          MCUCR &= (~(1 << ISC00));
       }
       else
       {   // The falling edge generated an interrupt request
          
          // Stop counting
          TCCR0B &= (~(1 << CS01));

          //Set The rising edge of INT0 generates an interrupt request.
          MCUCR |= (1 << ISC00);
       }
       // toggle PORTB pin 2
       PINB |= (1 << PINB2);
    }

    /*************************************************************************
    Interrupt: Timer/Counter Overflow
    Purpose:   called when an overflow occurs in Timer/Counter0 (timer reaches TOP)
    **************************************************************************/
    ISR(TIM0_OVF_vect)
    {
       // toggle PORTB pin 2
       PINB |= (1 << PINB2);
    }
    /*
     * @brief   Main function body
     * @return  none
     */
    int main(void)
    {
       // Set Port B pin 2 as output
       DDRB =  (1 << DDB2);
       // Enable pull-up resistor at Port B pin 1 input
       PORTB |= (1 << PORTB1);

       // Init Timer/Counter0
       // COM0x1=0 and COM0x0=0 : Normal port operation, OC0A and OC1B disconnected.
       // WGM02=0 and WGM01=0 and WGM00=0 : Timer/Counter Mode of Operation: Normal
       // CS02=0 and CS01=0 and CS00=0 : Timer/Counter stopped
       TCCR0A &= (~((1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0) | (1 << WGM01) | (1 << WGM00)));
       TCCR0B &= (~((1 << WGM02) | (1 << CS02) | (1 << CS01) | (1 << CS00)));

       // Init Interrupt on INT0
       // Set The rising edge of INT0 generates an interrupt request.
       MCUCR |= (1 << ISC01) | (1 << ISC00);
       // External Interrupt Request 0 Enable
       GIMSK |= (1 << INT0);
       // Enable Interrupt on Timer/Counter Overflow
       TIMSK0 |= (1 << TOIE0);
       sei(); // enable interrupts
       while(1);
       return 0;
    }

    0 4
  • Relpol
  • Pomocny post
    #2 04 Lis 2009 09:51
    Dr.Vee
    VIP Zasłużony dla elektroda

    Nie wiem, czy Twoja metoda obserwacji sygnałów jest poprawna, ale podłączanie przycisku bez eliminacji drgań pod wejście przerwania jest przepisem na kłopoty. Jeśli chcesz testować tego typu układ, to musisz sobie zrobić generator czystych impulsów.

    Przykładowo: przycisk pod PB4, generacja impulsu na PB3, które zwierasz z PB1.
    W pętli głównej odczytujesz stan przycisku, stabilizujesz przez np. 40ms, jeśli stan jest stabilny i się zmienił, to kopiujesz stan na PORTB.3.

    Daj znać jak teraz będą wyglądały wyniki testów.

    Pozdrawiam,
    Dr.Vee

    0
  • Relpol
  • #3 04 Lis 2009 22:54
    bkilian
    Poziom 9  

    Dzięki Tobie chyba ruszę z miejsca z projektem. Wygląda na to, że przyczyną była jednak moja karta dźwiękowa, a właściwie obciążanie mikrokontrolera przez nią. Jak zmierzyłem to na pinie 1 portu B miałem 1V zamiast blisko 5V, a wiadomo, że jak pin pracuje w trybie wejścia z włączonym pull-upem to nie jest zbyt wydajny prądowo (sam nie wiem czemu wcześniej do tego nie doszedłem). Oto co zrobiłem: zgodnie z Twoją sugestią zniwelowałem jak Bozia przykazała drgania styków, ale bardziej popularnym sposobem - dałem kondensator 100nF równolegle z przyciskiem. Poza tym podłączyłem wejście karty dźwiękowej przez dzielnik rezystorowy - na linii przycisku 100kΩ : 680kΩ, a na linii wyjścia 1kΩ : 5,6kΩ i... jakby ruszyło :-)
    Oto obecny schemat:
    [ATtiny13][c] int0 i timer0 - liczenie czasu
    i przebieg:
    [ATtiny13][c] int0 i timer0 - liczenie czasu

    Dobra - brnę dalej z tym projektem. Nie zamykam jeszcze tematu, bo przerwanie int0 nie zawsze zaskakuje, ale na razie spróbuję sobie z tym poradzić. Poza tym jestem pewien, że wystąpią jeszcze jakieś problemy z tym timerem i obwodem wejścia int0.

    0
  • #4 04 Lis 2009 23:28
    mieczotronix
    Poziom 15  

    Nie musisz zatrzymywać i uruchamiać licznika za każdym razem, może on ciągle chodzić, wystarczy że go będziesz zerował. A tak naprawdę to jeśli chcesz potem generować przebieg dla serwa za pomocą tego samego timera, to nawet zerować go nie możesz.... tylko zapamiętywać jego wartość.
    Wygodniej się to robi jednak na 16-bitowym timerze, który ma jeszcze Input Capture. Wtedy pomiar impulsu jest bardzo dokładny. Już na attiny 2313 to chodzi miodzio.

    0
  • #5 05 Lis 2009 07:40
    bkilian
    Poziom 9  

    O licznik to raczej jestem spokojny. Bardziej mnie niepokoją te przerwania na int0.
    No 16-bitowym timerem to nie sztuka zrobić taki spowalniacz :-) ale akurat 2313 nie mam na stanie, a np. dużą ATmegę 8 pakować do modelu w takim celu to jak armata na muchy. Ale w przyszłości na pewno się zabiorę za jakiś tiny z 16-bitowym timerem.

    Jeżeli chodzi o uruchamianie i zatrzymywanie licznika to temat mam już niemal przećwiczony. Wcześniej napisałem działający program spowalniacza z wykorzystaniem 8-bitowego timera na ATtiny13 bez przekręcania licznika i bez regulacji spowolnienia i działał, lecz serwo przechodząc powoli do swojej docelowej pozycji robiło drobne, lecz zauważalne i uciążliwe skoki - stąd decyzja o zwiększeniu rozdzielczości. Oto ten kod (są w nim jeszcze pozostałości po diagnostyce, np. linia:
    PORTB &= !(1 << PORTB3); ):

    Code:

    /*********************************************
      File                : main.c
      Author              : Bartosz Kilian bkilian(malpa)interia.pl           
      Chip type           : ATtiny13
      Clock frequency     : 4.8 MHz (set the value of define F_CPU)
     *********************************************/
    // defines section
    // definitions for MCU Clock Frequency; adapt the MCU clock frequency in Hz to your target
    #ifndef F_CPU
    #define F_CPU 4800000UL 
    #endif

    // includes section
    // standard IO routines
    #include <avr/io.h>
    // interrupts
    #include <avr/interrupt.h>


    // global variables
    static uint8_t prev_OCR0A_value;
    // function bodies
    /*************************************************************************
    Interrupt: External Interrupt Request 0
    Purpose:   called when a change occurs on INT0 pin depending on ISC01 and ISC00 bits in MCUCR
    **************************************************************************/
    ISR(INT0_vect)
    {
       if (MCUCR & ((1 << ISC01) | (1 << ISC00)))
       {   // The rising edge generated an interrupt request
          // Reset counter register
          TCNT0 = 0;
          // Start counting
          // set prescaler division factor
          // CS00, CS01: select clock from prescaler: frequency = F_CPU / 64;
          // at F_CPU=4.8MHz Tmin=0,0133ms Tmax = Tmin * 2^8 = 3,413ms
          // T=1.0ms : TCNT0 = 75
          // T=1.5ms : TCNT0 = 112
          // T=2.0ms : TCNT0 = 150
          TCCR0B |= (1 << CS01) | (1 << CS00);
          
          //Set The falling edge of INT0 generates an interrupt request.
          MCUCR = (MCUCR | (1 << ISC01)) & !(1 << ISC00);
       }
       else
       {   // The falling edge generated an interrupt request
          
          // Stop counting
          TCCR0B &= !((1 << CS01) | (1 << CS00));

          if (TCNT0 > prev_OCR0A_value)
             prev_OCR0A_value++;
          else if (TCNT0 < prev_OCR0A_value)
             prev_OCR0A_value--;
          OCR0A = prev_OCR0A_value;
          // Reset counter register
          TCNT0 = 0;
          
          // set logical 1 on Port B bit 0
          PORTB |= (1 << PORTB0);
          PORTB |= (1 << PORTB3);
          //Set The rising edge of INT0 generates an interrupt request.
          MCUCR |= (1 << ISC01) | (1 << ISC00);
          // Start counting
          TCCR0B |= (1 << CS01) | (1 << CS00);
          // Enable Interrupt on Timer/Counter Output Compare Match A
          TIMSK0 = (1 << OCIE0A);
       }
    }

    /*************************************************************************
    Interrupt: Timer/Counter Compare Match A
    Purpose:   called when a Compare Match occurs between the Timer/Counter0 and the data in OCR0A Output Compare Register0
    **************************************************************************/
    ISR(TIM0_COMPA_vect)
    {
       // Stop counting
       TCCR0B &= !((1 << CS01) | (1 << CS00));
       
       // set logical 0 on Port B bit 0
       PORTB &= !(1 << PORTB0);
       PORTB &= !(1 << PORTB3);
       // Disable Interrupt on Timer/Counter Output Compare Match A
       TIMSK0 &= !(1 << OCIE0A);
       OCR0A=255; // don't know why, but without this line the program doesn't work properly, maybe bescause OCR0A is double buffered...
    }
    /*
     * @brief   Main function body
     * @return  none
     */
    int main(void)
    {
       // Set Port B pin 0 as output
       DDRB = (1 << DDB0) | (1 << DDB3);
       
       // Init Timer/Counter0
       // COM0x1=0 and COM0x0=0 : Normal port operation, OC0A and OC1B disconnected.
       // WGM02=0 and WGM01=0 and WGM00=0 : Timer/Counter Mode of Operation: Normal
       // CS02=0 and CS01=0 and CS00=0 : Timer/Counter stopped
       TCCR0A &= !((1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0) | (1 << WGM01) | (1 << WGM00));
       TCCR0B &= !((1 << WGM02) | (1 << CS02) | (1 << CS01) | (1 << CS00));

       // Init Interrupt on INT0
       // Set The rising edge of INT0 generates an interrupt request.
       MCUCR |= (1 << ISC01) | (1 << ISC00);
       // External Interrupt Request 0 Enable
       GIMSK |= (1 << INT0);
       prev_OCR0A_value = 112;
       sei(); // enable interrupts
       while(1);
       return 0;
    }

    0