You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PIC16F15243的10Hz-20kHz方波频率测量精度问题求助

问题描述

使用PIC16F15243测量10Hz-20kHz的方波信号频率,以Hz为单位显示测量值,但高频段测量精度不足:输入20kHz时,测量结果显示为25kHz。已尝试调整不同波特率与时钟频率,当前方案为最接近准确值的版本,代码如下:

// 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;
}
精度优化建议
  • 修正核心计算逻辑错误
    当前定时器T0的时钟源为FOSC/4(32MHz/4=8MHz),分频系数1:64,因此定时器每个计数周期为1/(8MHz/64)=8μs。主循环中value=8*value; value=1000000/value;的计算完全颠倒,正确的频率计算公式应为:

    // 替换主循环内的计算代码
    unsigned long period_us = value * 8; // 计算单周期的微秒数
    unsigned long freq = 1000000 / period_us; // 频率=1/周期
    

    这是导致20kHz输入显示25kHz的直接原因。

  • 改用多周期/固定窗口测量法提升高频精度
    单周期测量在20kHz时(周期50μs),定时器仅计数6次,微小的计数误差会被放大为巨大的频率误差。建议切换为固定时间窗口内统计脉冲数的频率测量法:

    1. 用TMR0定时100ms(或50ms),开启全局中断;
    2. 在外部中断(输入信号上升沿)中统计脉冲次数;
    3. 定时结束后,频率=脉冲数/定时时间(如100ms窗口则频率=脉冲数*10)。
      这种方法在高频段能获得更多的计数样本,大幅降低相对误差。
  • 添加volatile修饰共享变量
    全局变量value被中断服务函数和主循环同时访问,必须添加volatile关键字防止编译器优化导致数据丢失:

    volatile unsigned long value;
    
  • 校准内部时钟或改用外部晶振
    PIC16F15243的内部HFINTOSC默认精度为±1%~±2%,高频下会直接影响测量结果。可以:

    • 读取芯片内部的时钟校准值(参考 datasheet 中 CALHFxx 寄存器);
    • 改用外部高精度晶体振荡器(如8MHz或32MHz),彻底解决时钟精度问题。
  • 修复代码笔误与中断安全问题

    • 函数名teim_init()拼写错误,应改为timer_init();
    • 中断中读取16位定时器寄存器时,可能出现主循环读取半字的情况,建议用临时变量缓存后再赋值:
      __interrupt() INTERRUPT_Interrupt_Manager (void){
          unsigned short temp_tmr = (TMR0H << 8) | TMR0L;
          value = temp_tmr;
          TMR0H = 0;
          TMR0L = 0;
          INTF = 0;
      }
      

内容的提问来源于stack exchange,提问作者olafur Eliasson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 22:31:12