PDA

View Full Version : Scaling ADC Result to a Set Range



RossWaddell
- 25th October 2015, 23:04
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.



#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

mark_s
- 25th October 2015, 23:32
Hi,

You can try this idea. It will never go under 50% using 125 as a constant. Then divide your adcin by 2 this variable will be 0 to 127

Duty = 125 + adcin/2

RossWaddell
- 26th October 2015, 00:45
Hi,

You can try this idea. It will never go under 50% using 125 as a constant. Then divide your adcin by 2 this variable will be 0 to 127

Duty = 125 + adcin/2

Thanks Mark. Did you choose 125 because it's half of the max duty value with the prescaler and PR2 I have above?

mark_s
- 26th October 2015, 01:28
Yes, because you said you were using 8 bit pwm. You can change that to fit your needs.

RossWaddell
- 28th October 2015, 01:07
On another note, I observed that with ADCIN the readings fluctuate quite a lot and I only want to change the motor speed if the value has 'really' changed (i.e. by me). I tried this code and while it yields a rock-solid value, it doesn't go to 0: the readings go from 1-255. Any ideas why?



' PIC 16F1825 with a 10k pot attached to AN1 (the data sheet says this is the maximum impedance allowed)

#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' Vdd -> pin 1 -> +5V
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' 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
APFCON0.7 = 0 ; Rx on RC5

BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

FVRCON = 0 ; Fixed Voltage Reference is disabled
ADCON0 = %00000101 ; ADC (analog-to-digital) is enabled on AN1 (RA1) only
PAUSEUS 20 ; wait for the analog switch 'glitch' to die down
ADCON1.7 = 0 ; Left-justified results in 8-bits

#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

ADCInVal VAR BYTE ; stores ADCIN result read from trim pot

