Scaling ADC Result to a Set Range


Closed Thread
Page 1 of 2 12 LastLast
Results 1 to 40 of 45
  1. #1

    Default Scaling ADC Result to a Set Range

    I'm using a trim pot to control a motor's speed via PWM on a PIC16F1825 but instead of the usual 0-100% duty cycles (corresponding to 0-255 from the ADCIN function with 8-bit resolution) I want to scale the adjustment of the duty to be around a set value. For example, if I determine that the typical duty cycle I want to provide in my application is 75% then I'd like to use the trim pot to adjust the value from 50-100% with 75% being the middle point of the pot. I can do this on an Arduino with the Map function but I can't seem to figure out how to do that similarly with PBP. Any guidance offered would be much appreciated.

    Code:
    #DEFINE USE_LCD_FOR_DEBUG   ; comment out for non-debug use
    
    ' ***************************************************************
    ' Pin Connections
    ' ***************************************************************
    
    ' Vdd      -> pin 1         -> +5V   
    ' RC4/Tx   -> pin 6         -> EUSART transmit (LCD)
    ' RC2      -> pin 8         -> Port motor direction signal (motor 2)
    ' RC1/CCP4 -> pin 9         -> Port motor PWM output (motor 2)
    ' RC0      -> pin 10        -> Stbd motor direction signal (motor 1)
    ' RA2/CCP3 -> pin 11        -> Stbd motor PWM output (motor 1)
    ' RA1      -> pin 12        -> trim pot input
    ' Vss      -> pin 14        -> GND
       
    DEFINE OSC 16               ; Set oscillator 16Mhz
    
    DEFINE ADC_BITS      8      ; Set number of bits in result
    DEFINE ADC_SAMPLEUS  50     ; Set sampling time in uS (was 5)
    DEFINE ADC_CLOCK     3      ; Set clock source (3=rc)
    
    DEFINE HSER_TXSTA   20h     ; Set transmit status and control register
    DEFINE HSER_BAUD    2400    ; Set baud rate
    
    ' ***************************************************************
    ' Device Fuses
    ' ***************************************************************
    
    #CONFIG
       __config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
       __config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
    #ENDCONFIG
    
    ' ***************************************************************
    ' Initialization
    ' ***************************************************************
    
    OSCCON    = %01111000       ; 16MHz internal osc
    
    APFCON0.2 = 0               ; Tx on RC4 for LCD display
    BAUDCON.4 = 1               ; Transmit inverted data to the Tx pin
    
    FVRCON    = 0               ; Fixed Voltage Reference is disabled
    ANSELA    = %00000010       ; Analog on PORTA.1 (AN1) only
    ADCON0    = %00000101       ; ADC (analog-to-digital) is enabled on AN1 (RA1) only
    ADCON1.7  = 0               ; Left-justified results in 8-bits
    TRISA     = %00000010	    ; Make all pins output except for RA1 (trim pot input)
    
    ANSELC = 0                  ; Digital only for all PortC pins
    TRISC  = 0                  ; Make all PORTC pins output
    
    MOTOR_1_DIR    VAR PORTC.0  ; Alias PORTC.0 as "MOTOR_1_DIR"
    MOTOR_1_PWM    VAR PORTA.2  ; Alias PORTA.2 as "MOTOR_1_PWM"
    MOTOR_2_DIR    VAR PORTC.2  ; Alias PORTC.2 as "MOTOR_2_DIR"
    MOTOR_2_PWM    VAR PORTC.1  ; Alias PORTC.1 as "MOTOR_2_PWM"
     
    #IFDEF USE_LCD_FOR_DEBUG
        LCD_INST   CON 254       ' instruction
        LCD_CLR    CON 1         ' Clear screen
        LCD_L1     CON 128       ' LCD line 1
        LCD_L2     CON 192       ' LCD line 2
    #ENDIF
    
    ' ***************************************************************
    ' Set up registers for PWM on CCP3 & CCP4
    ' ***************************************************************
    
    CCP3CON = %00001100         ; Use CCP3 in PWM mode
    CCP4CON = %00001100         ; Use CCP4 in PWM mode
    
    ' Use Mister E's PICMultiCalc_1.3.1.exe application (Windows only)   
    ' to determine prescaler and PR2 values for given OSC frequency (e.g. 16Mhz)
    ' and duty cycle (use 100% to see highest actual value)
                           
    T2CON   = %00000101         ; Timer2 on with 1:4 prescaler
    PR2     = 62                ; For 16Mhz OSC the desired output freq of 15,873Hz is
                                ; achieved with this PR2 value (8-bit resolution
                                ; with 1:4 prescaler)
                                
                                ; PWM freq must be ~ 16-20kHz to reduce noise
    
    PreSpinVal  CON 17          ; value to subtract from MinDuty for motor spin up 
    MinDuty     CON 75          ; 75 when max duty = 252 (8-bit resolution)
    ;SpinUpPause CON 17          ; Pause during motor spin up
    SpinUpPause VAR BYTE        ; Pause during motor spin up
    SpinUpPause = 99
    #IFDEF USE_LCD_FOR_DEBUG
        SpinUpPause = 17        ; Less pause is needed when using LCD
    #ENDIF
    MaxDuty     VAR WORD        ; According to Darrel:
                                ;   MaxDuty = (PR2 + 1) * 4
    
    MaxDuty = (PR2 + 1) * 4     ; 252 but with prescaler resolution it's actually 250
    
    DutyVar     VAR WORD        ; temp variable to store duty variable for CCP3/4
    MotorRPM    VAR BYTE    
    
    adcVal      VAR BYTE        ; stores ADCIN results  
                                                                  
    ' ***************************************************************
    ' Set default values
    ' ***************************************************************
    
    ADCIN 1, adcVal             ' Read channel 1 to adval
    MotorRPM = adcVal           ' (set speed for motors to spin up to)
    
    ' TODO: want to use trim pot to adjust motor speed but only within a
    '       a certain range; midpoint of the pot should be the typical speed
    '       observed from 'The Tholian Web' and the min/max adjustment accordingly
    '       (i.e. max is 100% duty cycle but if mid is 75% then min would be 50%)
    
    LOW MOTOR_1_DIR             ' Set stbd motor (motor 1) to fwd (CW)
    LOW MOTOR_2_DIR             ' Set port motor (motor 2) to fwd (CCW)
    
    ' Spin up motors to saved value of _MotorRPM
    ' (Below a value of 'MinDuty', the motors don't move at all)
    FOR DutyVar = (MinDuty - PreSpinVal) to MotorRPM
        'DutyVar4  = i
    
        CCP3CON.4 = DutyVar.0
        CCP3CON.5 = DutyVar.1
        CCPR3L    = DutyVar >> 2
    
        CCP4CON.4 = DutyVar.0
        CCP4CON.5 = DutyVar.1
        CCPR4L    = DutyVar >> 2 
                
        pause SpinUpPause
    
    #IFDEF USE_LCD_FOR_DEBUG
        HSEROUT [LCD_INST, LCD_CLR]
        pause 5
        HSEROUT ["RPM=", DEC DutyVar, "       ", 13, 10] ' Send text followed by carriage return and linefeed
    #ENDIF
    NEXT DutyVar
    
    #IFDEF USE_LCD_FOR_DEBUG
        HSEROUT [LCD_INST, LCD_CLR]
        pause 5
        HSEROUT ["RPM=", DEC MotorRPM, "       ", 13, 10] ' Send text followed by carriage return and linefeed
    #ENDIF
    
    Main:
        ' Check if motor RPM has changed
        ADCIN 1, adcVal        ' Read channel 0 to adval
    
    #IFDEF USE_LCD_FOR_DEBUG
        HSEROUT [LCD_INST, LCD_CLR]
        pause 5
        HSEROUT ["adcVal=", DEC adcVal, "       ", 13, 10] ' Send text followed by carriage return and linefeed
    #ENDIF
    
    ' TODO: only change motor speed if reading is changed by +/- 2 since the 
    '       value can change by +/- 1.
    '    If (adcVal - prevVal) > 1 Then
    '        gosub ChngMotorHPWM
    '    EndIf
     
     GOTO Main
    
    ChngMotorHPWM:
        DutyVar  = MotorRPM
    
        CCP3CON.4 = DutyVar.0
        CCP3CON.5 = DutyVar.1
        CCPR3L    = DutyVar >> 2
    
        CCP4CON.4 = DutyVar.0
        CCP4CON.5 = DutyVar.1
        CCPR4L    = DutyVar >> 2
        
        RETURN

  2. #2


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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
    Last edited by mark_s; - 26th October 2015 at 00:39.

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

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

  4. #4


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Yes, because you said you were using 8 bit pwm. You can change that to fit your needs.

  5. #5


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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?

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

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    the voltage to the pot is probably fluxuating small amount....... add a good size cap to wiper arm (1 microfarad +-) to smooth voltage.

  7. #7


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

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

  8. #8


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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:

    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

  9. #9


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    If I remove the DEFINEs usually associated with the ADCIN function:

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

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

  10. #10


    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 ?

  11. #11


    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.

  12. #12


    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?

  13. #13
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,517


    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.

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


    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

  15. #15


    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.

  16. #16
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,517


    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.

  17. #17


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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.

  18. #18


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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
    Attached Images Attached Images  
    Last edited by amgen; - 29th October 2015 at 22:03.

  19. #19
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,517


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Hi,
    > but can't mult decimal
    Sure you can, sort of, by using the */ or ** operators.
    Code:
    PWMDuty = 100 + (ADCInValue ** 39322)  ' Same as 100 + ADCInValue * 0.600006 but likely faster than 100+ADCInValue*6/10
    /Henrik.

  20. #20


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Thanks Henrik! I'll try that tonight.

  21. #21


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    since I might have to change that constant of 39332, how did you arrive at that value?

  22. #22


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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:

    Code:
    #DEFINE USE_LCD_FOR_DEBUG   ; comment out for non-debug use
       
    ' ***************************************************************
    ' Pin Connections
    ' ***************************************************************
    
    ' Vdd      -> pin 1         -> +5V   
    ' RC4/Tx   -> pin 6         -> EUSART transmit (LCD)
    ' 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?

  23. #23
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,517


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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
    Code:
    HSEROUT[LCD_INST, LCD_L2, "2d Line"]
    /Henrik.

  24. #24


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Thanks Henrik. You'd think I would have noticed that constant. Changing the HSEROUT to this:

    Code:
    #DEFINE USE_LCD_FOR_DEBUG   ; comment out for non-debug use
    
    ' ***************************************************************
    ' Pin Connections
    ' ***************************************************************
    
    ' Vdd      -> pin 1         -> +5V   
    ' RC4/Tx   -> pin 6         -> EUSART transmit (LCD)
    ' 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 of what the screen looks like.

  25. #25
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    try
    HSEROUT ["ADCInVal=", DEC ADCInVal, " ", LCD_INST,LCD_L2, "MotorDuty=", DEC MotorDuty, " ",13,10]




  26. #26


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    I used the HSEROUT line exactly as above and when I turn on the power:

    Name:  IMG_3667.JPG
