I'm using a trim pot to control a motor's speed via PWM on a PIC16F1825 but instead of the usual 0-100% duty cycles (corresponding to 0-255 from the ADCIN function with 8-bit resolution) I want to scale the adjustment of the duty to be around a set value. For example, if I determine that the typical duty cycle I want to provide in my application is 75% then I'd like to use the trim pot to adjust the value from 50-100% with 75% being the middle point of the pot. I can do this on an Arduino with the Map function but I can't seem to figure out how to do that similarly with PBP. Any guidance offered would be much appreciated.
Code:
#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use
' ***************************************************************
' Pin Connections
' ***************************************************************
' Vdd -> pin 1 -> +5V
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RC2 -> pin 8 -> Port motor direction signal (motor 2)
' RC1/CCP4 -> pin 9 -> Port motor PWM output (motor 2)
' RC0 -> pin 10 -> Stbd motor direction signal (motor 1)
' RA2/CCP3 -> pin 11 -> Stbd motor PWM output (motor 1)
' RA1 -> pin 12 -> trim pot input
' Vss -> pin 14 -> GND
DEFINE OSC 16 ; Set oscillator 16Mhz
DEFINE ADC_BITS 8 ; Set number of bits in result
DEFINE ADC_SAMPLEUS 50 ; Set sampling time in uS (was 5)
DEFINE ADC_CLOCK 3 ; Set clock source (3=rc)
DEFINE HSER_TXSTA 20h ; Set transmit status and control register
DEFINE HSER_BAUD 2400 ; Set baud rate
' ***************************************************************
' 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
APFCON0.2 = 0 ; Tx on RC4 for LCD display
BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin
FVRCON = 0 ; Fixed Voltage Reference is disabled
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only
ADCON0 = %00000101 ; ADC (analog-to-digital) is enabled on AN1 (RA1) only
ADCON1.7 = 0 ; Left-justified results in 8-bits
TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output
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"
#IFDEF USE_LCD_FOR_DEBUG
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
#ENDIF
' ***************************************************************
' 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 17 ; value to subtract from MinDuty for motor spin up
MinDuty CON 75 ; 75 when max duty = 252 (8-bit resolution)
;SpinUpPause CON 17 ; Pause during motor spin up
SpinUpPause VAR BYTE ; Pause during motor spin up
SpinUpPause = 99
#IFDEF USE_LCD_FOR_DEBUG
SpinUpPause = 17 ; Less pause is needed when using LCD
#ENDIF
MaxDuty VAR WORD ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4
MaxDuty = (PR2 + 1) * 4 ; 252 but with prescaler resolution it's actually 250
DutyVar VAR WORD ; temp variable to store duty variable for CCP3/4
MotorRPM VAR BYTE
adcVal VAR BYTE ; stores ADCIN results
' ***************************************************************
' Set default values
' ***************************************************************
ADCIN 1, adcVal ' Read channel 1 to adval
MotorRPM = adcVal ' (set speed for motors to spin up to)
' TODO: want to use trim pot to adjust motor speed but only within a
' a certain range; midpoint of the pot should be the typical speed
' observed from 'The Tholian Web' and the min/max adjustment accordingly
' (i.e. max is 100% duty cycle but if mid is 75% then min would be 50%)
LOW MOTOR_1_DIR ' Set stbd motor (motor 1) to fwd (CW)
LOW MOTOR_2_DIR ' Set port motor (motor 2) to fwd (CCW)
' Spin up motors to saved value of _MotorRPM
' (Below a value of 'MinDuty', the motors don't move at all)
FOR DutyVar = (MinDuty - PreSpinVal) to MotorRPM
'DutyVar4 = i
CCP3CON.4 = DutyVar.0
CCP3CON.5 = DutyVar.1
CCPR3L = DutyVar >> 2
CCP4CON.4 = DutyVar.0
CCP4CON.5 = DutyVar.1
CCPR4L = DutyVar >> 2
pause SpinUpPause
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["RPM=", DEC DutyVar, " ", 13, 10] ' Send text followed by carriage return and linefeed
#ENDIF
NEXT DutyVar
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["RPM=", DEC MotorRPM, " ", 13, 10] ' Send text followed by carriage return and linefeed
#ENDIF
Main:
' Check if motor RPM has changed
ADCIN 1, adcVal ' Read channel 0 to adval
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["adcVal=", DEC adcVal, " ", 13, 10] ' Send text followed by carriage return and linefeed
#ENDIF
' TODO: only change motor speed if reading is changed by +/- 2 since the
' value can change by +/- 1.
' If (adcVal - prevVal) > 1 Then
' gosub ChngMotorHPWM
' EndIf
GOTO Main
ChngMotorHPWM:
DutyVar = MotorRPM
CCP3CON.4 = DutyVar.0
CCP3CON.5 = DutyVar.1
CCPR3L = DutyVar >> 2
CCP4CON.4 = DutyVar.0
CCP4CON.5 = DutyVar.1
CCPR4L = DutyVar >> 2
RETURN