Re: Scaling ADC Result to a Set Range
Quote:
Originally Posted by
richard
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 ?
Code:
'****************************************************************
'* 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?
Re: Scaling ADC Result to a Set Range
Switched to using a 16F1825 that was previously rock solid and it's still jumping around like crazy. Could it be this 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:
Code:
' ****************************************************************
' 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?
Re: Scaling ADC Result to a Set Range
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
Code:
' ****************************************************************
' 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
Re: Scaling ADC Result to a Set Range
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.
Re: Scaling ADC Result to a Set Range
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.