搜索
bottom↓
回复: 12

Auto-ranging frequency meter

[复制链接]

出0入0汤圆

发表于 2010-3-27 20:56:04 | 显示全部楼层 |阅读模式
there are many frequency meter designs on the next and this one is probably the simplest one implemented on the smallest PIC.

it runs off a 12F675, a 4Mhz 8pdip device, and displays on a HD44780-compatible 16x2 LCD. The measurement is from 1Hz to about 4Mhz, in 1Hz increment. With no calibration, accuracy is about 1%. Minimum number of parts.

here is the code.

====================code======================
#include <htc.h>
#include <string.h>
#include "lcd_3wi.h"

#define CLK_IN                                (1<<5)                        //clk_in on ra5/t1cki. do NOT change
//frequency definitions
#define Freq_LF                                1000                        //Hz, if less than 1KHz, it is LF
#define Freq_HF                                100000                        //Hz, if more than 100Khz, it is HF

//high frequency
#define T1_PS_HF                        0b11                        //tmr1 prescaler for hf
#define Delay_MS_HF                        100                                //delay for 100ms

//medium frequency
#define T1_PS_MF                        0b10                        //tmr1 prescaler for mf
#define Delay_MS_MF                        500                                //delay for 250ms

//low frequency
#define T1_PS_LF                        0b00                        //tmr1 prescaler for lf
#define Delay_MS_LF                        1000                        //delay for 1000ms. 1000ms is the max

__CONFIG(INTIO & BORDIS & MCLRDIS & PWRTEN & WDTDIS);

const char str[]="f=            Hz";
unsigned long tmr_count;
unsigned char vRAM[17];

/**********************************************************
  delay routines
***********************************************************/

void delay(unsigned int dly) {                //delay dly cycles
        for (; dly>0; dly--)
                continue;
}

void delay_us(unsigned int us) {                //delay us microseconds
        for (; us>0; us--)
                NOP();
}

void delay_ms(unsigned int ms) {                //delay ms milliseconds
        for (; ms>0; ms--)
                {delay_us(89); NOP(); NOP(); NOP();}
}

void interrupt isr(void) {                                //interrupt service routine
        tmr_count++;                                                //increment the counter
        TMR1IF=0;                                                        //reset tmr1 flag
}

void tmr1_init(unsigned char T1_PS) {                                        //set up tmr0
        tmr_count=0;                                                //reset tmr_count
        TMR1IF=0;                                                        //reset tmr1 interrupt flag
        TMR1ON=0;                                                        //turn off tmr1
        TMR1H=0;                                                        //reset the timer1 high byte
        TMR1L=0;                                                        //reset the timer1 low byte
        T1CKPS1 = T1_PS >> 1;                                //set up tmr1 prescaler
        T1CKPS0 = T1_PS & 0b01;       
        T1OSCEN = 0;                                                //tmr1 low frequency osc off
        T1SYNC = 0;                                                        //no tmr1 synchronization
        TMR1CS = 1;                                                        //count pulses on t1cki
        TMR1IE=1;                                                        //enable tmr1 interrupt
        PEIE=1;                                                                //enable peripheral interrupt
        GIE=1;                                                                //enable interrupt
       
        /* old code on tmr0
        T0CS=1;                                                                //tmr0 source = t0cki/gpio2
        PSA=0;                                                                //prescaler assigned to tmr0
        PS2=(T0_PS & 0b100) >> 2;                        //set up tmr0 prescaler
        PS1=(T0_PS & 0b010) >> 1;
        PS0=(T0_PS & 0b001) >> 0;
        T0IE=1;                                                                //enable tmr0
        GIE=1;                                                                //enable the interrupt
        */
}

void mcu_init(void) {                                        //initialize the mcu
        ANSEL = 0x00;                                                //porta are digital io
        CMCON = 0x07;                                                //turn off comparators
}

void ultoa(char *s, unsigned long ul, unsigned char length) {        //convert unsigned long to a string
        unsigned char i;
       
        i=length;
        do {
                s[i--]=ul % 10 + '0';
                ul = ul / 10;
        } while (ul);
}


