PDA

View Full Version : Strange Behaviour with Conditional Compile



RossWaddell
- 19th November 2015, 16:33
With the line "#DEFINE USE_LCD_FOR_DEBUG" commented out the chip works perfectly; the PWM duty cycle sent to the SN754410 driver spins up the attached motor slowly and the trim pot attached to ADC 1 works smoothly in adjusting the speed.

When I uncomment it, though, (so I can see the debug info on the LCD) the motor immediately starts spinning when power is connected and the ADC behaviour is lagging, as if it takes more time to get the results.

I'm using a 16F1825 chip. What's weird is that this was working just fine with my old LCD @ 2400 baud but since I accidentally fried that and had to get a new one I changed it to 9600 baud as that's the default with the new serial LCD I got from SparkFun (I also needed to comment out "BAUDCON.4 = 1" with this LCD as otherwise it displayed garbage)



'#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?

HenrikOlsson
- 19th November 2015, 19:33
Hi,
You have a lot of PAUSE statements within the LCD sections, most noticable is the PAUSE 1500 (1.5 seconds!) in the Map_VrefInVal_to_PWM_Duty subroutine which you call each time thru your Main routine. When you don't include the LCD sections in the code it runs MUCH faster compared to when you DO include the LCD sections.

/Henrik.

RossWaddell
- 19th November 2015, 19:51
Hi,
You have a lot of PAUSE statements within the LCD sections, most noticable is the PAUSE 1500 (1.5 seconds!) in the Map_VrefInVal_to_PWM_Duty subroutine which you call each time thru your Main routine. When you don't include the LCD sections in the code it runs MUCH faster compared to when you DO include the LCD sections.

/Henrik.
Thanks Henrik. That would definitely seem to explain the slow reaction to reading ADC during the main loop, but why would the duty cycle on the PWM output go HIGH as soon as power is turned on?

HenrikOlsson
- 19th November 2015, 20:46
Hi

You might want to make sure that the dutycycle is set to 0 before enabling the PWM module.
As it is now, when you "activate" the LCD sections, there will be a 7 second delay between the PWM module being enabled (you setting CCP4CON at the start of the program) and the time at which the ramp up actually starts, ie when you actually set the dutycycle to 0.

/Henrik.

RossWaddell
- 19th November 2015, 20:49
Thanks again, Henrik. Do you mean I should have:



CCP4CON.4 = 0
CCP4CON.5 = 0
CCPR4L = 0


before the CCP4CON register setting?

I will try that out tonight, as well as reducing the PAUSE statements.

RossWaddell
- 20th November 2015, 02:01
As usual, you were right Henrik. I added the code above right after turning on CCP/PWM on CCP4 and reduced all the pauses within the #IFDEF USE_LCD_FOR_DEBUG and now everything works as before. It was obvious once you pointed it out, so thanks again for being so patient.