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) &num;

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

Similar Posts