Ένα απλό συχνόμετρο.

Στο παρόν άρθρο παρουσιάζω έναν απλό τρόπο για την κατασκευή ενός συχνόμετρου με έναν AVR ATmega8. Το συχνόμετρο εκμεταλλεύεται τις δυνατότητες των μετρητών (Timer/Counter) των AVR (για λεπτομέρειες σχετικά με του μετρητές δείτε εδώ και εδώ). Η συγκεκριμένη κατασκευή επιδεικνύει την αρχή λειτουργίας και δεν είναι ένα ολοκληρωμένο συχνόμετρο. Για παράδειγμα μετρά μόνο TTL σήμα (τετραγωνικό παλμό 0-5 volts). Για να μετρήσει κάποιος και άλλα σήματα (ημιτονικά, τριγωνικά) με μη συγκεκριμένο πλάτος αρκεί να βάλει πριν την είσοδο κάποιο κύκλωμα που θα μετατρέπει το σήμα σε TTL ίδια συχνότητας (Για παράδειγμα έναν συγκριτή σε συνδυασμό με κάποιο TTL Buffer).

Τεκμηρίωση του ATmega8.
Τα αρχεία του προγράμματος.

Πως μπορούμε λοιπόν να μετρήσουμε την συχνότητα ενός σήματος με έναν Timer/Counter ενός AVR; Ας δούμε τον 16-bit Timer/Counter1 του ATmega8 (που είναι και ο μικροελεγκτής που χρησιμοποιούμε στο κύκλωμα μας). Στις σελίδες 75 έως 101 της τεκμηρίωσης βλέπουμε πως ο Timer/Counter1 μπορεί να χρονιστεί από εξωτερικό σήμα στην ακίδα PD5(T1) (pin 11). Αυτό ελέγχεται από τα bit CS10, CS11, CS12 του καταχωρητή TCCR1B (πίνακας 40, σελίδα 99). Σε κάθε ανερχόμενο μέτωπο παλμού (CS10, CS11, CS12 = 1) ο Timer/Counter θα αυξάνει την τιμή του, καταχωρητής TCNT1, κατά ένα. Όταν πάρει την τιμή 65535 (16-bit) θα πάει στο 0 και θα προκαλέσει ένα interrupt υπερχείλισης. Αν μετρήσουμε των αριθμό των interrupt και γνωρίζουμε τον χρόνο που το σήμα οδηγούσε τον Timer/Counter1, μπορούμε να υπολογίσουμε την συχνότητα. Έστω πως εφαρμόζουμε ένα σήμα 1 MHz. Τότε ο μετρητής θα γεμίσει 1000000\65535=15 (ακέραια διαίρεση) φορές και η τρέχουσα τιμή θα είναι 1000000-15*65535=16975. Η συχνότητα σε Hz δίνετε από την σχέση:

Για να ελέγξουμε τον χρόνο που θα χρονίζεται ο Timer/Counter1 (gate time) από το σήμα προς μέτρηση χρησιμοποιούμε τον Timer0 που τον έχουμε ρυθμίσει να χτυπά κάθε 1 χιλιοστό του δευτερολέπτου. Έτσι ελέγχουμε τον χρόνο σε επίπεδο ενός msec.

Η μέγιστη συχνότητα που μπορεί να μετρηθεί χωρίς μεγάλο σφάλμα δίνεται από την συχνότητα χρονισμού του μικροελεγκτή προς την τιμή 2,5. Έτσι αν η συχνότητα χρονισμού είναι 16 MHz, μπορούμε να μετρήσουμε 6,4 MHz. Στον κώδικα μας αφήνω την πύλη ανοικτή για 1000 msec (1 δευτερόλεπτο).

Το σφάλμα σε σχέση με το όργανο στην γεννήτρια συχνοτήτων κυμαίνεται στο 0,02% για συχνότητες από 10 Hz έως 2.2352 MHz (που είναι και η μέγιστη συχνότητα της γεννήτρια που έχω). Λογικά μέχρι τα 6 MHz εκεί θα κυμαίνεται το σφάλμα.

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


/* TIMER0_PRELOAD_VAL=255-((F_CPU/Prescaler)*Time - 1).
   Για F_CPU 16 MHz, Time 1 msec είναι 6. */
#define TIMER0_PRELOAD_VAL 6


/* Baud rate calculations. */
#define BAUD 9600UL

