简体   繁体   English

使用pic16F15243精确测量和显示10Hz-20kHz方波的频率

[英]Measuring and displaying frequency of 10Hz-20kHz square wave using pic16F15243 with precision

I am trying to measure the frequency of a squared signal (10Hz-20kHz), using pic16F15243 and displaying the value in Hz.我正在尝试使用 pic16F15243 测量平方信号 (10Hz-20kHz) 的频率,并以 Hz 为单位显示值。

The problem is that I cant get accurate measurements the higher the frequency goes (20kHz I get 25kHz).问题是频率越高,我就无法获得准确的测量值(20kHz 我得到 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 have tried different baud rates with different clock speeds and this was the closest I got.我尝试过不同的波特率和不同的时钟速度,这是我得到的最接近的。

Any hints on how I can get readings with acceptable accuracy would be appreciated.任何关于如何以可接受的精度获得读数的提示都将不胜感激。

You are capturing the number of clock counts between input pulses, and that is necessarily an integer value.您正在捕获输入脉冲之间的时钟计数数,这必然是一个 integer 值。 At high frequencies the resolution will be very low.在高频下,分辨率会非常低。 The difference between 5 and 6 counts being around 5KHz in this case.在这种情况下,5 和 6 计数之间的差异约为 5KHz。

If you wanted say 1% precision at the highest frequency you wanted to measure you would need a clock frequency of f max x 100, and 1% precision is still pretty poor - you would need very fast clocks to get good precision and given that it is only a 16 bit timer it would limit your lower frequency limit.如果您想在要测量的最高频率下说 1% 的精度,则需要f max x 100 的时钟频率,而 1% 的精度仍然很差 - 您需要非常快的时钟才能获得良好的精度,并且考虑到它只是一个 16 位定时器,它会限制您的频率下限。

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, and providing a fair degree of precision).适用于高频和低频的解决方案是对脉冲计数,直到定时器计数大于 32767(半满量程 - 有点武断,但显然避免溢出,并提供相当程度的精度)。 Then:然后:

frequency = 125000 x pulse_count / TMR0_COUNT频率 = 125000 x pulse_count / TMR0_COUNT

So for example for 20KHz, TMR0 whould reach 32768 after 5243 pulses;因此,例如对于 20KHz,TMR0 在 5243 个脉冲后将达到 32768; 125000 x 32768 / 5243 = 20000 Hz. 125000 x 32768 / 5243 = 20000 赫兹。

At the lower frequency of 10Hz, the counter will reach 50000 after 4 pulses (so well within the 16-bit range of TMR0), and 125000 * 4 / 50000 = 10Hz.在 10Hz 的较低频率下,计数器将在 4 个脉冲后达到 50000(完全在 TMR0 的 16 位范围内),并且 125000 * 4 / 50000 = 10Hz。

So here instead of resetting the counter to zero on the input interrupt, you would increment the pulse count then if the counter value is greater than 32767 (or whatever you choose as sufficient precision), you would capture both its value and the pulse count and then reset both to zero.因此,在这里不是在输入中断时将计数器重置为零,而是增加脉冲计数,然后如果计数器值大于 32767(或您选择的足够精度的任何值),您将捕获其值和脉冲计数,并且然后将两者都重置为零。

Something like (illustrative - I have not tested or even compiled this):类似的东西(说明性的 - 我没有测试甚至编译过这个):

static volatile uint32_t timer_counts = 0 ;
static volatile uint32_t pulse_counts = 0 ;

__interrupt() INTERRUPT_Interrupt_Manager (void)
{
    static pulse_count = 0 ;

    pulse_count++ ;
    int tmr_count = (TMR0H<<8)|(TMR0L) ;

    if( tmr_count > 0x7FFFu )
    {
        timer_counts = tmr_count ;
        pulse_counts = pulse_count ;

        pulse_count = 0 ;
        TMR0H=0;
        TMR0L=0;
    }
    INTF=0;
}

Then in main() :然后在main()中:

    for(;;)
    {
        // Capture timer and pulse counts atomically
        INTE = 0 ;
        uint32_t tcount = timer_counts ;
        uint32_t pcount = pulse_counts ;
        INTE = 1 ;

        uint32_t f = 125000u * tcount / pcount ;
        printf( "Frequency = %lu\n", f ) ;

        __delay_ms(1000);
    }

There are issues with your code such as the non-atomic access of value and the fact that it was not declared volatile .您的代码存在问题,例如value的非原子访问以及未声明为volatile的事实。 The above code addresses those issues.上面的代码解决了这些问题。

I think your problem is the integer math:我认为你的问题是 integer 数学:

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

This is effectively doing这有效地做

value = 125000/value;

If value is 5 you get 25000;如果值为 5,则获得 25000; if value is 6 you get 20833. The error in this computation is very large for small values of value .如果 value 为 6,则得到 20833。对于较小的value值,此计算中的误差非常大。 To reduce this error you need to accumulate pulses for a longer period of time.要减少此错误,您需要在更长的时间内累积脉冲。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM