Frecventmetru Digital
Frecventmetru Digital
Cuprins
Scopul proiectului
Principiul de functionare
Schema bloc
Schema electrica
Lista de componente (Bom)
Cablaj
Simulare
Schema logica
Cod sursa
Bibliografie
Scopul proiectului
Proiectul isi propune realizarea unui aparat pentru masurarea frecventelor digitale in domeniul 0-50 Mhz.
Calculul aferent masurari frecventelor este efectuat cu ajutorul microcontrolerului PIC16F870. Frecventa masurata este afisata pe un ecran LCD 16×2.
Echipamentul poate fi util la masurarea frecventei unor dispozitive variate cum ar fi oscilatoare, emitatoare, timmer, generator de impulsuri, etc. Dispozitiv-ul este stabil si ofera o masuratoare foarte precisa datorita amplificatorului de intrare, așa că poate măsura chiar semnale slabe de la oscilatoare de cristal.
Principiul de functionare
Un frecventmetru simplu masoara frecventa numarand impulsurile unui semnal de intrare intr-un anumit timp. Frecventa este definita ca numarul de evenimente(in cazul nostru impulsuri) / timp si se masoara in Hz.
In cazul nostru este folosit timerul TMR1 al microcontrolerului pentru a măsura numarul de impulsuri iar TMR0 se folosește pentru a asigura baza de timp de o secundă. Timerul TMR1 al Pic-ului este un registru de 16 biți, la fiecare impuls prezent la intrarea RC0, valoarea acestui registru crește pana ajunge la valoarea de 65535. In acest moment, programul reseteaza valoarea TMR1 si incrementează valoarea unei alte variabile (să o numim INC). La sfârșitul perioadei de contorizare, de o secunda, se masoara valoarea acestei variabile INC si se adauga valoarea prezenta in acel moment in TMR1.
Pentru o ințelegere mai buna sa presupunem ca avem la intrare un semnal de 1Mhz. Aceasta inseamnă că intr-o secundă la intrarea RC0 vor fi 1 milion de impulsuri. In acest caz 1.000.000/65536=15,25878906 adică INC va avea valoarea de 15 iar TMR1 va avea valoarea de 0,25878906* 65536=16959,99(~16960).
Deoarece timpul de masurare este de T= 1 secundă, rezultatul final este de fapt frecvența semnalului de intrare. Utilizarea timpului de masurare 1 secundă oferă, de asemenea, o rezoluție de 1 Hz.
Pentru acest proiect oscilatorul microcontrollerului este folosit ca baza de timp. Setarea timpului de masurare T la 1 secunda se face executand functii de intarziere care folosesc un anumit numar de cicluri masina.
Folosind un oscilator de 4MHz vom obtine un ciclu masina de 1Mhz(o perioada de 1 us) ceea ce face calculul si setarea timpilor de intarziere mai simplu deoarece majoritatea instructiunilor pentru un PIC se executa intr-un ciclu masina. Deci executand 1.000.000 de astfel de cicluri obtinem o intarziere de o secunda.
Schema bloc
Schema electrica
Proiectul se alimenteaza extern cu o tensiune alternativa de 12 V. Aceasta este redresata si stabilizata intern cu ajutorul unei punti redresoare. Pentru obtinerea tensiunii de 5V pentru alimentarea integratelor se foloseste regulatorul liniar LM7805. Alimentarea circuitului este confirmata de aprinderea unui LED.
Ca si microcontroler s-a ales circuitul PIC16F870. Acesta dispune de trei timere: TMR0 si TMR2 pe 8 biti si TMR1 pe 16 biti. Pentru realizarea proiectului s-au folosit doar TMR0 si TMR1.
Microcontrolerul se programeaza prin interfata ICSP (In Circuit Serial Programming). Pentru comunicarea datelor rezultate, proiectul dispune si de comunicare seriala cu PC-ul prin intermediul interfetei RS232, folosind ca transceiver circuitul MAX232.
Pentru generarea semnalului de tact necesar microcontrolerului s-a folosit un oscillator de 4MHz.
Tranzistorul de la intrare are rolul de amplificare si formare a semnalului pentru etajul de intrare din PIC.
Pentru afisarea datelor este utilizat un ecran LCD 16×2, din care se utilizeaza o singura linie.
Lista de componente (Bom)
Cablaj
Top
Bottom
Assembly
Simulare
Schema logica
Cod sursa
Cod Principal
Acest fișier conține inițializarea portului, și rutina principal care se executa ciclic.
// Program Principal
//////////////////////////////////////////////////////////////////////
//
// Masurarea frecventei cu TMR1 pana la 50MHz.
//
// Rezultatul se va afisa pe un display LCD (folosit in modul 4 biti).
//
// Acuratetea proiectului este data de acuratetea oscilatorului
//
//////////////////////////////////////////////////////////////////////
#include "bit.h"
#include "delay.h"
#include "bcd.h"
unsigned long ldmy=0; // declarare variabila fara semn(32-biti) cu valoare implicita 0
unsigned short sdmy=0; // declarare variabila fara semn(32-biti) cu valoare implicita 0
////////////////////////////////////////////////////////////////////////
void init_ports(void) {
ADCON1 = 0x06; // configuarea registrului CON al timerului tmr1(16 biti)
// bitul T1SYNC pus pe 1–> sincronizare interna
// bitul TMR1CS pus pe 1–> numara fronturile crescatoare de la T1CKI
PORTA = 0;
TRISA = 0xFC; // setare ca intrare exceptand RA1,RA0
PORTB = 0;
TRISB = 0; // iesire
}
////////////////////////////////////////////////////////////////////////
//
// gate_loop
//
// Ce face:
// Citeste 'overflow flag' in constanta de timp de 1 secunda
// si returneaza numarul masurat de la TIMER1
//
// numarare si resetare:
//
// Se regleaza numararea pentru aproximativ 1 secunda
unsigned long gate_loop(unsigned int gt) org 200 {
unsigned long oc = 0;
// org–> specifica adresa de start a rutinei in ROM
do {
gt–; // decrementare valoare operand dupa evaluare
// test pentru overflow
// registrul PIR1 contine bitii cu intreruperi
// TMR1IF primul bit din registrul PIR1
sdmy = PIR1 & (1<<TMR1IF); // parcurge TMR1IF de la stanga la dreapta si face 'si' cu PIR1
asm {
CLRW ; // Registrul W este sters si steagul Z din registrul de stare este setat
}
if (sdmy!=0) {
PIR1 &= ~(1<<TMR1IF); // sterge bitul.
oc += 1; // aduna 1
} else {
sdmy &= ~(1<<TMR1IF);
ldmy += 1;
asm {
CLRW ;
}
}
// functii definite in programul delay.c
delay_500_us();
delay_100_us();
delay_100_us();
delay_100_us();
delay_100_us();
delay_10_us();
delay_10_us();
delay_10_us();
delay_10_us();
delay_10_us();
asm {
CLRW
CLRW
CLRW
}
} while (gt>0);
return oc;
}
////////////////////////////////////////////////////////////////////////
// Start here
//
void main() org 400 {
char op[12] ; // pentru afisarea sirului ascii
unsigned short st_TMR1L ; // memorare Timer 1 LSB
unsigned short st_TMR1H ; // memorare Timer 1 MSB
unsigned long st_TMR1_ovfl=0 ; // memorare Timer 1 'overflows' – multiplu de65536.
unsigned long df = 0 ; // afisare frecventei.
unsigned short blinkc=0 ; // numarator
char lcdop[4];
init_ports();
// Timer 1
// prescale = 0, oscillator off, timer on, external clock, unsynchronized
T1CON = (1<<TMR1ON) | (1<<TMR1CS) | (1<<T1SYNC);
PIE1 = (1<<TMR1IE) ;
Lcd_Config(&PORTB,2,3,1,7,6,5,4); // PORT,RS,EN,WR,D7,D6,D5,D4
LCD_Cmd(LCD_CLEAR);
LCD_Cmd(LCD_CURSOR_OFF);
for(;;) {
////////////////////////////
// calcukarea frecventei de la timer
if (!(st_TMR1L==0 && st_TMR1H==0 && st_TMR1_ovfl==0) ) {
df = st_TMR1L+(st_TMR1H<<8)+(st_TMR1_ovfl<<16);
}
else {
df = 0;
}
ulong2bcd(df);
process_ulong2str(op);
LCD_Out(1,1,"PER:");
LCD_Out(1,6,op);
// initializarea pentru urmatoarea poarta de numarare
st_TMR1L = 0;
st_TMR1H = 0;
st_TMR1_ovfl = 0;
TMR1H = 0; // stergetimer1 high count
PIR1 &= ~(1<<TMR1IF); // sterge 'overflow' bit.
TMR1L = 0; // sterge timer1 low count
// inceputul porti de timp
st_TMR1_ovfl = gate_loop(1000);
// delay pentru 1 secunda.
delay_500_us();
delay_100_us();
delay_100_us();
delay_10_us();
asm {
CLRW
CLRW
CLRW
CLRW
}
st_TMR1L = TMR1L; // obtine valoarea numaratorului inainte de update
st_TMR1H = TMR1H;
// sfarsitul porti de timp
////////////////////
// test pentru overflow dupa delay
if ( PIR1 & (1<<TMR1IF) ) {
PIR1 &= ~(1<<TMR1IF); // sterge bitul
st_TMR1_ovfl += 1; // aduna 1
}
}
}
Afisarea pe LCD
Aceasta rutina a fost creata în cod mașină pentru a economisi spațiul de pe chip-urile mai mici, și, de asemenea, e mai rapida decât constructia în rutine pentru multiplicare si divizare.
Acesta utilizează metoda Add 3 pentru a converti variabilele fara semn de tip long într-o valoare ASCII care pot fi afișate pe LCD.1.hex
// Program secundar –> Functie BCD (display)
// scrierea in ascii
static char DIG absolute 0x30;
static char DIG1 absolute 0x31;
static char DIG2 absolute 0x32;
static char DIG3 absolute 0x33;
static char DIG4 absolute 0x34;
static char DIG5 absolute 0x35;
static char DIG6 absolute 0x36;
static char DIG7 absolute 0x37;
static char DIG8 absolute 0x38;
static char DIG9 absolute 0x39;
// numarare in bucla
static unsigned short BITCNT absolute 0x3A;
static unsigned short DIGCNT absolute 0x3B;
// mamorarea numerelor binare
static unsigned short BIN3 absolute 0x2C;
static unsigned short BIN2 absolute 0x2E;
static unsigned short BIN1 absolute 0x2D;
static unsigned short BIN0 absolute 0x2F;
void ulong2bcd_p1(unsigned long num) {
unsigned short *p;
p = (unsigned short) #
bin0 = *(p++); bin1 = *(p++);
bin2 = *(p++); bin3 = *(p++);
dig = 0; dig1 = 0; dig2 = 0;
dig3 = 0; dig4 = 0; dig5 = 0;
dig6 = 0; dig7 = 0; dig8 = 0; dig9 = 0;
DIGCNT = 0;
BITCNT = 16;
// codul asamblare
asm {
BITLOOP:
movlw 10 // 10 digiti
movwf bcd_DIGCNT
movlw bcd_DIG // locatia de iesire a digitilor
movwf FSR
// obtinerea bitului urmator
rlf bcd_BIN0,F
rlf bcd_BIN1,F
rlf bcd_BIN2,F
rlf bcd_BIN3,F // ; MSBit shiftat in 'carry flag'
PROCESSDIGITSLOOP:
rlf INDF,F // intoarcerea 'carry' la digitul curent
//start conversie
movlw -10
addwf INDF,W
btfsc STATUS,C
addlw 6
addlw 10
movwf INDF
BCF STATUS,C
btfsc INDF,4
BSF STATUS,C
andlw 0x0f
movwf INDF
INCF FSR,F // urmatorul bit
decfsz bcd_DIGCNT,F
goto PROCESSDIGITSLOOP
decfsz bcd_BITCNT,F
goto BITLOOP
} // sfarsit asamblare
} // sfarsit conversie
void ulong2bcd_process(void) {
BITCNT = 16;
asm {
call BITLOOP
}
}
void ulong2bcd(unsigned long num) {
unsigned short i;
ulong2bcd_p1(num);
ulong2bcd_process(void);
}
void packedBCDToStr(unsigned short packedBCD,unsigned short len, char *op) {
unsigned short *p=packedBCD, val, i;
for (i=0;i<len;i++) {
val = (*p >> 4) + '0';
*(op++) = val;
val = (*p & 0x0f) + '0';
*(op++) = val;
p++;
}
}
void unpackedBCDtoStr(unsigned short *bcd, unsigned short c, char *op) {
unsigned short i;
for(i=0;i<c;i++) {
*(op++) = (*(bcd++)) + '0';
}
*op=0;
}
void strrev(char *s) {
unsigned short i,len;
char *e, *p, st;
len = strlen(s);
e = s+len-1;
for ( i=0; i<(len>>1); i++) { // >>1 = len/2
st = *e;
*e=*s;
*s = st;
e–;
s++;
}
}
void process_ulong2str(char *str) {
char *s=str;
unsigned short stop=0;
unpackedBCDtoStr(&DIG, 10, str);
// pentru a face un digit spatiu
strrev(str);
s = str;
while (*s) {
if (*s!='0') { stop=1; }
if (!stop) { // inlocuieste zerourile cu spatiu
if (*s=='0') {*s=' '; }
}
s++;
}
}
void ulong2str(long ip, char *str) {
char *s=str;
ulong2bcd(ip);
process_ulong2str(str);
}
Rutine de intarziere
Rutinele de întârziere au fost create folosind cod mașină, astfel încât acestea să aibă un timp de execuție fix (nu se schimba cand compilatorul re-optimizează codul). Ele sunt, de asemenea, fixate în locația de memorie.
// Program secundar
unsigned short CounterA absolute 0x53;
void delay_10_us(void) org 10 {
asm {
CLRW
CLRW
CLRW
CLRW
CLRW
CLRW
}
}
void delay_100_us(void) org 20 {
CounterA = 29;
asm {
//intarziere pic = 0.0000920 pentru Osc = 4 MHz
// movlw D'29'
movwf CounterA
loop100us:
decfsz CounterA,1
goto loop100us
}
// intarziere
asm {
CLRW
CLRW
CLRW
CLRW
CLRW
}
}
void delay_500_us(void) org 40 {
CounterA = 164;
asm {
// intarziere pic = 0.0004970 pentru Osc = 4 MHz
// movlw D'164'
movwf CounterA
loop500us:
decfsz CounterA,1
goto loop500us
}
}
Bibliografie
http://www.best-microcontroller-projects.com/pic-frequency-counter.html
Datasheet PIC16F870
Datasheet LCD – WC1602K
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Frecventmetru Digital (ID: 162511)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
