利用pic 16 F15243实现10 Hz-20 kHz方波频率的精密测量与显示

wvyml7n5  于 2022-12-11  发布在  其他
关注(0)|答案(2)|浏览(221)

我尝试使用pic 16 F15243测量平方信号的频率(10 Hz-20 kHz),并以Hz为单位显示值。
问题是,我不能得到准确的测量,频率越高(20千赫,我得到25千赫)。
代码如下:

// 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;
}

我已经尝试了不同的波特率与不同的时钟速度,这是最接近我得到的。
任何关于我如何能以可接受的准确度获得读数的提示都将不胜感激。

i2byvkas

i2byvkas1#

您捕获的是输入脉冲之间的时钟计数,它必须是一个整数值。在高频率下,分辨率将非常低。在这种情况下,5和6个计数之间的差值约为5 KHz。
如果您希望在最高频率下测量1%的精度,则需要 fmax x 100的时钟频率,而1%的精度仍然很差--您需要非常快的时钟才能获得良好的精度,并且由于它只是一个16位定时器,因此会限制频率下限。
对于高频,一个更好的方法是在一个固定的时间段内计数脉冲数--这样分辨率就由测量周期而不是计时器时钟速率决定。当然,缺点是在“低”频率下分辨率较差。
一种适用于高频和低频的解决方案是对脉冲进行计数,直到定时器计数大于32767(半满量程--有些随意,但显然避免了溢出,并提供了相当高的精度)。
频率= 125000 x pulse_count/TMR0_COUNT
因此,以20 KHz为例,在5243个脉冲之后,TMR 0将达到32768; 125000 x 32768 / 5243 = 20000赫兹。
在10 Hz的较低频率下,计数器将在4个脉冲后达到50000(因此在TMR 0的16位范围内),并且125000 * 4 / 50000 = 10 Hz。
因此,这里不是在输入中断时将计数器复位为零,而是递增脉冲计数,然后如果计数器值大于32767(或您选择的任何足够精度的值),则捕获其值和脉冲计数,然后将两者复位为零。
类似于(说明性的-我没有测试,甚至没有编译):

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;
}

则在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);
    }

您的代码中存在一些问题,例如value的非原子访问以及它未声明为volatile。上述代码解决了这些问题。

4xrmg8kj

4xrmg8kj2#

我认为你的问题是整数数学:

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

这实际上是在

value = 125000/value;

如果值为5,则得到25000;如果值为6,则得到20833。对于较小的value值,此计算中的误差非常大。为了减小此误差,需要在较长的时间段内累积脉冲。

相关问题