unsigned long freq_measure(unsigned int dly, unsigned char T1_PS) {        //measure the frequency
        unsigned int sTMR0;                                                //shadow tmr0
        tmr1_init(T1_PS);                                                //initialize the tmr0
        TMR1ON=1;                                                        //turn on tmr1
        delay_ms(dly);                                //delay 100ms. need to adjust the argument for precise timing
        TMR1ON=0;                                                        //turn off tmr1
        sTMR0=TMR1L + (TMR1H <<8 );                        //save the tmr0 counter
        return ((((unsigned long) tmr_count<<16) | sTMR0)<<(T1_PS))*1000/dly;                //update tmr_count
}

int main(void) {
       
        unsigned long freq;

        mcu_init();                                                                //initialize the mcu
        lcd_init();                                                                //initilize the lcd
       
        TRISIO |= CLK_IN;                                                 //set clk_in as input;

        lcd_clear();
        lcd_display(LCD_Line0, "12F675 Frequency Meter");
        while (1) {
                strcpy(vRAM, str);
                freq=freq_measure(Delay_MS_HF, T1_PS_HF);                                                //default is to measure HF
                if (freq<Freq_HF) freq=freq_measure(Delay_MS_MF, T1_PS_MF);                //then mf
                if (freq<Freq_LF) freq=freq_measure(Delay_MS_LF, T1_PS_LF);                //then lf
/*                tmr_count=tmr_count<<8;
                tmr_count=tmr_count+sTMR0;
                tmr_count=tmr_count<<(T0_PS+1);
                tmr_count=tmr_count*10;
*/
                ultoa(&vRAM[1], freq, 11);
                lcd_display(LCD_Line1, vRAM);
        }
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2010-3-27 20:58:56 | 显示全部楼层
the entire meter consists of three parts:

1) 12F675;
2) HC164: to help 12F675 control the LCD;
3) HD44780-compatible LCD.

here is showing that the meter is measuring a 4Mhz signal.


(原文件名:12F675 Frequency Meter.PNG)

出0入0汤圆

 楼主| 发表于 2010-3-27 21:02:57 | 显示全部楼层
the autoranging is transparent to the user and user-definable: it first measures the frequency in High Frequency mode and then determines if it is medium frequency;

if so, it measures it in Medium Frequency mode and then determines if it is low frequency.

if so, it measures in low frequency mode and display the results.

if you want to extend the frequency range, you have a few options:

1) ran the chip at 20Mhz;
2) use external frequency divider. One of the unused pins, GP1, can be used to switch input between the input pulse directly or through a hardware divider.

出0入0汤圆

 楼主| 发表于 2010-3-27 21:06:45 | 显示全部楼层
the code is highly portable. here is the implementation on a 16F684 + Nokia 7110 graphic LCD.

this time, it is measuring a low frequency signal (100Hz).


(原文件名:16F684 7110 Frequency Meter.PNG)

出0入0汤圆

 楼主| 发表于 2010-3-27 21:08:56 | 显示全部楼层
here is the code for the Nokia version. the code itself is almost identical to that using the hd44780 lcd.

=========code============
#include <htc.h>
#include <string.h>
#include "GLCDNokia7110.h"

__CONFIG(BORDIS & MCLRDIS & PWRTEN & WDTDIS & INTIO);

#define CLK_IN                                (1<<5)                        //clk_in on ra5/t1cki. do NOT change
//frequency definitions
#define Freq_LF                                1000                        //Hz, if less than 1KHz, it is LF
#define Freq_HF                                100000                        //Hz, if more than 100Khz, it is HF

//high frequency
#define T1_PS_HF                        0b11                        //tmr1 prescaler for hf
#define Delay_MS_HF                        100                                //delay for 100ms

//medium frequency
#define T1_PS_MF                        0b10                        //tmr1 prescaler for mf
#define Delay_MS_MF                        500                                //delay for 250ms

//low frequency
#define T1_PS_LF                        0b00                        //tmr1 prescaler for lf
#define Delay_MS_LF                        1000                        //delay for 1000ms. 1000ms is the max

const unsigned char str0[]="f=            Hz";
const unsigned char str1[]="cycle_ct=       ";
const unsigned char str2[]="tmr_cnt =       ";
const unsigned char str3[]="sTMR0   =       ";
unsigned char vRAM[17];
unsigned long tmr_count;
unsigned int cycle_count=0;
unsigned int sTMR0;                                                //shadow tmr0


