Scaling ADC Result to a Set Range


Closed Thread
Results 1 to 40 of 45

Hybrid View

  1. #1


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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 ?

  2. #2


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Quote Originally Posted by amgen View Post
    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.

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    I found the math behind the Arduino map function and have replicated it in a subroutine in PBP:

    Code:
    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?

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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:
    Code:
    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.

  5. #5
    Join Date
    May 2013
    Location
    australia
    Posts
    2,636


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    (MaxDuty - MinDuty) would be 250(2)-100= 150

    this would make more sense if your pwm value is a byte
    it now becomes
    Code:
    MotorDuty = ADCInVAL */ 150(2) + 100

    not knowing whats in the sub ChngMotorHPWM

    snippets lead to speculation

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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:

    Code:
    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.

  7. #7
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    > 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:
    Code:
    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.

  8. #8


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Quote Originally Posted by RossWaddell View Post
    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?

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

  9. #9
    Join Date
    May 2013
    Location
    australia
    Posts
    2,636


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    after setting adc input you need to Wait A/D channel acquisition time before you Start conversion

  10. #10


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    There's a PAUSEUS 50 there - isn't that sufficient?

  11. #11
    Join Date
    May 2013
    Location
    australia
    Posts
    2,636


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    yep , must get some glasses. still looking

  12. #12


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    I removed the CCP stuff to leave just the ADC and still the value jumps around by +- 6 or more:

    Code:
    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

  13. #13


    Did you find this post helpful? Yes | No

    Default 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?
    Last edited by RossWaddell; - 18th February 2016 at 23:17.

  14. #14
    Join Date
    May 2013
    Location
    australia
    Posts
    2,636


    Did you find this post helpful? Yes | No

    Default 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
    Last edited by richard; - 19th February 2016 at 00:01. Reason: typo

  15. #15


    Did you find this post helpful? Yes | No

    Default 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.

Similar Threads

  1. Replies: 4
    Last Post: - 27th January 2015, 15:34
  2. Replies: 4
    Last Post: - 30th May 2012, 08:28
  3. Converting 10bit ADC result to 8 bit
    By jmgelba in forum mel PIC BASIC Pro
    Replies: 9
    Last Post: - 5th March 2012, 20:38
  4. strange A2D 5V scaling result - 16F819
    By Max Power in forum mel PIC BASIC Pro
    Replies: 3
    Last Post: - 8th April 2010, 02:28
  5. Scaling ADC values
    By purkolator in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 29th November 2007, 05:14

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts