It compiles to approx. 1700 words, making it a nice project that works with the free version of the compilers. The demo project was tested with a 16F877A, but will work nicely with a 18-pin 16F628A.
It is very accurate compared with commercial test instruments. Photos below.
Code: Select all
'*******************************************************************************
'*******************************************************************************
'
' SERVO PWM_METER USING 16F877A @ 8MHz; EasyPIC4 And 16x2 LCD
' Displays PWM Period/Duty-Period And Frequency/Duty-Percent
' LCD Screen Toggles Using PORTA.0 Pushbutton
'
' Code Compiled with mikroBasic 7.2 for 16F877A @ 8MHz & Default Configs
' by Warren Schroeder on June 15,2008
'
' Demonstrates Use Of Input Capture Feature of CCP1 On Pin RC2
'
'*******************************************************************************
'*******************************************************************************
Const Capture_RE as Byte = 5 ' CCP1CON Value For Rising Edge
Const Capture_FE as Byte = 4 ' CCP1CON Value For Falling Edge
Dim CCPR1 as Word Absolute $15 Volatile ' CCPR1 location
Dim Period as Word ' Rising Edge to Rising Edge value
Dim StartEdge as Word ' First Rising Edge Timer1 value
Dim DCEdge as Word ' Falling Edge Timer1 value
Dim DutyCycle as Word ' Rising Edge to Falling Edge value
Dim RisingEdge as Boolean ' Switch Edge Detection Flag
Dim LCDOUT as Boolean ' Switch LCD Screen Flag
dim t0 as LongWord ' temp var
Dim PerStr as String[6] Absolute $20 ' Period String for LCD
Dim DCStr as String[6] Absolute $26 ' Duty Cycle String for LCD
sub procedure interrupt ' ISR to manage edge detection
If RisingEdge Then
CCP1CON = Capture_FE ' set up to detect next edge
StartEdge = CCPR1
Period = CCPR1 - DCEdge + DutyCycle
Else
CCP1CON = Capture_RE ' set up to detect next edge
DCEdge = CCPR1
DutyCycle = CCPR1 - StartEdge
End If
RisingEdge = Not(RisingEdge) ' toggle edge detection flag
PIR1.CCP1IF = 0 ' clear CCP1 interrupt flag
end sub
sub procedure CCP_Setup()
T1CON = 16 ' Timer1 prescaler = 2 = 1us
CCP1CON = Capture_RE ' RC2 is CCP1 Capture Input
TRISC.2 = 1 ' Tris RC2 as Input
PIE1.CCP1IE = 1 ' Enable CCP1 interrupt
PIR1.CCP1IF = 0 ' Clear CCP1 Interrupt Flag
INTCON = 192 ' Global & Peripheral interrupts enabled
T1CON.TMR1ON = 1 ' Start Timer1
end sub
sub procedure AddDec(dim byref lcdstr as string[6]) ' Adds decimal to LCD output string
dim t1,t2,t3 as byte
FSR = @lcdstr
t1 = INDF
inc(FSR)
t2 = INDF
inc(FSR)
t3 = INDF
INDF = 46
dec(FSR)
INDF = t3
dec(FSR)
INDF = t2
dec(FSR)
INDF = t1
end sub
sub procedure LCD_Output_Manager() ' manage LCD screen output between
LCD_OUT(2,1," ") ' Period/Duty-Period and
If LCDOUT Then ' Frequency/Duty-Percent
LCD_OUT(1,1,"FREQUENCY DUTY ")
t0 = 100000000 div Period
WordToStr(t0, PerStr)
t0 = DutyCycle * 10000 div Period
WordToStr(t0, DCStr)
AddDec(PerStr)
LCD_OUT(2,2,PerStr)
LCD_OUT_CP("hz")
AddDec(DCStr)
LCD_OUT(2,11,DCStr)
LCD_CHR_CP("%")
Else
LCD_OUT(1,1," PERIOD DUTY ")
WordToStr(Period, PerStr)
LCD_OUT(2,1,PerStr)
LCD_OUT_CP("us")
WordToStr(DutyCycle, DCStr)
LCD_OUT(2,10,DCStr)
LCD_OUT_CP("us")
End If
end sub
main:
ADCON1 = 6 ' disable adc's
CMCON = 7 ' disable comparators
PORTA = 0
PORTB = 0
PORTC = 0
PORTD = 0
TRISA = 1 ' RA0 is switch input pulled low active high
TRISB = 0
TRISC = 0
TRISD = 0
RisingEdge = False
LCDOUT = False
Period = 0
DutyCycle = 0
StartEdge = 0
DCEdge = 0
LCD_INIT(PORTD)
LCD_CMD(LCD_CURSOR_OFF)
CCP_Setup()
While 1=1
Delay_ms(100) ' LCD screen refresh delay
LCD_Output_Manager()
If PORTA.0 = 1 Then ' RA0 Button Manager; If 1 Then....
LCDOUT=NOT(LCDOUT) ' toggle LCD Ouput Flag
LCD_Output_Manager() ' toggle LCD Screen
While PORTA.0 = 1 ' If RA0 still pressed then wait
Delay_ms(5) ' until released
Wend
End If
Wend
end.