Main:
gosub Do_ADC
pause 100
#IFDEF USE_LCD_FOR_DEBUG
' DO SOMETHING HERE
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["ADC val=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed
#ENDIF

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 ' Wait for it to complete
WEND

ADCInVal = ADRESH
return

amgen
- 28th October 2015, 13:14
the voltage to the pot is probably fluxuating small amount....... add a good size cap to wiper arm (1 microfarad +-) to smooth voltage.

RossWaddell
- 28th October 2015, 14:31
the voltage to the pot is probably fluxuating small amount....... add a good size cap to wiper arm (1 microfarad +-) to smooth voltage.
I added a 1uF, 63V electrolytic cap between the middle wiper arm input to the PIC and GND, but no change. I measured the voltage on the middle wiper arm of the pot and with a reading of '255' I see 4.99V and with '1' I see 0.00V.

Am I doing something wrong with using ADRESH in 8-bit mode?

RossWaddell
- 28th October 2015, 16:00
Interestingly, if I switch to 10-bit resolution on ADC I get values from 5-1023. I think there must be something wrong with my config or code:



#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' Vdd -> pin 1 -> +5V
' RC5/Rx -> pin 5 -> EUSART receive
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RA1 -> pin 12 -> trim pot input (1 uF electrolytic cap?)
' Vss -> pin 14 -> GND

DEFINE OSC 16 ; Set oscillator 16Mhz

DEFINE ADC_BITS 10 ; 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
APFCON0.7 = 0 ; Rx on RC5

BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

FVRCON = 0 ; Fixed Voltage Reference is disabled
ADCON0 = %00000101 ; ADC (analog-to-digital) is enabled on AN1 (RA1) only
PAUSEUS 20 ; wait for the analog switch 'glitch' to die down
ADCON1.7 = 1 ; Right-justified results in 10-bits

#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

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
compVal VAR WORD ; stores last-changed ADC value

GOSUB Do_ADC
compVal = ADCInVal ; set initial compare value

Main:
gosub Do_ADC
pause 100
#IFDEF USE_LCD_FOR_DEBUG
If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn
' If this method of reading ADC is as rock-solid as it appears.
' then I can remove the +/- 1 before doing anything; it would be
' IF ADCInVal <> compVal

' DO SOMETHING HERE
compVal = ADCInVal
endif
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["ADC val=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed
#ENDIF

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
' (does this need to be the same as ADC_SAMPLEUS?)
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 ' Wait for it to complete
WEND

ADCInVal.HighBYTE = ADRESH
ADCInVal.LOWBYTE = ADRESL
return

RossWaddell
- 28th October 2015, 16:29
If I remove the DEFINEs usually associated with the ADCIN function:



'DEFINE ADC_BITS 10 ; 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)


and use Fosc/8 as the timer:



ADCON1 = %00010000 ; Left-justified results in 8-bits; Fosc/8 as timer


Then I get 0-255. If I do the same for 10-bit resolution, though, I now get 1-1023.

amgen
- 28th October 2015, 21:23
that's cool...... 1 count in 1023 is small..small error..... that error with 0 volts could be where pic ground is in relation to pot low end...... ground pot low end close to pic ground.....
what about the fluxuating readings ?

RossWaddell
- 28th October 2015, 21:37
that's cool...... 1 count in 1023 is small..small error..... that error with 0 volts could be where pic ground is in relation to pot low end...... ground pot low end close to pic ground.....
what about the fluxuating readings ?
Now that I'm not using PBP's ADCIN the readings are rock solid.

RossWaddell
- 29th October 2015, 00:54
I found the math behind the Arduino map function and have replicated it in a subroutine in PBP:



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
MinDuty CON 100 ; Minimum speed to rotate motor for this application
MaxDuty VAR WORD ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4

MaxDuty = (PR2 + 1) * 4 ; 252 but with prescaler resolution it's actually 250
MotorDuty VAR WORD ; Actual duty cycle for motor
MaxADCVal CON 255 ; 255 for 8-bit; 1023 for 10-bit
ADCInVal VAR BYTE ; stores ADCIN result read from trim pot
compVal VAR BYTE ; stores last-changed ADC value

Main:
gosub Do_ADC
pause 100

If ADCInVal <> compVal Then
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new ADCInVal=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed
#ENDIF

GOSUB Map_ADC_Val_to_PWM_Duty
gosub ChngMotorHPWM
compVal = ADCInVal
endif


GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND

ADCInVal = ADRESH

return

Map_ADC_Val_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;
' }

MotorDuty = (ADCInVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["MotorDuty=", DEC MotorDuty, " ", 13, 10] ; Send text followed by carriage return and linefeed
pause 1500
HSEROUT [LCD_INST, LCD_CLR]
#ENDIF

RETURN


It doesn't look like this is working as expected - any ideas?

HenrikOlsson
- 29th October 2015, 06:18
Hi,
I don't know exactly how you expect it to work but if Iäm not mistaken your formula, when re-written looks like:

MotorDuty = ADCInVAL * 252 / 255 + 100

When ADCInVal = 1 Motor Duty will be 100
When ADCInVAL = 255 MotorDuty will be 352

Is that what you're seeing?

/Henrik.

richard
- 29th October 2015, 10:41
(MaxDuty - MinDuty) would be 250(2)-100= 150

this would make more sense if your pwm value is a byte
it now becomes

MotorDuty = ADCInVAL */ 150(2) + 100


not knowing whats in the sub ChngMotorHPWM

snippets lead to speculation

RossWaddell
- 29th October 2015, 15:22
I'm using 8-bit resolution for PWM so it should be a byte but I have the variable defined as WORD in case the math takes it over 255:



MotorDuty VAR WORD ; Actual duty cycle for motor

ChngMotorHPWM:

CCP3CON.4 = MotorDuty.0
CCP3CON.5 = MotorDuty.1
CCPR3L = MotorDuty >> 2

CCP4CON.4 = MotorDuty.0
CCP4CON.5 = MotorDuty.1
CCPR4L = MotorDuty >> 2

RETURN


Do you think the issue would be resolved if I change the datatype of MotorDuty to BYTE? I was also thinking I might switch to 10-bit resolution for both ADC and PWM to get the finest grain control of the motor speed, but that may be overkill.

HenrikOlsson
- 29th October 2015, 18:47
> Do you think the issue would be resolved....

What issue? What exactly is it that doesn't work the way you expect? What do you expect and what does it do?

75% dutycycle (8bit resolution) +/-25% from an 8bit ADC readin:

PWMDuty = 191 + (64 - ADCValue >> 1)

191 is 75% of 255, your initial PWM duty cycle. When ADCvalue (BYTE) is 0 PWMDuty (BYTE) will be 255=100%. When ADCValue is 255 PWMDuty will be 127=50%.

/Henrik.

/Henrik.

RossWaddell
- 29th October 2015, 18:58
The goal is to scale the ADC values of 0-255 to a range of duty cycles appropriate for my project (100-252 with the chosen prescaler). If I manually calculate the results are correct:


ADCInVal =0 yields 100
ADCInVal=127 yields 175
ADCinVal=255 yields 252


But perhaps I don't have the order right in the expression because I don't see those results with the above code.

amgen
- 29th October 2015, 20:56
from excel chart, multiply adc val by .6 and add 100........ but can't mult decimal so multiply by 6 then divide by 10 then add 100

HenrikOlsson
- 29th October 2015, 21:57
Hi,
> but can't mult decimal
Sure you can, sort of, by using the */ or ** operators.


PWMDuty = 100 + (ADCInValue ** 39322) ' Same as 100 + ADCInValue * 0.600006 but likely faster than 100+ADCInValue*6/10


/Henrik.

RossWaddell
- 29th October 2015, 22:00
Thanks Henrik! I'll try that tonight.

RossWaddell
- 30th October 2015, 00:18
since I might have to change that constant of 39332, how did you arrive at that value?

RossWaddell
- 30th October 2015, 01:48
Your formula works, Henrik - not that should come as a surprise. Knowing how you arrived at that constant in the expression would help me me adjust for different resolutions.

Here's the code I used to test Henrik's formula:



#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' Vdd -> pin 1 -> +5V
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RA1 -> pin 12 -> trim pot input
' Vss -> pin 14 -> GND

DEFINE OSC 16 ; Set oscillator 16Mhz

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
APFCON0.7 = 0 ; Rx on RC5

BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

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

#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

MotorDuty VAR BYTE ; Actual duty cycle for motor
ADCInVal VAR BYTE ; stores ADCIN result read from trim pot
MinDuty CON 100 ; Minimum speed to rotate motor for this application

Main:
gosub Do_ADC
pause 100
GOSUB Map_ADC_Val_to_PWM_Duty

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND

ADCInVal = ADRESH

return


Map_ADC_Val_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;
' }

' MotorDuty = (ADCInVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty
MotorDuty = (ADCInVal ** 39322) + MinDuty ' Same as 100 + ADCInValue * 0.600006 but likely faster than 100+ADCInValue*6/10

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
' HSEROUT ["ADCInVal=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed
HSEROUT ["ADCInVal=", DEC ADCInVal, " ", 13, 10, "MotorDuty=", DEC MotorDuty]
' pause 5
' HSEROUT ["MotorDuty=", DEC MotorDuty, " "] ; Send text followed by carriage return and linefeed
#ENDIF

RETURN

Here's the annoying part - no matter how many searches I do here I can't find the syntax to print on the 2nd line of the LCD. With the above the screen just seems to flicker (which also doesn't make sense - with the ADC method here it usually results in a rock-solid value; in other words, it doesn't change until I actually move the wiper arm. I know it's unrelated to the original post, but any ideas?

HenrikOlsson
- 30th October 2015, 06:16
Hi,
Think of the ** operator as multiplying by units of 1/65536. 0.6*65536=39322.

As for printing to second line of the LCD you're obviously using some sort of serial interface display(?) which I know nothing about but if I'd make a guess based on your defined constants you'd do something like

HSEROUT[LCD_INST, LCD_L2, "2d Line"]


/Henrik.

RossWaddell
- 30th October 2015, 23:11
Thanks Henrik. You'd think I would have noticed that constant. Changing the HSEROUT to this:



#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' Vdd -> pin 1 -> +5V
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RA1 -> pin 12 -> trim pot input
' Vss -> pin 14 -> GND

DEFINE OSC 16 ; Set oscillator 16Mhz

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
APFCON0.7 = 0 ; Rx on RC5

BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

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

#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

MotorDuty VAR BYTE ; Actual duty cycle for motor
ADCInVal VAR BYTE ; stores ADCIN result read from trim pot
MinDuty CON 100 ; Minimum speed to rotate motor for this application

Main:
gosub Do_ADC
pause 100
GOSUB Map_ADC_Val_to_PWM_Duty

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND

ADCInVal = ADRESH

return


Map_ADC_Val_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;
' }

' MotorDuty = (ADCInVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty
MotorDuty = (ADCInVal ** 39322) + MinDuty ' Same as 100 + ADCInValue * 0.600006 but likely faster than 100+ADCInValue*6/10

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
' HSEROUT ["ADCInVal=", DEC ADCInVal, " ", 13, 10] ; Send text followed by carriage return and linefeed
HSEROUT ["ADCInVal=", DEC ADCInVal, " ", LCD_L2, "MotorDuty=", DEC MotorDuty, " ", 13, 10]
' pause 5
' HSEROUT ["MotorDuty=", DEC MotorDuty, " "] ; Send text followed by carriage return and linefeed
#ENDIF

RETURN



I can now see the 2nd line, but it's not starting at the first character position and there's a weird character there before 'MotorDuty'. It might be related to the spaces after the first line, but I'm not sure. Also, the first line seems to be refreshing/fluttering even when I'm not moving the trim pot. I have to admit, I use my serial LCD so infrequently that I'm a noobie where it comes to this.

Here's a video (http://vid188.photobucket.com/albums/z301/RossAWaddell/IMG_3663_zpsmffncx29.mp4) of what the screen looks like.

richard
- 31st October 2015, 00:13
try
HSEROUT ["ADCInVal=", DEC ADCInVal, " ", LCD_INST,LCD_L2, "MotorDuty=", DEC MotorDuty, " ",13,10]

RossWaddell
- 31st October 2015, 14:25
I used the HSEROUT line exactly as above and when I turn on the power:

8083

Then, as I turn the trim pot wiper:

8084

The serial LCD is 2400 baud by default and that's what I have set in code, but maybe there's some timing issues?

RossWaddell
- 31st October 2015, 20:26
If I'm using a PIC16F1825 how do I know whether to use the USART or EUSART settings from Mister E's PIC Multi-Calc application? This is what I have set currently:



DEFINE OSC 16 ; Set oscillator 16Mhz

DEFINE HSER_TXSTA 20h ; Set transmit status and control register
DEFINE HSER_BAUD 2400 ; Set baud rate
DEFINE HSER_CLROERR 1

OSCCON = %01111000 ; 16MHz internal osc

PAUSE 100

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


I see that Mister E's app doesn't use DEFINE HSER_BAUD. For USART I get:



DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 20h ' Enable transmit, BRGH = 0
DEFINE HSER_SPBRG 103 ' 2400 Baud @ 16MHz, 0.17%
DEFINE HSER_CLROERR 1 ' Clear overflow automatically


For EUSART:



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 130 ' 2400 Baud @ 16MHz, 0.0%
SPBRGH = 6
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator


Which should I use? And should I continue to use DEFINE HSER_BAUD which I think I got from this board a ling time ago?

richard
- 31st October 2015, 23:25
either setting will work the 0% error eusart option is probably best ,
if you set the baudrate regs manually ie
DEFINE HSER_SPBRG 130 ' 2400 Baud @ 16MHz, 0.0%/SPBRGH = 6
or
DEFINE HSER_SPBRG 103 ' 2400 Baud @ 16MHz, 0.17%

then DEFINE HSER_BAUD should not be used ,it may cause your setting to be overwritten

ps
baudrate error is not your display problem, you need to find out
1. how to initialise your display and how long it takes to happen
2. how to send control codes to the display ( seems $fe is not correct)

do you have any docs for the display / or a model no ?

RossWaddell
- 1st November 2015, 00:23
I switched to using the EUSART DEFINEs & register settings (commenting out the original ones) and it seems better (no initial wonky characters) but the 2nd line still isn't going to the 2nd line. Maybe if I split out the instruction for the 2nd line with a 5 ms pause?

Here's (http://www.jameco.com/Jameco/Products/ProdDS/150990.pdf) the data sheet for my serial LCD.

Archangel
- 1st November 2015, 00:40
From Data sheet
Smart Linefeed (control-J, ASCII 10)
Move cursor down one line. If immediately preceded by carriage return, linefeed is ignored

Try it without the 13, see what happens. Or try it with only 13 (more likely)
Failing that, if your message never changes, add spaces between " and MOTOR

richard
- 1st November 2015, 01:27
none of these are correct


#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



LCD_CLR CON 12 ' Clear screen
LCD_HOME CON 1 ' HOME
LCD_NEXT_LINE CON 13 ' CR does this go back to line 1 when current line is line2 ? seems it will go to first pos of line

LINE 2
WOULD BE HSEROUT [16,80] to be sure


TRY
HSEROUT [1,"ADCInVal=", DEC ADCInVal, " ", 13, "MotorDuty=", DEC MotorDuty, " "]
or
HSEROUT [1,"ADCInVal=", DEC ADCInVal, " ", 16,80, "MotorDuty=", DEC MotorDuty, " "]


ps the display needs 1000ms delay to power on/boot before you try to write to it

RossWaddell
- 1st November 2015, 02:04
none of these are correct



LCD_CLR CON 12 ' Clear screen
LCD_HOME CON 1 ' HOME
LCD_NEXT_LINE CON 13 ' CR does this go back to line 1 when current line is line2 ? seems it will go to first pos of line

LINE 2
WOULD BE HSEROUT [16,80] to be sure


TRY
HSEROUT [1,"ADCInVal=", DEC ADCInVal, " ", 13, "MotorDuty=", DEC MotorDuty, " "]
or
HSEROUT [1,"ADCInVal=", DEC ADCInVal, " ", 16,80, "MotorDuty=", DEC MotorDuty, " "]


ps the display needs 1000ms delay to power on/boot before you try to write to it
What makes you think they're not correct? The clear screen command has worked just fine all along. Nonetheless, I will try what you suggest.

richard
- 1st November 2015, 02:18
from your data sheet link


Clear Screen (control-L, ASCII 12)
Clear the screen.

you did read it ?

RossWaddell
- 1st November 2015, 14:09
Thanks to all your help, the screen is finally working. In case anyone is interested, here's the working code:



#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' Vdd -> pin 1 -> +5V
' 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 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

' USART 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 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 130 ' 2400 Baud @ 16MHz, 0.0%

' ************************************************** *************
' 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

BAUDCON.4 = 1 ; Transmit inverted data to the Tx pin

' From Mister E's Multi-Calc for EUSART:
SPBRGH = 6
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1
; (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

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

#IFDEF USE_LCD_FOR_DEBUG
' Display control codes for PC1602LRU-XWA serial LCD
' (see 'Dropbox\PBP Projects\PIC Datasheets\PC1602LRU-XWA LCD Datasheet.pdf'
' for list of control codes)
LCD_HOME CON 1 ' Cursor home
LCD_CLR CON 12 ' Clear screen
LCD_HIDE_CRSR CON 4 ' Hide cursor
LCD_NEXT_LINE CON 13 ' Move cursor to the first position of the next line
#ENDIF

MotorDuty VAR BYTE ; Actual duty cycle for motor
ADCInVal VAR BYTE ; stores ADCIN result read from trim pot
compVal VAR BYTE ; stores last-changed ADC value
MinDuty CON 100 ; Minimum speed to rotate motor for this application
MaxDuty CON 252
MaxADCVal CON 255 ; 255 for 8-bit; 1023 for 10-bit

#IFDEF USE_LCD_FOR_DEBUG
pause 1000
HSEROUT [LCD_CLR]
pause 5
HSEROUT ["LCD Init",LCD_NEXT_LINE]
pause 5
HSEROUT ["PC1602LRU-XWA"]
pause 1500
#ENDIF

gosub Do_ADC
compVal = ADCInVal ; set initial compare value
GOSUB Map_ADC_Val_to_PWM_Duty

Main:
gosub Do_ADC
pause 100

If ADCInVal <> compVal Then
compVal = ADCInVal
GOSUB Map_ADC_Val_to_PWM_Duty
endif

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND

ADCInVal = ADRESH

return


Map_ADC_Val_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;
' }

MotorDuty = (compVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_CLR]
pause 5
HSEROUT ["compVal=",DEC compVal," ",LCD_NEXT_LINE]
pause 5
HSEROUT ["MotorDuty=",DEC MotorDuty," "]
#ENDIF

RETURN

RossWaddell
- 17th February 2016, 21:18
Now that I'm not using PBP's ADCIN the readings are rock solid.
Well, they were rock solid. Now the readings are varying quite a lot and even with Melanie's 'averaging' code to pick the middle 8 values from 16 it's still jumping around a lot (and still doesn't read 0 when the trim pot wiper is at the GND end). If I measure the voltage reaching pin 6 (AN1) with a voltmeter it seems accurate and steady, varying from +5V to 0. What am I doing wrong now?



'************************************************* ***************
'* Name : Nacelle_Steady-On_Lights_12F1840_16Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2016 *
'* : All Rights Reserved *
'* Date : 02/02/2016 *
'* Version : 1.0 *
'* Notes : Steady-on nacelle engine lights (TOS Enterprise) *
'* : *
'************************************************* ***************


' TODO:
' Add gamma correction
' Increase ADC resolution to 1024
' Update PWM frequency/prescaler to use higher resolution



' The PWM/fade works perfectly when **not** connected to a FET
' - if using a FET, the LEDs 'pulse' and blink but don't fade in/out
' - use a 2N2222A transistor


DEFINE OSC 16 ' Set oscillator 16Mhz

' For production version of this code, comment out the line below re:
' LCD debugging and also update config fuses to have code protect on
#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' VDD -> pin 1 -> +5V
' RA5/Rc -> pin 2 -> EUSART receive
' RA2 -> pin 5 -> 1kohm -> 2N2222A transistor -> LEDs
' RA1 -> pin 6 -> Trim pot input
' RA0/Tx -> pin 7 -> EUSART transmit (LCD)
' VSS -> pin 8 -> GND

' ************************************************** *************
' 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
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#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 ' for EEPROM issue

APFCON.2 = 0 ; Tx on RA0 for LCD display
APFCON.7 = 1 ; Rc on RA5
APFCON.0 = 0 ; CCP1 on RA2

' 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 (EUSART):
' ************************************************** ***************
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator

' ************************************************** ***************

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
'PAUSEUS 20 ; wait for the analog switch 'glitch' to die down
ADCON1 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer;
; VREF is connected to VDD

TRISA = %00100010 ' Make all pins output except for RA1 (trim pot input)
' and RA5 (EUSART Rc)

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
LEDBrVal VAR WORD
FadeInPause CON 25 ; Pause during LED fade in

i VAR WORD

' ************************************************** *************
' Set up PWM on CCP1
' ************************************************** *************

CCP1CON = %00001100 ; Use CCP1 in PWM mode

' Set duty cycle registers initially to 0
CCP1CON.4 = 0
CCP1CON.5 = 0
CCPR1L = 0

' 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)

' ************************
' CCP1 uses TMR2
' ************************

T2CON = %00000110 ; Timer2 on with 1:16 prescaler
PR2 = 255 ; For 16Mhz OSC the desired output freq of 976.563Hz
; is achieved with this PR2 value (10-bit resolution
; with 1:16 prescaler)

MaxDuty VAR WORD ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4

MaxDuty = (PR2 + 1) * 4 ; 1024
MinDuty CON 0 ; Minimum brightness for this application
MaxADCVal CON 1023 ; 255 for 8-bit; 1023 for 10-bit
compVal VAR WORD ; stores last-changed ADC value


CounterA var BYTE ' Just a BYTE Temporary working variable
DataW var WORD ' Just a WORD Temporary working variable
RawData var WORD [16] ' Array holding ADC Result


#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

' 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 500
#ENDIF

GOSUB DO_ADCIN_Chk
LEDBrVal = ADCInVal
compVal = LEDBrVal
'compVal = ADCInVal
'GOSUB Map_VrefInVal_to_PWM_Duty

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["LEDBrVal=",DEC LEDBrVal," "]
pause 500
#ENDIF

' Fade in LED to final brightness
FOR i = 0 to LEDBrVal
CCP1CON.4 = i.0
CCP1CON.5 = i.1
CCPR1L = i >> 2

pause FadeInPause

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["i=",DEC i," "]
#ENDIF
NEXT i


Main:
GOSUB DO_ADCIN_Chk
PAUSE 100

IF ADCInVal <> compVal THEN
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "]
#ENDIF

LEDBrVal = ADCInVal
compVal = LEDBrVal
gosub ChngLEDBrightness
ENDIF
GOTO Main

'*********** Read ADC or USART Rc inputs *******************************
Do_ADCIN_Chk:
' Stuff 16 Element WORD Array full of ADC values
' ----------------------------------------------
For CounterA=0 to 15
ADCON0 = %00000101 ' Select Channel, Turn-On A/D
' 7=0 Unused
' 6=0 Unused
' 5=0 )
' 4=0 )
' 3=0 ) selects AN1
' 2=0 )
' 1=0 Go-done Bit
' 0=1 switch-On ADC Module
Pauseus 50 ' Wait for channel to setup
ADCON0.1 = 1 ' Start conversion
While ADCON0.1=1:Wend ' Wait for conversion
DataW.HighByte=ADRESH ' Read variable from ADC and save
DataW.LowByte=ADRESL
RawData(CounterA)=DataW
Next CounterA

' Sort ADC Input
' --------------
CounterA=0
GetADCSortLoop:
If RawData(CounterA+1) < RawData(CounterA) then
DataW=RawData(CounterA)
RawData(CounterA)=RawData(CounterA+1)
RawData(CounterA+1)=DataW
If CounterA>0 then CounterA=CounterA-2
endif
CounterA=CounterA+1
If CounterA < 15 then goto GetADCSortLoop

' Quanticise, discarding top and bottom FOUR elements
' ----------------------------------------------------
DataW=0
For CounterA=4 to 11
DataW=DataW+RawData(CounterA)
Next CounterA
ADCInVal=DataW>>3 ' Divide Result by EIGHT

' Read trim pot Vref to set LED brightness
' PAUSEUS 50 ' Wait for A/D channel acquisition time
' ADCON0.1 = 1 ' Start conversion

' WHILE ADCON0.1 = 1 ' Wait for it to complete
' WEND
' ADCInVal.HighBYTE = ADRESH
' ADCInVal.LOWBYTE = ADRESL

return

'*********** Set duty registers ****************************************
ChngLEDBrightness:
CCP1CON.4 = LEDBrVal.0
CCP1CON.5 = LEDBrVal.1
CCPR1L = LEDBrVal >> 2

RETURN

richard
- 17th February 2016, 22:07
after setting adc input you need to Wait A/D channel acquisition time before you Start conversion

RossWaddell
- 17th February 2016, 22:11
There's a PAUSEUS 50 there - isn't that sufficient?

richard
- 17th February 2016, 22:18
yep , must get some glasses. still looking

richard
- 17th February 2016, 23:19
for what its worth I tried your code cleaned up a bit and slightly changed to suit a 16f1825
and it works perfectly with nice steady results , could you have a noisy pot or power supply ?



'************************************************* ***************
'* Name : Nacelle_Steady-On_Lights_12F1840_16Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2016 *
'* : All Rights Reserved *
'* Date : 02/02/2016 *
'* Version : 1.0 *
'* Notes : Steady-on nacelle engine lights (TOS Enterprise) *
'* : *
'************************************************* ***************
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _CP_OFF & _WDTE_ON & _PWRTE_ON & _MCLRE_ON & _CLKOUTEN_OFF
__config _CONFIG2, _PLLEN_ON & _LVP_OFF
#ENDCONFIG

OSCCON=$70
DEFINE OSC 32

ANSELA = %00001000 ; Analog on PORTA.4(AN3) only

ADCON1 = %10100000 ; Right-justified results in 10-bits; Fosc/32 as timer;
; VREF is connected to VDD

TRISA = %111110 ' Make all pins input except for RA0 (ser out)
trisc= %11011111 ' ccp1

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
LEDBrVal VAR WORD
FadeInPause CON 25 ; Pause during LED fade in

i VAR WORD

' ************************************************** *************
' Set up PWM on CCP1
' ************************************************** *************

CCP1CON = %00001100 ; Use CCP1 in PWM mode

' Set duty cycle registers initially to 0
CCP1CON.4 = 0
CCP1CON.5 = 0
CCPR1L = 0
lata.0=1
pause 2000
' ************************
' CCP1 uses TMR2
' ************************

T2CON = %00000110 ; Timer2 on with 1:16 prescaler
PR2 = 255 ; For 16Mhz OSC the desired output freq of 976.563Hz
; is achieved with this PR2 value (10-bit resolution
; with 1:16 prescaler)

MaxDuty VAR WORD ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4

MaxDuty = (PR2 + 1) * 4 ; 1024
MinDuty CON 0 ; Minimum brightness for this application
MaxADCVal CON 1023 ; 255 for 8-bit; 1023 for 10-bit
compVal VAR WORD ; stores last-changed ADC value


CounterA var BYTE ' Just a BYTE Temporary working variable
DataW var WORD ' Just a WORD Temporary working variable
RawData var WORD [16] ' Array holding ADC Result


serout2 PORTA.0,84, ["ready",13,10 ]



GOSUB DO_ADCIN_Chk
LEDBrVal = ADCInVal
compVal = LEDBrVal

FOR i = 0 to LEDBrVal
CCP1CON.4 = i.0
CCP1CON.5 = i.1
CCPR1L = i >> 2

pause FadeInPause


NEXT i


Main:
GOSUB DO_ADCIN_Chk
PAUSE 100

IF ADCInVal <> compVal THEN

LEDBrVal = ADCInVal
compVal = LEDBrVal
gosub ChngLEDBrightness
ENDIF
GOTO Main

'*********** Read ADC or USART Rc inputs *******************************
Do_ADCIN_Chk:
' Stuff 16 Element WORD Array full of ADC values
' ----------------------------------------------
For CounterA=0 to 15
ADCON0 = %00001101 ' Select Channe3, Turn-On A/D

Pauseus 50 ' Wait for channel to setup
ADCON0.1 = 1 ' Start conversion
While ADCON0.1=1:Wend ' Wait for conversion
DataW.HighByte=ADRESH ' Read variable from ADC and save
DataW.LowByte=ADRESL
RawData(CounterA)=DataW
Next CounterA

' Sort ADC Input
' --------------
CounterA=0
GetADCSortLoop:
If RawData(CounterA+1) < RawData(CounterA) then
DataW=RawData(CounterA)
RawData(CounterA)=RawData(CounterA+1)
RawData(CounterA+1)=DataW
If CounterA>0 then CounterA=CounterA-2
endif
CounterA=CounterA+1
If CounterA < 15 then goto GetADCSortLoop

' Quanticise, discarding top and bottom FOUR elements
' ----------------------------------------------------
DataW=0
For CounterA=4 to 11
DataW=DataW+RawData(CounterA)
Next CounterA
ADCInVal=DataW>>3 ' Divide Result by EIGHT

serout2 PORTA.0,84, ["adc ",#ADCInVal,13,10 ]

return


'*********** Set duty registers ****************************************
ChngLEDBrightness:
CCP1CON.4 = LEDBrVal.0
CCP1CON.5 = LEDBrVal.1
CCPR1L = LEDBrVal >> 2

RETURN

RossWaddell
- 18th February 2016, 02:05
I removed the CCP stuff to leave just the ADC and still the value jumps around by +- 6 or more:



DEFINE OSC 16 ' Set oscillator 16Mhz

' For production version of this code, comment out the line below re:
' LCD debugging and also update config fuses to have code protect on
#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' VDD -> pin 1 -> +5V
' RA5/Rc -> pin 2 -> EUSART receive
' RA2 -> pin 5 -> 1kohm -> 2N2222A transistor -> LEDs
' RA1 -> pin 6 -> Trim pot input
' RA0/Tx -> pin 7 -> EUSART transmit (LCD)
' VSS -> pin 8 -> GND

' ************************************************** *************
' 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
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#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 ' for EEPROM issue

APFCON.2 = 0 ; Tx on RA0 for LCD display
APFCON.7 = 1 ; Rc on RA5
APFCON.0 = 0 ; CCP1 on RA2

' 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 (EUSART):
' ************************************************** ***************
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator

' ************************************************** ***************

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
PAUSEUS 20 ; wait for the analog switch 'glitch' to die down
ADCON1 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer;
; VREF is connected to VDD

TRISA = %00100010 ' Make all pins output except for RA1 (trim pot input)
' and RA5 (EUSART Rc)

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
compVal VAR WORD

#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

' 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 500
#ENDIF

GOSUB DO_ADCIN_Chk
compVal = ADCInVal

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["ADCInVal=",DEC ADCInVal," "]
pause 500
#ENDIF

Main:
GOSUB DO_ADCIN_Chk
PAUSE 100

IF ADCInVal <> compVal THEN
#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "]
#ENDIF
compVal = ADCInVal
ENDIF
GOTO Main

'*********** Read ADC or USART Rc inputs *******************************
Do_ADCIN_Chk:
' Read trim pot Vref to set LED brightness
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND
ADCInVal.HighBYTE = ADRESH
ADCInVal.LOWBYTE = ADRESL

return

RossWaddell
- 18th February 2016, 02:07
for what its worth I tried your code cleaned up a bit and slightly changed to suit a 16f1825
and it works perfectly with nice steady results , could you have a noisy pot or power supply ?



'************************************************* ***************
'* Name : Nacelle_Steady-On_Lights_12F1840_16Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2016 *
'* : All Rights Reserved *
'* Date : 02/02/2016 *
'* Version : 1.0 *
'* Notes : Steady-on nacelle engine lights (TOS Enterprise) *
'* : *
'************************************************* ***************
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _CP_OFF & _WDTE_ON & _PWRTE_ON & _MCLRE_ON & _CLKOUTEN_OFF
__config _CONFIG2, _PLLEN_ON & _LVP_OFF
#ENDCONFIG

OSCCON=$70
DEFINE OSC 32

ANSELA = %00001000 ; Analog on PORTA.4(AN3) only

ADCON1 = %10100000 ; Right-justified results in 10-bits; Fosc/32 as timer;
; VREF is connected to VDD

TRISA = %111110 ' Make all pins input except for RA0 (ser out)
trisc= %11011111 ' ccp1

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
LEDBrVal VAR WORD
FadeInPause CON 25 ; Pause during LED fade in

i VAR WORD

' ************************************************** *************
' Set up PWM on CCP1
' ************************************************** *************

CCP1CON = %00001100 ; Use CCP1 in PWM mode

' Set duty cycle registers initially to 0
CCP1CON.4 = 0
CCP1CON.5 = 0
CCPR1L = 0
lata.0=1
pause 2000
' ************************
' CCP1 uses TMR2
' ************************

T2CON = %00000110 ; Timer2 on with 1:16 prescaler
PR2 = 255 ; For 16Mhz OSC the desired output freq of 976.563Hz
; is achieved with this PR2 value (10-bit resolution
; with 1:16 prescaler)

MaxDuty VAR WORD ; According to Darrel:
; MaxDuty = (PR2 + 1) * 4

MaxDuty = (PR2 + 1) * 4 ; 1024
MinDuty CON 0 ; Minimum brightness for this application
MaxADCVal CON 1023 ; 255 for 8-bit; 1023 for 10-bit
compVal VAR WORD ; stores last-changed ADC value


CounterA var BYTE ' Just a BYTE Temporary working variable
DataW var WORD ' Just a WORD Temporary working variable
RawData var WORD [16] ' Array holding ADC Result


serout2 PORTA.0,84, ["ready",13,10 ]



GOSUB DO_ADCIN_Chk
LEDBrVal = ADCInVal
compVal = LEDBrVal

FOR i = 0 to LEDBrVal
CCP1CON.4 = i.0
CCP1CON.5 = i.1
CCPR1L = i >> 2

pause FadeInPause


NEXT i


Main:
GOSUB DO_ADCIN_Chk
PAUSE 100

IF ADCInVal <> compVal THEN

LEDBrVal = ADCInVal
compVal = LEDBrVal
gosub ChngLEDBrightness
ENDIF
GOTO Main

'*********** Read ADC or USART Rc inputs *******************************
Do_ADCIN_Chk:
' Stuff 16 Element WORD Array full of ADC values
' ----------------------------------------------
For CounterA=0 to 15
ADCON0 = %00001101 ' Select Channe3, Turn-On A/D

Pauseus 50 ' Wait for channel to setup
ADCON0.1 = 1 ' Start conversion
While ADCON0.1=1:Wend ' Wait for conversion
DataW.HighByte=ADRESH ' Read variable from ADC and save
DataW.LowByte=ADRESL
RawData(CounterA)=DataW
Next CounterA

' Sort ADC Input
' --------------
CounterA=0
GetADCSortLoop:
If RawData(CounterA+1) < RawData(CounterA) then
DataW=RawData(CounterA)
RawData(CounterA)=RawData(CounterA+1)
RawData(CounterA+1)=DataW
If CounterA>0 then CounterA=CounterA-2
endif
CounterA=CounterA+1
If CounterA < 15 then goto GetADCSortLoop

' Quanticise, discarding top and bottom FOUR elements
' ----------------------------------------------------
DataW=0
For CounterA=4 to 11
DataW=DataW+RawData(CounterA)
Next CounterA
ADCInVal=DataW>>3 ' Divide Result by EIGHT

serout2 PORTA.0,84, ["adc ",#ADCInVal,13,10 ]

return


'*********** Set duty registers ****************************************
ChngLEDBrightness:
CCP1CON.4 = LEDBrVal.0
CCP1CON.5 = LEDBrVal.1
CCPR1L = LEDBrVal >> 2

RETURN

The 5V voltage regulator is a linear switching one from Pololu and the voltmeter output looks rock solid. It could be the pot, though - it's a sliding one from Sparkfun. i'll try with a traditional trim pot and see if it behaves any different.

EDIT: just tried a trim pot that has previously worked fine and the same thing is happening. Am I doing anything wrong with the 10-bit ADC aspect? Could it be the PIC itself?

RossWaddell
- 18th February 2016, 23:08
Switched to using a 16F1825 that was previously rock solid and it's still jumping around like crazy. Could it be this (https://www.pololu.com/product/2843) 5V switching voltage regulator? This code was what I used previously to test 10-bit ADC and is what I just loaded onto the 16F1825:



' ************************************************** **************
' PIC16F1825
' ************************************************** **************

#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

' Vdd -> pin 1 -> +5V
' RC5/Rx -> pin 5 -> EUSART receive
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RA1 -> pin 12 -> trim pot input (1 uF electrolytic cap?)
' 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 (EUSART):
' ************************************************** ***************
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
' ************************************************** ***************

ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output

TRISA = %00000010 ; Make all pins output except for RA1 (trim pot input)
ANSELA = %00000010 ; Analog on PORTA.1 (AN1) only

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 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer

#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

' 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 500
#ENDIF

ADCInVal VAR WORD ; stores ADCIN result read from trim pot
compVal VAR WORD ; stores last-changed ADC value

GOSUB Do_ADC
compVal = ADCInVal ; set initial compare value

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["ADCInVal=",DEC ADCInVal," "]
pause 500
#ENDIF

Main:
gosub Do_ADC
pause 100

If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn
' If this method of reading ADC is as rock-solid as it appears.
' then I can remove the +/- 1 before doing anything; it would be
' IF ADCInVal <> compVal

' DO SOMETHING HERE
compVal = ADCInVal

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "]
#ENDIF
endif

GOTO Main

Do_ADC:
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND

ADCInVal.HighBYTE = ADRESH
ADCInVal.LOWBYTE = ADRESL
return


EDIT: Tried the standard 7805 voltage reg approach and it's more stable but still jumps around by 3-4. Could it be the settings for my serial LCD on EUSART Tx?

richard
- 19th February 2016, 00:00
I 'm using a pickit2 to power this test so an 0,1 are unavailable (used by pickit) so I changed code to use an3
and debug via serout . needless to say it works just fine.

you may need an oscilloscope to look at your an input and power supply for noise (dvm's won't do it)
have you installed bypass caps on the regulator input/output and the pic vdd pin ?
a bit of c across the pot wiper to gnd won't hurt either



' ************************************************** **************
' PIC16F1825
' ************************************************** **************
;#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use
' Vdd -> pin 1 -> +5V
' RC5/Rx -> pin 5 -> EUSART receive
' RC4/Tx -> pin 6 -> EUSART transmit (LCD)
' RA1 -> pin 12 -> trim pot input (1 uF electrolytic cap?)
' 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_ON & _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 (EUSART):
' ************************************************** ***************
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
' ************************************************** ***************
ANSELC = 0 ; Digital only for all PortC pins
TRISC = 0 ; Make all PORTC pins output
ANSELA = %00001000 ; Analog on PORTA.4(AN3) only
ADCON1 = %10100000 ; Right-justified results in 10-bits; Fosc/32 as timer;
; VREF is connected to VDD
TRISA = %111110 ' Make all pins output except for RA1 (trim pot 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 = %10010000 ; Right-justified results in 10-bits; Fosc/8 as timer
#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
' 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 500
#ENDIF
ADCInVal VAR WORD ; stores ADCIN result read from trim pot
compVal VAR WORD ; stores last-changed ADC value
GOSUB Do_ADC
compVal = ADCInVal ; set initial compare value

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["ADCInVal=",DEC ADCInVal," "]
pause 500
#ENDIF

lata.0=1
pause 2000
serout2 PORTA.0,84, ["ready",13,10 ]

Main:
gosub Do_ADC
pause 100
If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn
' If this method of reading ADC is as rock-solid as it appears.
' then I can remove the +/- 1 before doing anything; it would be
' IF ADCInVal <> compVal

' DO SOMETHING HERE
compVal = ADCInVal

#IFDEF USE_LCD_FOR_DEBUG
HSEROUT [LCD_INST, LCD_CLR]
pause 5
HSEROUT ["new ADCInVal", LCD_INST, LCD_L2, DEC ADCInVal, " "]
#ENDIF


endif

GOTO Main
Do_ADC:
ADCON0 = %00001101 ' Select Channel 3, Turn-On A/D
PAUSEUS 50 ' Wait for A/D channel acquisition time
ADCON0.1 = 1 ' Start conversion

WHILE ADCON0.1 = 1 ' Wait for it to complete
WEND
ADCInVal.HighBYTE = ADRESH
ADCInVal.LOWBYTE = ADRESL

serout2 PORTA.0,84, ["adc ",#ADCInVal,13,10 ]

return

RossWaddell
- 19th February 2016, 00:59
At 8-bit ADC it works much better; very little (if any) variation. Since I only need to adjust the brightness between 75-100% maybe 8-bit will be OK.

RossWaddell
- 20th February 2016, 01:09
Could the 0.1uF cap across Vdd/GND have an effect here? I just swapped out the one on the breadboard for another and now the readings seem stable.