/****************************************************************************
Precission Frequency meter
PIC 18F2550
Input Pin = Pin11 / RC0 / T1CKI
****************************************************************************/
#include <p18cxxx.h>
#include <stdlib.h>
#include <stdio.h>
#include <delays.h>
#include "main.h"
/****************************************************************************
GLOBAL VARS AND DEFINITIONS
****************************************************************************/
#pragma udata
char str_buf[20];
static unsigned char sys_clk, cent_second;
static unsigned short timer1_carry; // Timer1 extended counter
#define TMR0_COUNT (FOSC/((unsigned long)4*250*100)) // Number of carrys per second
#define FOSC 20000000 // clock oscillator
#define BAUD 57600 // usart baud speed
#pragma code
/****************************************************************************
STRING ROUTINES
****************************************************************************/
/*
Return string lenght
*/
unsigned char str_len(char *str) {
unsigned char len;
len = 0;
while(*str!=0) {
len++;
str++;
}
return len;
}
/*
Shift string to right
*/
void str_shift_right(char *str) {
unsigned char c1, c2;
c1 = *str++;
while(c1!=0) {
c2 = *str;
*str = c1;
c1 = c2;
str++;
}
*str = c1;
}
/*
Add '.' character at position 'dot' of string 'str'
digit: 0 0 1 2 3 4 5 6
position: -1 0 1 2 3 4 5 6
*/
void str_dot(char *str, char dot) {
if (*str==0) return;
while(dot<1) {
str_shift_right(str);
*str = '0';
dot++;
}
while(dot>1) {
str++;
dot--;
if (*str==0) break;
}
if (*str!=0) {
str++;
str_shift_right(str);
*str = '.';
}
}
/*
Convert float to string number in engineering format
*/
void ftos(float fnum, long *str) {
char exp10;
exp10 = 0;
if (fnum<0.0) fnum = -fnum;
while(fnum<10000000.0) {
fnum *= 10.0;
exp10++;
if (exp10>38) break;
};
str_dot(str, 8-exp10);
}
/****************************************************************************
INTERRUPT SERVICE ROUTINE
****************************************************************************/
#pragma interrupt isr_main
void isr_main(void) {
if (INTCONbits.TMR0IF==1) { // if TIMER0 overflow interrupt
INTCONbits.TMR0IF = 0; // Clear Timer0 interrupt flag
TMR0L += 6+3;
sys_clk--;
if (sys_clk==0) {
sys_clk = TMR0_COUNT;
cent_second++;
}
}
}
#pragma code high_vector=0x08
void isr_high(void) {
_asm GOTO isr_main _endasm
}
/****************************************************************************
TIMER 0
****************************************************************************/
/*
Initialize Timer0
*/
void timer0_init(void) {
INTCON = 0b11100000; // Enable interrupts
T0CON = 0b01001000;
sys_clk = TMR0_COUNT;
TMR0L = 6;
cent_second = 0;
}
/****************************************************************************
TIMER 1
****************************************************************************/
/*
Initialize Timer1
*/
void timer1_init(void) {
T1CON = (unsigned char)
(1<<7) // RD16: 1 = 16-Bit Read/Write Mode Enable bit
+(0<<6) // T1RUN: 0 = Device clock is derived from another source
+(0b00<<4) // T1CKPS: Timer1 Input Clock Prescale Select bits
// 11 = 1:8 Prescale value
// 10 = 1:4 Prescale value
// 01 = 1:2 Prescale value
// 00 = 1:1 Prescale value
+(0<<3) // T1OSCEN: 0 = Timer1 oscillator is shut off
+(1<<2) // T1SYNC: 1 = Do not synchronize external clock input
+(1<<1) // TMR1CS: 1 = External clock from RC0 pin
+(0<<0); // TMR1ON: 0 = Stops Timer1
TMR1H = 0;
TMR1L = 0;
timer1_carry = 0;
PIR1bits.TMR1IF = 0;
PIE1bits.TMR1IE = 0;
}
/*
Count Timer1 carry
*/
void timer1_addcarry(void) {
if (PIR1bits.TMR1IF == 1) {
PIR1bits.TMR1IF = 0;
timer1_carry++;
}
else {
Nop(); Nop();
Nop(); Nop();
Nop(); Nop();
}
}
/*
Read Timer1 counter
*/
unsigned long timer1_read(void) {
union {
unsigned long dword;
unsigned int word[2];
unsigned char byte[4];
} timer;
do {
timer1_addcarry();
timer.byte[0] = TMR1L;
timer.byte[1] = TMR1H;
timer.word[1] = timer1_carry;
} while(PIR1bits.TMR1IF==1);
return timer.dword;
}
/****************************************************************************
TIMER 1 FREQUENCY METER
****************************************************************************/
/*
Run Timer1 count 1 second with interrupts
*/
unsigned long freqmeter_int(unsigned char sampletime) {
timer1_init();
timer0_init();
cent_second = 1-sampletime;
T0CONbits.TMR0ON = 1;
Delay10TCYx(3);
Nop(); Nop(); Nop(); Nop();
T1CONbits.TMR1ON = 1;
while(cent_second != 0) {
timer1_addcarry();
}
while(sys_clk > 1) {
timer1_addcarry();
}
while(cent_second==0);
T1CONbits.TMR1ON = 0;
T0CONbits.TMR0ON = 0;
return timer1_read();
}
#define FREQMETER_IN PORTCbits.RC0
/*
Returns measure of low frequency on RC0
Max accurate frequency: f<500kHz Ton>1us
*/
float freqmeter_real(unsigned char sample_time) {
float freq;
char str_buf[20];
// Reset timers
timer1_init();
timer0_init();
cent_second = 0-sample_time;
// Start counters
while(FREQMETER_IN==1);
while(FREQMETER_IN==0);
T1CONbits.TMR1ON = 1;
T0CONbits.TMR0ON = 1;
// Counting
while(cent_second!=0) {
timer1_addcarry();
};
Delay10TCYx(30);
// Stop counters
while(FREQMETER_IN==1);
while(FREQMETER_IN==0);
T1CONbits.TMR1ON = 0;
T0CONbits.TMR0ON = 0;
timer1_addcarry();
// Compute frequency
time = (unsigned long)TMR0L
- 6; time += ((unsigned long)sample_time
+ cent_second
)*(250*TMR0_COUNT
); time += (TMR0_COUNT
-sys_clk
)*250; freq
= (float) timer1_read
() / time * ((float)FOSC
/4);
return freq;
}
/****************************************************************************
USART AND RS232 FUNCTIONS
****************************************************************************/
/*
Initialize USART for RS232 comunications
*/
void rs232_init(void) {
BAUDCONbits.BRG16 = 0; // BRG16: 16-Bit Baud Rate Register Enable bit
SPBRGH = 0;
SPBRG = (FOSC/(16*BAUD))-1; // Real Baud = FOSC/(16*(SPBRG+1))
TXSTA = (char)
(0<<7) // CSRC: 1 = Syncronous Master mode
+ (0<<6) // TX9: 1 = Selects 9-bit transmission
+ (1<<5) // TXEN: 1 = Transmit enabled
+ (0<<4) // SYNC: 1 = Synchronous mode
+ (0<<3) // SENDB: 1 = Asynchronous mode: Send Sync Break on next transmission (cleared by hardware upon completion)
+ (1<<2) // BRGH: 1 = Asynchronous mode: High speed
+ (1<<1) // TRMT: 1 = TSR empty
+ (0<<0); // TX9D: Ninth bit of Transmit Data
RCSTA = (char)
(1<<7) // SPEN: 1 = Serial port enabled
+ (0<<6) // RX9: 1 = Selects 9-bit reception
+ (0<<5) // SREN: 1 = Enables single receive in Master Synchronous mode
+ (1<<4) // CREN: 1 = Enables Continuous Receive
+ (0<<3) // ADDEN: 1 = Enables address detection (RX9 = 1)
+ (0<<2) // FERR: 1 = Framing error
+ (0<<1) // OERR: 1 = Overrun error (can be cleared by clearing bit CREN)
+ (0<<0); // RX9D: Ninth bit of Received Data
TRISCbits.TRISC6 = 0; // Enable TX output
PIE1bits.TXIE = 0; // Disable RS232 interrupts
PIR1bits.TXIF = 0;
//TXREG = 0;
}
/*
Puts rom buffer to usart
*/
void rs232_puts(char const rom *str) {
while(*str) {
if (*str == '\n')
rs232_putc('\r');
rs232_putc(*str);
str++;
}
}
/*
Puts ram buffer to usart
*/
void rs232_puts_ram(char *str) {
while(*str) {
if (*str == '\n')
rs232_putc('\r');
rs232_putc(*str);
str++;
}
}
/*
Send char to USART with pooling
*/
void rs232_putc(char c) {
while (PIR1bits.TXIF == 0);
TXREG = c;
}
/****************************************************************************
MAIN PROGRAM
****************************************************************************/
#pragma code
//#define PPM_ERROR (11)
#define PPB_ERROR (10300+0*12.5)
/*
MAIN ROUTINE
*/
void main(void) {
unsigned long lfreq;
float freq;
// Initialize subsystems
rs232_init();
rs232_puts("\nSystem OK.\n");
// Freqmeter
while(1) {
rs232_puts("F = ");
lfreq = freqmeter_int(1);
if (lfreq>10000) {
lfreq = freqmeter_int(100);
lfreq += ((lfreq/10000) * PPB_ERROR)/100000.0;
}
else {
freq = freqmeter_real(100)*(1.0+PPB_ERROR/1000000000.0);
ftos(freq, str_buf);
}
rs232_puts_ram(str_buf);
rs232_puts("Hz\n");
}
}