Home > OS >  I am trying to measure the frequency of a squared signal (10Hz-20kHz), using pic16F15243 and display
I am trying to measure the frequency of a squared signal (10Hz-20kHz), using pic16F15243 and display

Time:12-06

The problem is that I cant get accurate measurments the higher the frequency goes (20kHz I get 25kHz). The code is below:

// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator Mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINTOSC_1MHZ// Power-up Default Value for COSC bits (HFINTOSC (1 MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O function on RA4)
#pragma config VDDAR = HI       // VDD Range Analog Calibration Selection bit (Internal analog systems are calibrated for operation between VDD = 2.3V - 5.5V)

// CONFIG2
#pragma config MCLRE = EXTMCLR  // Master Clear Enable bit (If LVP = 0, MCLR pin is MCLR; If LVP = 1, RA3 pin function is MCLR)
#pragma config PWRTS = PWRT_OFF // Power-up Timer Selection bits (PWRT is disabled)
#pragma config WDTE = OFF       // WDT Operating Mode bits (WDT disabled; SEN is ignored)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection bit (Brown-out Reset Voltage (VBOR) set to 1.9V)
#pragma config PPS1WAY = OFF    // PPSLOCKED One-Way Set Enable bit (The PPSLOCKED bit can be set and cleared as needed (unlocking sequence is required))
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)

// CONFIG3

// CONFIG4
#pragma config BBSIZE = BB512   // Boot Block Size Selection bits (512 words boot block size)
#pragma config BBEN = OFF       // Boot Block Enable bit (Boot Block is disabled)
#pragma config SAFEN = OFF      // SAF Enable bit (SAF is disabled)
#pragma config WRTAPP = OFF     // Application Block Write Protection bit (Application Block is not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block is not write-protected)
#pragma config WRTC = OFF       // Configuration Registers Write Protection bit (Configuration Registers are not write-protected)
#pragma config WRTSAF = OFF     // Storage Area Flash (SAF) Write Protection bit (SAF is not write-protected)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (High Voltage on MCLR/Vpp must be used for programming)

// CONFIG5
#pragma config CP = OFF         // User Program Flash Memory Code Protection bit (User Program Flash Memory code protection is disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#include <stdio.h>
#define _XTAL_FREQ 32000000
unsigned long value;

void osc_init()
{
    OSCENbits.HFOEN = 1;    // USE INTERNAL CLOCK
    OSCFRQbits.FRQ = 0b101; // 32 MHZ CLOCK
}

void pin_master()
{
    TRISAbits.TRISA2 = 1;   // RA5 IS INPUT
    ANSELAbits.ANSA2 = 0;   // RA5 IS digital INPUT
    TRISBbits.TRISB7 = 0;   // RB7 IS OUTPUT (TX UART)
    TRISBbits.TRISB6 = 0;
    RB7PPS = 0x05;          // RB7 IS MAPPED AS EUSART1 TX1
}

void uart_init()
{
    // CONFIGURE BAUD RATE, ENABLE EUART UNIT, ENABLE TRANSMISSION
    // CONFIGURE THE DIVIDER AS 4:
    SYNC=0;
    BRG16=1;
    BRGH=1;

    // FINETUNE THE MULTIPLIER
    SP1BRG = 9;

    // CONFIGURE THE EUART CONTROLS
    RC1STAbits.CREN = 0;    // DISABLE RX
    TX1STAbits.TXEN = 1;    // ENABLE TX
    RC1STAbits.SPEN = 1;    // ENABLE EUART UNIT
}

void putch(char data)
{
    while( ! TX1IF) 
       continue;
    TX1REG = data;
}
__interrupt() INTERRUPT_Interrupt_Manager (void){
    value=(TMR0H<<8)|(TMR0L);
    TMR0H=0;
    TMR0L=0;
    INTF=0;
}

teim_init() {
    TMR0IF=0;
    TMR0IE=1;
    T0CON1 = 0b01000110; //0b01000011
    T0CON0 = 0b10010001; //
}

inter_init(){
    INTE=1;
    INTF=0;
    GIE=1;
    INTEDG=1;
}

void main(void) {
    osc_init();
    pin_master();
    uart_init();
    teim_init();
    inter_init();
    TMR0H=0;
    TMR0L=0;
    while(1)
    {
        value=8*value;
        value=1000000/value;
        printf("Frequency = %lu\n",value);
        __delay_ms(1000);
    }

    return;
}

I I tried diffrent baud rates with different clock speeds and this was the closest I got. I had too many lines of code in the interrupt function and tried to move as many as I can to the main(). Any hints on how I can get readings with acceptable accuracy would be appreciated.

CodePudding user response:

I think your problem is the integer math:

value=8*value;
value=1000000/value;

This is effectively doing

value = 125000/value;

If value is 5 you get 25000; if value is 6 you get 20833. The error in this computation is very large for small values of value. To reduce this error you need to accumulate pulses for a longer period of time.

CodePudding user response:

You are capturing the number of clock counts between a input pulses, and that is necessarily an integer value. And at high frequencies the resolution will be very low. The difference between 5 and 6 counts being around 5KHz in this case.

If you wanted say 1% precision at the highest frequency you wanted to measure you would need a clock frequency of fmax x 100.

A better method for high frequencies is to count the number of pulses over a fixed period of time - then the resolution is determined by the measurement period rather than the timer clock rate. The disadvantage then of course is poor resolution at low frequencies.

A solution that would work for high and low frequencies is to count pulses until the timer count is greater than say 32767 (half full scale - somewhat arbitrarily, but clearly avoiding overflow). Then:

frequency = 125000 x pulse_count / TMR0_COUNT

So for example for 20KHz, TMR0 whould reach 32768 after 5243 pulses; 125000 x 32768 / 5243 = 20000 Hz.

There are other issues with this code such as the non-atomic access of value and the fact that it is not declared volatile.

  • Related