/**********************************************************
  delay routines
***********************************************************/

void delay(unsigned int dly) {                //delay dly cycles
        for (; dly>0; dly--)
                continue;
}

void delay_us(unsigned int us) {                //delay us microseconds
        for (; us>0; us--)
                NOP();
}

void delay_ms(unsigned int ms) {                //delay ms milliseconds
        for (; ms>0; ms--)
                {delay_us(89); NOP(); NOP();}
}


/****************************************
*               initialize the mcu
****************************************/
void mcu_init(void) {
// need to initialize the mcu here.
        ANSEL=0x00;                                //turn portA digital
        CMCON0=0x07;                        //turn off comparators
//        ANSELH=0x00;                        //turn portB digital
//        TRISC = 0;
        IRCF2=1, IRCF1=1, IRCF0=0;        //4Mhz
}

void interrupt isr(void) {                                //interrupt service routine
        tmr_count++;                                                //increment the counter
        TMR1IF=0;                                                        //reset tmr1 flag
}

void tmr1_init(unsigned char T1_PS) {                                        //set up tmr0
        tmr_count=0;                                                //reset tmr_count
        TMR1IF=0;                                                        //reset tmr1 interrupt flag
        TMR1ON=0;                                                        //turn off tmr1
        TMR1H=0;                                                        //reset the timer1 high byte
        TMR1L=0;                                                        //reset the timer1 low byte
        T1GE=0;                                                        //tmr1 is not gated and always on
        T1CKPS1 = T1_PS >> 1;                                //set up tmr1 prescaler
        T1CKPS0 = T1_PS & 0b01;       
        T1OSCEN = 0;                                                //tmr1 low frequency osc off
        T1SYNC = 0;                                                        //no tmr1 synchronization
        TMR1CS = 1;                                                        //count pulses on t1cki
        TMR1IE=1;                                                        //enable tmr1 interrupt
        PEIE=1;                                                                //enable peripheral interrupt
        GIE=1;                                                                //enable interrupt
       
        /* old code on tmr0
        T0CS=1;                                                                //tmr0 source = t0cki/gpio2
        PSA=0;                                                                //prescaler assigned to tmr0
        PS2=(T0_PS & 0b100) >> 2;                        //set up tmr0 prescaler
        PS1=(T0_PS & 0b010) >> 1;
        PS0=(T0_PS & 0b001) >> 0;
        T0IE=1;                                                                //enable tmr0
        GIE=1;                                                                //enable the interrupt
        */
}

void ultoa(char *s, unsigned long ul, unsigned char length) {        //convert unsigned long to a string
        unsigned char i;
       
        i=length;
        do {
                s[i--]=ul % 10 + '0';
                ul = ul / 10;
        } while (ul);
}


unsigned long freq_measure(unsigned int dly, unsigned char T1_PS) {        //measure the frequency
        unsigned long tmp;                                        //temperary variable
        tmr1_init(T1_PS);                                                //initialize the tmr0
        TMR1ON=1;                                                        //turn on tmr1
        delay_ms(dly);                                        //delay 100ms. need to adjust the argument for precise timing
        TMR1ON=0;                                                        //turn off tmr1
        sTMR0=TMR1L + ((unsigned int) TMR1H <<8 );                        //save the tmr0 counter
        //tmp=((((unsigned long) tmr_count<<16) | sTMR0)<<(T1_PS))*(1000/dly);                //update tmr_count
        //tmp = (unsigned long) tmr_count<<16;
        tmp = ((unsigned long) tmr_count<<16) | sTMR0;
        tmp = tmp << (T1_PS);
        tmp = tmp * 1000/dly;
        return tmp;                                                        //update tmr_count
}

