我尝试使用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;
}
我已经尝试了不同的波特率与不同的时钟速度,这是最接近我得到的。
任何关于我如何能以可接受的准确度获得读数的提示都将不胜感激。
2条答案
按热度按时间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(或您选择的任何足够精度的值),则捕获其值和脉冲计数,然后将两者复位为零。
类似于(说明性的-我没有测试,甚至没有编译):
则在
main()
中:您的代码中存在一些问题,例如
value
的非原子访问以及它未声明为volatile
。上述代码解决了这些问题。4xrmg8kj2#
我认为你的问题是整数数学:
这实际上是在
如果值为5,则得到25000;如果值为6,则得到20833。对于较小的
value
值,此计算中的误差非常大。为了减小此误差,需要在较长的时间段内累积脉冲。