Code:
' ***************************************************************
' Pin Connections
' ***************************************************************
' RA0/pin 13 -> B pin of rotary encoder
' RA1/pin 12 -> A pin of rotary encoder
' RA2/CCP3/pin 11 -> Stbd motor PWM output (motor 1)
' RA3/MCLR/pin 4 -> Button switch pin of rotary encoder
' RC0/pin 10 -> Stbd motor direction signal (motor 1)
' RC1/CCP4/pin 9 -> Port motor PWM output (motor 2)
' RC2/pin 8 -> Port motor direction signal (motor 2)
' RC4/Tx/pin 6 -> Serial LCD output
' RC5/Rx/pin 5 -> Serial input
DEFINE OSC 16 ; Set oscillator 16Mhz
DEFINE HSER_TXSTA 20h ; Set transmit status and control register
DEFINE HSER_BAUD 2400 ; Set baud rate
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG
' ***************************************************************
' Initialization
' ***************************************************************
OSCCON = %01111000 ' 16MHz internal osc
pause 100 ' As advised by Darrel Taylor for EEPROM issue
APFCON0.2 = 0 ; Tx on RC4 for LCD display
APFCON0.7 = 0 ; Rx on RC5
BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin
ANSELA = 0 ; Digital only for all PortA pins
ANSELC = 0 ; Diginal only for all PortC pins
TRISA = %00001011 ' Make PORTA pins 0-1 input for rotary encoder,
' pin 3 for rotary button
TRISC = 0 ' Make all PORTC pins output
' The INTEDG bit of the OPTION_REG register determines on which edge the
' interrupt will occur. When the INTEDG bit is set, the rising edge will
' cause the interrupt. When the INTEDG bit is clear, the falling edge will
' cause the interrupt.
OPTION_REG.6 = 1 ' 1=Rising edge (default) or button "PRESS";
' 0=Falling edge or button "RELEASE"
Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortA.1;
' Rot Enc pin B connected to PortA.0
Old_RPM VAR BYTE
i VAR BYTE
ButtonPress VAR PORTA.3 ' Alias PORTA.3 as "ButtonPress"
TimeCnt VAR Word
ISRCounter VAR BYTE
ValueDirty VAR BIT
' ***************************************************************
' Includes
' ***************************************************************
INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp3)
' ***************************************************************
' ASM Interrupt Definitions
' ***************************************************************
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, ReloadTMR1, ASM, no ; MUST be first
INT_Handler TMR1_INT, _T1handler, PBP, yes
INT_Handler IOC_INT, _RotEncAdjust, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
;--- Change these to match the desired interrupt frequency -------------------
;--- See http://DarrelTaylor.com/DT_INTS-14/TimerTemplate.html for more Info.
@Freq = 8 ; Frequency of Interrupts in Hz
@Prescaler = 8 ; Timers Prescaler setting
T1CON = $30 ; $30 = Prescaler 1:8, TMR1 OFF
; $00=1:1, $10=1:2, $20=1:4, $30=1:8 -- Must match @Prescaler value
' ***************************************************************
' Set default values
' ***************************************************************
Old_RPM = MotorRPM
Old_Bits = PORTA & (%00000011)
ISRCounter = 0
' Enable interrupts on RPM control now that motors are fully spun up
INTCON = %10001000 ' Global int enabled, IOCI enabled,
' INTF flag bit 0 clr, IOCI flag bit 0 clr
IOCAP.0 = 1 ' Enable positive (rising edge) change
IOCAP.1 = 1 ' Enable positive (rising edge) change
IOCAN.0 = 1 ' Enable negative (falling edge) change
IOCAN.1 = 1 ' Enable negative (falling edge) change
IOCAF.0 = 0 ' Clear interupt-on-change flag
IOCAF.1 = 0 ' Clear interupt-on-change flag
@ INT_ENABLE IOC_INT ; Interrupt-on-Change interrupt
@ INT_ENABLE TMR1_INT ; enable Timer 1 interrupts
GOSUB StartTimer ; Start Timer 1 ('MotorRPM' will be saved on every
; interrupt if dirty
Main:
' Check if motor RPM has changed
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB ChngMotorHPWM
EndIF
TimeCnt = 0
While ButtonPress = 0
TimeCnt = TimeCnt + 1
Pause 10
If TimeCnt > 500 Then BtnAction
Wend
BtnAction:
If TimeCnt > 0 and TimeCnt < 200 Then
PortEngDir = PortEngDir + 1
If PortEngDir > 1 Then PortEngDir = 0
WRITE EE_PortEngDir, PortEngDir
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["PortEngDir=", DEC PortEngDir, " ", 13, 10] ' Send text followed by carriage return and linefeed
#ENDIF
GOSUB ReversePortEng
ElseIf TimeCnt >= 200 Then
WRITE EE_MotorRPM, MotorRPM_Default ; restore Default value
MotorRPM = MotorRPM_Default
EndIf
GOTO Main
' ***************************************************************
' [IOC - interrupt handler]
' ***************************************************************
RotEncAdjust:
New_Bits = PORTA & (%00000011)
; IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc
IF New_Bits = Old_Bits Then DoneRotEnc
' Increment ISR counter (since quad encoder throws 4 interrupts
' for every single detent). Only want to update MotorRPM if this counter
' reaches 4 (i.e. a single detent)
ISRCounter = ISRCounter + 1
IF ISRCounter < 4 THEN DoneRotEnc
RotEncDir = New_Bits.1 ^ Old_Bits.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 'MaxDuty'
IF MotorRPM < MaxDuty then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF
; GOSUB StartTimer ; Start the TMR1 Timer to save MotorRPM
ValueDirty = 1
ISRCounter = 0
; WRITE EE_MotorRPM, MotorRPM
; pause 100
DoneRotEnc:
Old_Bits = New_Bits
IOCAF.0 = 0 ' Clear interrupt flags
IOCAF.1 = 0
@ INT_RETURN
;____This routine is Called on each TMR1 Interrupt____________________________
T1handler:
IF ValueDirty = 1 THEN
WRITE EE_MotorRPM, MotorRPM
; GOSUB StopTimer ; Stop the Timer
ValueDirty = 0
EndIf
@ INT_RETURN
;---[TMR1 reload - interrupt handler]-----------------------------------------
ASM ; Calculate Timer Reload Constant
ReloadInst = 8 ; # of Intructions used to reload timer
if ((Prescaler == 1)||(Prescaler == 2)||(Prescaler == 4)||(Prescaler == 8))
MaxCount = 65536 + (ReloadInst / Prescaler)
TimerReload = MaxCount - (OSC*1000000/4/Prescaler/Freq)
if ((TimerReload < 0) || (TimerReload > (65535-ReloadInst)))
error Invalid Timer Values - check "OSC", "Freq" and "Prescaler"
endif
else
error Invalid Prescaler
endif
ENDASM
@Timer1 = TMR1L ; map timer registers to a word variable
Timer1 VAR WORD EXT
TimerReload CON EXT ; Get the External Constant
TMR1ON VAR T1CON.0 ; Alias the Timers ON/OFF bit
;---Reload Timer1------
ASM
ReloadTMR1
MOVE?CT 0, T1CON, TMR1ON ; 1 stop timer
MOVLW LOW(TimerReload) ; 1 Add TimerReload to the
ADDWF TMR1L,F ; 1 value in Timer1
BTFSC STATUS,C ; 1/2
INCF TMR1H,F ; 1
MOVLW HIGH(TimerReload) ; 1
ADDWF TMR1H,F ; 1
MOVE?CT 1, T1CON, TMR1ON ; 1 start timer
INT_RETURN
ENDASM
;---Start/Stop controls -----
StartTimer:
Timer1 = TimerReload ; Load Timer
TMR1ON = 1 ; start timer
RETURN
StopTimer:
TMR1ON = 0 ; stop timer
RETURN
(When I turn off the PIC and turn it on again, it sometimes seems to speed up the motor to (MotorRPM - 1), i.e. 1 less than what was displayed on the LCD before turning off power.
Bookmarks