int main(void) {
       
        unsigned long freq;

        mcu_init();                                                                //initialize the mcu
        lcd_init();                                                                //initilize the lcd
        lcd_clear();
       
        TRISA |= CLK_IN;                                                 //set clk_in as input;

        strcpy(vRAM, "16F684 Frequency"); lcd_display(LCD_Line0, vRAM, LCD_NORMAL);
        strcpy(vRAM, "     Hello,     "); lcd_display(LCD_Line6, vRAM, LCD_NORMAL);
        strcpy(vRAM, "Left   +   Right"); lcd_display(LCD_Line7, vRAM, LCD_NORMAL);

        while (1) {
                cycle_count+=1;
                strcpy(vRAM, str1); ultoa(&vRAM[8], cycle_count, 7); lcd_display(LCD_Line3, vRAM, LCD_NORMAL);
                freq=freq_measure(Delay_MS_HF, T1_PS_HF);                                                //default is to measure HF
                if (freq<Freq_HF) freq=freq_measure(Delay_MS_MF, T1_PS_MF);                //then mf
                if (freq<Freq_LF) freq=freq_measure(Delay_MS_LF, T1_PS_LF);                //then lf
                strcpy(vRAM, str2); ultoa(&vRAM[8], tmr_count, 7); lcd_display(LCD_Line4, vRAM, LCD_NORMAL);
                strcpy(vRAM, str3); ultoa(&vRAM[8], sTMR0, 7); lcd_display(LCD_Line5, vRAM, LCD_NORMAL);
                //sprintf(vRAM, "f=%12uHz", freq); lcd_display(LCD_Line1, vRAM, LCD_NORMAL);
                strcpy(vRAM, str0); ultoa(&vRAM[1], freq, 12); lcd_display(LCD_Line1, vRAM, LCD_NORMAL);
        }
}

出0入0汤圆

 楼主| 发表于 2010-3-28 00:14:12 | 显示全部楼层
here is the real thing running at ~800Khz.


(原文件名:16F684 7110 Frequency Meter 800Khz.PNG)

出0入0汤圆

 楼主| 发表于 2010-3-29 07:13:53 | 显示全部楼层
here is a true minimalist implementation. Just a 16F684 + LCD (potentially a 10K resistor on t1cki).

You can probably solder the mcu on the back of the lcd and be done with it, :).


(原文件名:16F684 Frequency Meter.PNG)

the code is (roughly) the same.

出0入0汤圆

发表于 2010-3-29 11:35:21 | 显示全部楼层
Hi millwood0, can you briefly describe the principle of this auto-ranging frequency meter?
Really want to learn how does it work, and how you simulate on the Proteus environment?

出0入0汤圆

发表于 2010-3-29 13:03:50 | 显示全部楼层
很棒 受教了

出0入0汤圆

 楼主| 发表于 2010-7-4 08:20:35 | 显示全部楼层
This has got to be the world's simplest frequency meter:


(原文件名:16F684 Frequency Meter USART SW.PNG)

for a total of three parts - one mcu + two resistors which are optional, it measures frequency up to 4Mhz, and displays it on a computer with RS232 port.

The code is identical to what I posted earlier. The only change is the output device, from lcd to RS232 (aka HyperTerminal or the like). The HyperTerminal is set to 300bps - 9600bps, 8 data bits, 1 stop bits, no parity check.

You can power up the mcu from the RS232 and make it even simpler, :).

Enjoy.

出0入0汤圆

 楼主| 发表于 2010-7-4 08:25:28 | 显示全部楼层
the above shows the device measuring a 120Khz signal, and then a 1.2Khz signal, simulated in Proteus.

here is the code base. While the code utilizes UART/RS232, it does not require any UART hardware. Instead, it big-bangs RS232. As such, it runs on pretty much any MCU.

the code is compiled using 9.60std but you may wish to play with the timing in usart_sw.c for your compiler / platform.

enjoy.



点击此处下载 ourdev_565821WOMMBK.rar(文件大小:20K) (原文件名:16F684 Frequency Meter UART.rar)

出0入0汤圆

 楼主| 发表于 2010-7-4 08:28:10 | 显示全部楼层
the inspiration for using rs232 as the output device comes from here: http://panteltje.com/panteltje/pic/freq_pic/index.html

it provides fairly good instruction on wiring if you intend to power it from the host PC's rs232 port.

出0入0汤圆

发表于 2011-11-8 22:58:42 | 显示全部楼层
楼主个个都是强帖啊
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-20 22:14

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表