Code:
'#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use
' ***************************************************************
' TODOs
' ***************************************************************
' 1. Need to calibrate motor RPMs so they spin at the same rate
' -> Separate "MotorDuty" EEPROM variables?
' ***************************************************************
' Pin Connections
' ***************************************************************
' Vdd -> pin 1 -> +5V
' RA5 -> pin 2 -> Blue LED (USART motor control indicator)
' RA4 -> pin 3 -> Green LED (ADC motor control indicator)
' RC5/Rx -> pin 5 -> EUSART receive
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RC2 -> pin 8 -> Port motor direction signal (motor 2)
' RC1/CCP4 -> pin 9 -> Port/Stbd motor PWM output (motor 1 & 2)
' RC0 -> pin 10 -> Stbd motor direction signal (motor 1)
' RA2/INT -> pin 11 -> Motor control option select button
' RA1 -> pin 12 -> trim pot input
' Vss -> pin 14 -> GND
DEFINE OSC 16 ; Set oscillator 16Mhz
' EUSART Settings for Tx/Rc (e.g. LCD)
' > use Mister E PIC Multi-Calc application to get register/DEFINE settings
' > as the values are dependent on the OSC and desired baud rate
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 9600 Baud @ 16MHz, -0.08%
' ***************************************************************
' Device Fuses
' ***************************************************************
#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
APFCON0.2 = 0 ; Tx on RC4 for LCD display
APFCON0.7 = 0 ; Rx on RC5
' Some LCD serial modules need inverted data, some do not
' Enable the line below if needed, but for SparkFun SerLCD it should be
' commented out
'BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin
' From Mister E's Multi-Calc for EUSART:
' *****************************************************************
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
' *****************************************************************
ANSELC = 0 ; Digital only for all PortC pins
TRISC = %00001000 ; Make all pins output except for RC3
; (motor control option button input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only
TRISA = %00000110 ; Make all pins output except for RA1 & RA2
; (trim pot input & button input)
FVRCON = 0 ; Fixed Voltage Reference is disabled
ADCON0 = %00000101 ; ADC enabled on AN1 (RA1) only; ADC conversion
; enabled
PAUSEUS 20 ; Wait for the analog switch 'glitch' to die down
ADCON1 = %00110000 ; Left-justified results in 8-bits; Frc as timer
' The INT pin can be used to generate an asynchronous edge-triggered interrupt.
' This interrupt is enabled by setting the INTE bit of the INTCON register. 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. The INTF bit of the INTCON register will be set when a valid
' edge appears on the INT pin. If the GIE and INTE bits are also set, the
' processor will redirect program execution to the interrupt vector.
OPTION_REG.6 = 1 ; 1=Rising edge (default) or button "PRESS";
; 0=Falling edge or button "RELEASE"
#IFDEF USE_LCD_FOR_DEBUG
' Display control codes for SerLCD serial LCD (SparkFun part #LCD-09395)
' (see 'Dropbox\PBP Projects\PIC Datasheets\SerLCD_V2_5 Datasheet.pdf'
' for list of control codes)
LCD_INST CON 254 ' instruction
LCD_CLR CON 1 ' Clear screen
LCD_L1 CON 128 ' LCD line 1
LCD_L2 CON 192 ' LCD line 2
LCD_BR_CMD CON 124 ' command character for adjusting backlight brightness
LCD_BR_LVL CON 140 ' 140==40%
#ENDIF
' ***************************************************************
' 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)
;-- Place a copy of these variables in your Main program -------------------
;-- The compiler will tell you which lines to un-comment --
;-- Do Not un-comment these lines --
;---------------------------------------------------------------------------
;wsave VAR BYTE $20 SYSTEM ' location for W if in bank0
;wsave VAR BYTE $70 SYSTEM ' alternate save location for W
' if using $70, comment wsave1-3
' --- IF any of these three lines cause an error ?? ------------------------
' Comment them out to fix the problem ----
' -- Which variables are needed, depends on the Chip you are using --
;wsave1 VAR BYTE $A0 SYSTEM ' location for W if in bank1
;wsave2 VAR BYTE $120 SYSTEM ' location for W if in bank2
;wsave3 VAR BYTE $1A0 SYSTEM ' location for W if in bank3
' --------------------------------------------------------------------------
' ***************************************************************
' ASM Interrupt Definitions
' ***************************************************************
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler INT_INT, _Mtr_Speed_Ctrl_Btn, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
' ***************************************************************
' Set up registers for PWM on CCP3 & CCP4
' ***************************************************************
'CCP3CON = %00001100 ; Use CCP3 in PWM mode
CCP4CON = %00001100 ; Use CCP4 in PWM mode
' Use Mister E's PICMultiCalc_1.3.1.exe application (Windows only)
' to determine prescaler and PR2 values for given OSC frequency (e.g. 16Mhz)
' and duty cycle (use 100% to see highest actual value)
T2CON = %00000101 ; Timer2 on with 1:4 prescaler
PR2 = 62 ; For 16Mhz OSC the desired output freq of 15,873Hz
; is achieved with this PR2 value (8-bit resolution
; with 1:4 prescaler)
; PWM freq must be ~ 16-20kHz to reduce noise
PreSpinVal CON 30 ; Value to subtract from MinDutyToSpin for motor spin up
MinDutyToSpin CON 75 ; 75 for 8-bit resolution
MinDuty CON 80 ; Minimum speed to rotate motor for this application
SpinUpPause VAR BYTE ; Pause during motor spin up
SpinUpPause = 75
#IFDEF USE_LCD_FOR_DEBUG
SpinUpPause = 17 ; Less pause is needed when using LCD
#ENDIF
MaxDuty VAR BYTE ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4
MaxDuty = (PR2 + 1) * 4 ; 252 but with prescaler resolution it's actually
; 250 (8-bit)
MotorDuty VAR BYTE ; Actual duty cycle for motor
i VAR BYTE
MaxADCVal CON 255 ; 255 for 8-bit; 1023 for 10-bit
VrefInVal VAR BYTE ; stores ADCIN result read from trim pot
compVal VAR BYTE ; stores last-changed ADC value
MOTOR_1_DIR VAR PORTC.0 ; Alias PORTC.0 as "MOTOR_1_DIR"
'MOTOR_1_PWM VAR PORTA.2 ; Alias PORTA.2 as "MOTOR_1_PWM"
MOTOR_2_DIR VAR PORTC.2 ; Alias PORTC.2 as "MOTOR_2_DIR"
MOTOR_2_PWM VAR PORTC.1 ; Alias PORTC.1 as "MOTOR_2_PWM"
ButtonPress VAR PORTA.2 ; Alias PORTA.2 as "ButtonPress"
LED_ADC VAR PORTA.4 ; Alias PORTA.4 as "LED_ADC"
LED_USART VAR PORTA.5 ; Alias PORTA.4 as "LED_USART"
Old_Mtr_Ctrl_Opt VAR BIT ; Stoes last set motor speed control option
' ***************************************************************
' Read EEPROM value(s)
' ***************************************************************
' 1 = Trim pot via ADC
' 0 = Serial data via USART Rc (external source)
Mtr_Ctrl_Opt_Default CON 1
EE_Mtr_Ctrl_Opt DATA Mtr_Ctrl_Opt_Default
Mtr_Ctrl_Opt VAR BIT
READ EE_Mtr_Ctrl_Opt, Mtr_Ctrl_Opt
' ***************************************************************
' Set default values
' ***************************************************************
' Set initial value of comparison variable
Old_Mtr_Ctrl_Opt = Mtr_Ctrl_Opt
' Turn on appropriate LED indicator
LED_ADC = Mtr_Ctrl_Opt
LED_USART = !Mtr_Ctrl_Opt ; inverts value of Mtr_Ctrl_Opt
'if Mtr_Ctrl_Opt = 1 then
' HIGH LED_ADC
' LOW LED_USART
'else
' LOW LED_ADC
' HIGH LED_USART
'endif
LOW MOTOR_1_DIR ; Set stbd motor (motor 1) to fwd (CW)
'LOW MOTOR_1_PWM
LOW MOTOR_2_DIR ; Set port motor (motor 2) to fwd (CW)
LOW MOTOR_2_PWM ; (motor will be connected in reverse in model for
; CCW movement)
' Should only need to do this one time to adjust backlight brightness
'#IFDEF USE_LCD_FOR_DEBUG
' HSEROUT [LCD_BR_CMD, LCD_BR_LVL]
' PAUSE 5
'#ENDIF
#IFDEF USE_LCD_FOR_DEBUG
pause 1000
HSEROUT [LCD_INST, LCD_CLR, "LCD Init"]
pause 5
HSEROUT [LCD_INST, LCD_L2, " SerLCD_V2_5"]
pause 2000
#ENDIF
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["Mtr_Ctrl_Opt=", DEC Mtr_Ctrl_Opt, " "]
pause 2000
#ENDIF
GOSUB Do_Speed_Chk
compVal = VrefInVal ; set initial compare value
GOSUB Map_VrefInVal_to_PWM_Duty
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["MotorDuty=",DEC MotorDuty," "]
pause 2000
#ENDIF
' Spin up motors to MotorDuty
' (Below a value of 'MinDutyToSpin', the motors don't move at all)
FOR i = 0 to MotorDuty
'FOR i = (MinDutyToSpin - PreSpinVal) to MotorDuty
' CCP3CON.4 = i.0
' CCP3CON.5 = i.1
' CCPR3L = i >> 2
CCP4CON.4 = i.0
CCP4CON.5 = i.1
CCPR4L = i >> 2
pause SpinUpPause
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["i=",DEC i," "]
#ENDIF
NEXT i
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["Final set Duty:", LCD_INST, LCD_L2," ",DEC MotorDuty]
#ENDIF
; Henrik Olson says I don't need this
; -----------------------------------
;INTCON = %10010000 ' Global int enabled (GIE), INTE enabled, INTF flag bit 0 clr
; -----------------------------------
@ INT_ENABLE INT_INT ; INT Port Change Interrupt
Main:
' Check if motor speed control option has changed
IF Mtr_Ctrl_Opt <> Old_Mtr_Ctrl_Opt Then
' Turn on appropriate LED indicator
LED_ADC = Mtr_Ctrl_Opt
LED_USART = !Mtr_Ctrl_Opt ; inverts value of Mtr_Ctrl_Opt
' Save selected motor speed control option
WRITE EE_Mtr_Ctrl_Opt, Mtr_Ctrl_Opt
PAUSE 100
Old_Mtr_Ctrl_Opt = Mtr_Ctrl_Opt
EndIF
gosub Do_Speed_Chk
pause 100
If VrefInVal <> compVal Then
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new VrefInVal=", DEC VrefInVal, " "]
#ENDIF
compVal = VrefInVal
GOSUB Map_VrefInVal_to_PWM_Duty
gosub ChngMotorHPWM
endif
GOTO Main
Do_Speed_Chk:
IF Mtr_Ctrl_Opt = 1 then
' Read trim pot Vref to set motor speed
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion
WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND
VrefInVal = ADRESH
else
' Get data from USART Rc channel
ENDIF
return
ChngMotorHPWM:
' CCP3CON.4 = MotorDuty.0
' CCP3CON.5 = MotorDuty.1
' CCPR3L = MotorDuty >> 2
CCP4CON.4 = MotorDuty.0
CCP4CON.5 = MotorDuty.1
CCPR4L = MotorDuty >> 2
RETURN
Map_VrefInVal_to_PWM_Duty:
' Arduino Map function to emulate:
' ===============================
' map(value, fromLow, fromHigh, toLow, toHigh)
' long map(long x, long in_min, long in_max, long out_min, long out_max)
' {
' return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
' }
' TODO: If switching from onboard trim pot to ext trim pot and there isn't one
' one connected to the 3-pin connector, may need to make in_min greater
' than 0
MotorDuty = (compVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty
' MotorDuty = (compVal ** 39322) + MinDuty ' Same as 100 + VrefInValue * 0.600006 but likely faster than 100+VrefInValue*6/10
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["compVal=",DEC compVal," ",LCD_INST, LCD_L2]
pause 1500
HSEROUT ["MotorDuty=",DEC MotorDuty," "]
#ENDIF
RETURN
END
' ***************************************************************
' [INT - interrupt handler]
' ***************************************************************
Mtr_Speed_Ctrl_Btn:
' Toggle motor speed control option (values are 0 or 1)
Mtr_Ctrl_Opt = NOT Mtr_Ctrl_Opt
' Shouldn't need to do this as the interrupt handler was defined above
' to have ResetFlag = yes
; INTCON.1 = 0 ' Clear RA2/INT External Interrupt Flag
@ INT_RETURN
Why would the conditional compile define cause such weird behaviour? Is it because I'm now transmitting serially @ 9600 baud? Are there other configuration parameters I need with this LCD?
Bookmarks