#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // Smart roundup.
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))   // Real Baud.
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)    // Fault per 1000 parts.

#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
    #error To sfalma ston ry8mo Baud einai megalytero toy 1%, mataiosh!
#endif


//adjust UARTsendChar() function for stream
static int UARTsendstream(char c, FILE *stream);
//----set output stream to UART----
static FILE uart_str = FDEV_SETUP_STREAM(UARTsendstream, NULL, _FDEV_SETUP_WRITE);

static int UARTsendstream(char c , FILE *stream){
    uart_putc(c);
    return 0;
}


void startcounter(uint16_t);

uint16_t gate_time;               // Ο χρόνος σε msec που θα μένει η πύλη ανοικτή.
volatile uint16_t timer0_ticks;   // Ο αριθμός των ticks του Timer0.
uint8_t counter1_overflows;       // Ο αριθμός των overflow του Timer/Counter1.
volatile uint8_t ready_flag;      // Σημαία ολοκλήρωσης της μέτρησης.
volatile uint32_t counter_val;    // Η συνολική τιμή της μέτρησης.

int main(void){

    DDRB |= (1<<PB0);

    stdout = &uart_str;                         // Stream initialization.
    uart_init(UART_BAUD_SELECT(BAUD,F_CPU));    // UART initialization.

    sei();

    uint16_t gate=1000; // Σε milliseconds.

    uint32_t frequency;

    uint8_t i; // Ένα μικρό κόλπο για να μην "μπουκώνει"
               // το UART όταν έχουμε μικρό χρόνο gate.

    while(1){

        i++;

        startcounter(gate);

        while(!ready_flag){};

        PORTB ^= (1<<PB0); // Strobe LED.

        frequency = counter_val*(1000/gate);

        if (i>=1000/gate) {
            printf("Frequency: %lu Hz.\n", frequency);
            i=0;
        };
    };

    return 0;
}

void startcounter(uint16_t msecs){

    gate_time=msecs;
    counter1_overflows=0;
    timer0_ticks=0;
    ready_flag=0;

    TIMSK |= (1<<TOIE1);  // Ενεργοποίηση Interrupt υπερχείλισης για τον Timer/Counter1.
    TIMSK |= (1<<TOIE0);  // Ενεργοποίηση Interrupt υπερχείλισης για τον Timer0.

    //Αρχικοποίηση του Timer0. Προκαλεί Interrupt κάθε έναν msec.

    TCNT0 = TIMER0_PRELOAD_VAL;   // Προφόρτωση με την τιμή για χρόνο 1 msec.
    TCCR0 |= (1<<CS01)|(1<<CS00);  // Ενεργοποίηση με Prescaler 64.

    // Μηδενισμός του TCNT1;
    TCNT1=0;
    // Αύξηση του TCNT1 με εφαρμογή παλμού στο PD5.
    TCCR1B |= (1<<CS10)|(1<<CS11)|(1<<CS12);

}


ISR(TIMER1_OVF_vect){

    counter1_overflows++;
}


ISR(TIMER0_OVF_vect){

    uint16_t counter1_ticks;

    // Τιμή του μετρητή του Counter1.
    counter1_ticks = TCNT1;

    TCNT0 += TIMER0_PRELOAD_VAL;  // Προφόρτωση του TCNT0 για χρόνο 1 msec.

    timer0_ticks++;

    // Έλεγχος αν έχει συμπληρωθεί ο χρόνος gate_time. Αν όχι έξοδος από την ISR;
    if (timer0_ticks < gate_time) return;

    // Αν χάσαμε κάποιο Timer/Counter1 interrupt αύξηση του μετρητή.
    if (bit_is_set(TIFR,TOV1)) counter1_overflows++;

    TIMSK &= ~((1<<TOIE1)|(1<<TOIE0)); // Απενεργοποίηση Interrupt υπερχείλισης για Timer0, Timer/counter1.

    TCCR1B &= ~((1<<CS10)|(1<<CS11)|(1<<CS12)); //Σταμάτημα του Timer/Counter1.

    TCCR0 &= ~( (1<<CS01)|(1<<CS00)); //Σταμάτημα του Timer0.

    counter_val = (counter1_overflows*65536)+counter1_ticks;

    ready_flag = 1;
}
Advertisements
This entry was posted in AVR, Electronics and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s