Microcontroller Converts Frequency to Voltage with High Resolution
Download this article in PDF format.
When you need to convert a measurement signal from the digital to the analog domain, this design is a suitable solution with only two chips. Frequency-to-voltage conversion has many applications in instrumentation circuits.
This design (Fig. 1) is based on the 14-pin PIC Microcontroller 16F753, which has an embedded 16-bit counter and a 9-bit digital-to-analog converter (DAC). The input frequency range for this design is within 0 to 50 KHz, and its output voltage is within the range of 0 to 4.99 V, with a resolution of 10 mV.
To achieve the conversion, the input frequency is separated into four scales, which are manually selected by the inputs SEL1 and SEL2 (Fig. 2).
The DAC can deliver a maximum value of 4.99 V when its input code is 1FFh (511d), and 0.000V for a 000h input value. For the first scale, we get maximum and minimum values that are substituted in the following conversion equation:
Substituting those values, we get two equations:
Solving both equations we get:
And solving for M, we get:
Substituting both values in Equation 1, we get the offset value, and the result is Equation 3:
Now Equation 3 can be implemented in PIC basic code. But first, we need to measure the input frequency in 1.00 second intervals using TIMER1 as follows:
TMR1L = 0; Clearing TIMER1 registers TMR1H = 0; T1CON.0 = 1; TIMER 1 Enabled PAUSE 1000; for 1.00 Seconds T1CON.0 = 0; TIMER 1 Disabled COUNTER.BYTE0 = TMR1L; Storing both register in two bytes COUNTER.BYTE1 = TMR1H;
Now we can apply Equation 3 as follows:
DIV = COUNTER *1000 DAC = DIV32 9784 DAC = DAC + offset; freq offset = 0;If we get 2,500 pulses in TIMER1, for example, we can get the DAC’s value by dividing the pulses read by the constant 9.784 that we found previously:
Then, converting this to the software code, we get:
Now we can determine how many pulses are equivalent to each bit measured (Fig. 3).
For each scale, it’s necessary to obtain the constants by doing the same method used for Equations 1, 2, and 3. Thus, for the second scale (5-10 kHz), we get Equation 4:
Then we determine how many pulses are equivalent to each bit (Fig. 4).
For the third scale (10-15 kHz), we get Equation 5:
Then, we determine how many pulses are equivalent to each bit (Fig. 5).
For the fourth scale (10-50 kHz), we get Equation 6:
Then, finally, we determine how many pulses are equivalent to each bit in Figure 6.
Figures 7 and 8 show two cases in the scope for different input frequencies with their respective voltage output. The code listing below shows the software code implemented in the PIC16F753.
Ricardo Jimenez holds a Master’s degree in electronics. He is the author of the book “The PIC Microcontroller Notebook, Vol 3,” ISBN: 978-1-7325906-1-8.
Gabriel Lee Álvarez is an Electronics Engineering student at ITM.
Software Code for the Frequency-to-Voltage Converter Based on the PIC16F753
'* Name : FREQ-TO-VOLTAGE.BAS '* Authors : Ricardo Jimenez and Gabriel Lee Alvarez '* Version : 1 ; PIC16F753 ; Frequency to Voltage Converter ; 0hz - 5khz = 0v - 5 v; 1st Scale ;5khz - 10khz = 0 - 5v; 2nd Scale ;10khz - 15khz = 0 - 5v; 3rd Scale ;10khz - 50khz = 0 - 5v; 4th Scale ;pic16f753 ; Oscillator and PORTS Configuration OSCCON = $26; = $26; Clock set to 4 MHz OSCTUNE = 0; TRISA = %111110; RA0 IS A OUTPUT, RA1:RA5 AS INPUTS ANSELA = %000010; RA0:RA5 DIGIITALS TRISC = %0000000; RC0:RC2 AS INPUTS, RC3:RC5 AS OUTPUTS ANSELC = %000000; RC0:RC5 AS DIGITALS WPUA = %011100; RA2,RA3 PULL IS ENABLE WPUC = %000000 DEFINE LCD_DREG PORTC ' PORTC is LCD data port DEFINE LCD_DBIT 0 ' PORTC.0 is the data LSB DEFINE LCD_RSREG PORTC ' RS is connected to PORTC.4 DEFINE LCD_RSBIT 4 DEFINE LCD_EREG PORTC ' E is connected to PORTC.5 DEFINE LCD_EBIT 5 DEFINE LCD_BITS 4 ' 4 data line are used DEFINE LCD_LINES 2 ' It is a 2-line display DEFINE LCD_COMMANDUS 1500 ' Use 1500uS command delay DEFINE LCD_DATAUS 44 ' Use 44uS data delay ;---------SETTING UP LCD-------------------------------------------------------- LCDOUT $FE,$28; $28 FUNCTION SET, 4 BITS LCDOUT $FE,$10; $10 SHIFT DISPLAY LCDOUT $FE,$0C; $0C DISPLAY ON LCDOUT $FE,$06; $06 ENTRY MODE SET ;------------TIMER CONFIG ---------- T1CON = %10000100; $84 TIMER 1 DISABLE ;---HPWM SET to 250 Hz, when needed remove semicolons --- ;CCP1CON = %00001100; PWM mode selection and CCPx enabled ;PR2 = 79; Value obtained from equation ;T2CON = %00000100; enabling timer 2, PRESCALER 16 ;CCP1CON.5 =0 ;CCP1CON.4 =0 ;CCPR1L = %000101000; ;ADC ENABLED ADCON0 = %10000111; ENABLE ADC ADCON1 = %00000000;FOSC/2 ;-------- DAC CONFIG --------------------------------------- DAC1CON0 = %11100000;$E0, DAC ENABLED RIGHT JUSTIFIED ;---------DECLARING VARIABLES COUNTER VAR WORD; DECLARING COUNTING VARIABLES ;COUNTER.BYTE0 VAR TMR1L ;COUNTER.BYTE1 DAC VAR WORD; VARIABLE TO BE USED BY DAC SEL VAR BYTE; SCALE SELECTOR HZ VAR BYTE[5]; DIGITS FOR HERTZ DIV VAR WORD; IN VAR BYTE; VBE var word OUT VAR BYTE; I VAR WORD; I2 VAR WORD; ID VAR BYTE[3]; VIN VAR WORD; VID VAR BYTE[4]; VED VAR BYTE[4]; VIN2 VAR WORD; INVERT VAR PORTA.2; PIN FOR INVERTING DATA x var byte; VO VAR WORD[4]; OPTION_REG.7 = 0; ;--------PROGRAM STARTING ---------------- RPT: ;FIRST TEST, LET'S DO THE FIRST SCALE ;QUANTITY OF BITS IN THE DAC = 511, SO 5KHZ/511 ;5KHZ/511 = 9.7843 ;K=9.7843 FOR X = 0 TO 5; STARTING LOOPS HZ[X] = "0"; VO[X] = "0"; VIN = 0; CLEARING VARIABLES IN = 0; OUT = 0; SEL = 0; DAC = 0; DIV = 0; DAC = 0; VID[X] = "0"; ID[X] = "0" NEXT X; LCDOUT $FE,$C0,"WAITING FOR SCALE " OBTAIN_PULSES:; LCDOUT $FE,$80,"HZ= ",HZ[4],HZ[3],HZ[2],HZ[1],HZ[0]," Vout= ",VO[2],".",VO[1],VO[0]; TMR1L = 0; CLEARING REGISTERS IN TIMER1 TMR1H = 0; T1CON.0 = 1; TIMER 1 ENABLED PAUSE 1000; T1CON.0 = 0; TIMER 1 DISABLED COUNTER.BYTE0 = TMR1L; STORING LOW BYTE REGISTERS COUNTER.BYTE1 = TMR1H; STORING HI-BYTE REGISTERS FOR X = 0 TO 4; IN = COUNTER DIG X; GETTING DIGITS LOOKUP IN,["0123456789"],OUT; DECODING EACH DIGIT HZ[X] = OUT; STORING DIGITS NEXT X; LCDOUT $FE,$80,"HZ= ",HZ[4],HZ[3],HZ[2],HZ[1],HZ[0]," Vout= ",VO[2],".",VO[1],VO[0]; ;-----SELECTION------------ ;FOR X= 0 TO 255 SEL = (PORTA & %011000)>>3; READING PORTA ANS SHIFT RIGHT BITS 3 SPACES ;SELECTING SCALE IF SEL = %00 THEN GOSUB ESC1; 0-5KHZ IF SEL = %01 THEN GOSUB ESC2; 10K-50K IF SEL = %10 THEN GOSUB ESC3; 10KHZ-15KHZ IF SEL = %11 THEN GOSUB ESC4; 5KHZ-10KHZ IF INVERT = 0 THEN DAC = 511-DAC; INVERT DATA IF = 0 GOSUB V_DAC; LCDOUT $FE,$80,"HZ= ",HZ[4],HZ[3],HZ[2],HZ[1],HZ[0]," Vout= ",VO[2],".",VO[1],VO[0]; GOSUB DAC_OUT; GOTO OBTAIN_PULSES; GO TO LABEL OBTAIN_PULSES; ;------------------ FIRST SCALE ------------------------------ ESC1:; 0HZ A 5KHZ ; getting Scale values DIV = COUNTER *1000 DAC = DIV32 9784 IF (COUNTER >5000) THEN DAC = 0; ; EQUAL TO ZERO IF NOT IN RANGE LCDOUT $FE,$C0,"0-5KHZ DAC= ",dec dac," " RETURN; ;----------------------4th SCALE------------------- ESC4:; 10KHZ-50KHZ DIV = COUNTER*100 DAC = DIV32 7827 DAC = DAC - 127; IF (COUNTER >50000) OR (COUNTER <10000) THEN DAC = 0;EQUAL TO ZERO IF NOT IN RANGE LCDOUT $FE,$C0,"10-50KHZ DAC= ",DEC DAC," " RETURN; ;-----------------------3rd scale----------------- ESC3: ; ESCALA 10KHZ - 15KHZ DIV = COUNTER*1000 DAC = DIV32 9784 DAC = DAC - 1022; IF (COUNTER >15000) OR (COUNTER <10000) THEN DAC = 0; DAC=0 IF NOT IN RANGE LCDOUT $FE,$C0,"10-15KHZ DAC= ",DEC DAC," " RETURN; ;------------------------------------------------------------------------------ ESC2:; SCALE 5KHZ - 10KHZ ; --------------getting the values for this Scale IF (COUNTER >10000) OR (COUNTER <5000) THEN DAC = 0; DAC=0 IF NOT IN RANGE LCDOUT $FE,$C0,"5-10KHZ DAC= ",DEC DAC," " DIV = COUNTER*1000 DAC = DIV32 9784 DAC = DAC - 511; IF (COUNTER >10000) OR (COUNTER <5000) THEN DAC = 0 LCDOUT $FE,$C0,"5-10KHZ DAC= ",DEC DAC ," " RETURN; ;--VOLTAJE DAC--------- V_DAC: DISABLE VO[1]= DAC*976; GETTING VOLTAGE FROM DAC VO[3]= DIV32 100; WITH RESPECT TO NUMBER ENABLE FOR X = 0 TO 2; IN = VO[3] DIG (X+1); FIND THE RESPECTIVE DIGITS LOOKUP IN,["0123456789"],OUT;DECODING DIGITS VO[X] = OUT; STORE DIGITS NEXT X; RETURN; ;------------------------------------------------------------------------------- DAC_OUT: DAC1REFL = DAC.BYTE0; modifying the DAC0 register DAC1REFH = DAC.8; modifying DAC0-bit 8 RETURN; ;------------------------------ END;