Views: 996
Size:  109.9 KB

    Then, as I turn the trim pot wiper:

    Name:  IMG_3669.JPG
Views: 985
Size:  113.4 KB

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

  27. #27


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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:

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

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

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

  28. #28
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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 ?

  29. #29


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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 the data sheet for my serial LCD.

  30. #30
    Join Date
    Aug 2006
    Location
    Look, behind you.
    Posts
    2,818


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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
    Last edited by Archangel; - 1st November 2015 at 01:50.
    If you do not believe in MAGIC, Consider how currency has value simply by printing it, and is then traded for real assets.
    .
    Gold is the money of kings, silver is the money of gentlemen, barter is the money of peasants - but debt is the money of slaves
    .
    There simply is no "Happy Spam" If you do it you will disappear from this forum.

  31. #31
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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
    Last edited by richard; - 1st November 2015 at 02:34. Reason: ps and home display

  32. #32


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

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

  33. #33
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    from your data sheet link

    Clear Screen (control-L, ASCII 12)
    Clear the screen.
    you did read it ?

  34. #34


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    Thanks to all your help, the screen is finally working. In case anyone is interested, here's the working code:

    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

  35. #35


    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

  36. #36
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    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

  37. #37


    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?

  38. #38
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    yep , must get some glasses. still looking

  39. #39
    Join Date
    May 2013
    Location
    australia
    Posts
    2,383


    Did you find this post helpful? Yes | No

    Default Re: Scaling ADC Result to a Set Range

    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

  40. #40


    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

Similar Threads

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

Members who have read this thread : 2

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