Weird ADC / Math Results with 9-Bit PWM


Closed Thread
Results 1 to 22 of 22

Hybrid View

  1. #1


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    Quote Originally Posted by tumbleweed View Post
    I think the justification is ok... if you want to use the ADC in 8-bit mode then you want it to be left-justified (ADFM=0)
    and just read the ADRESH register, disgarding the lower bits in ADRESL.

    I think the problem is in the section that averages the readings
    Code:
    '	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
    Here DataW must be a WORD since you're adding 8 byte values together which could give you 8 x 255 = 2040.
    Thanks tumbleweed. I think I had that as a WORD earlier, but I will try it again today.

  2. #2


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    Reset DataW to WORD variable (as it should have been all along) as well as the two ADC variables (since it's left-justified or 8-bit) but am still only getting a max value of 254 from the Map_ADCInVal_to_PWM_Duty function. The ADC is now correctly getting ranges of 0-255, but the LEDBrVal is capped at 254 (should be 512). Also, when I move the trim pot wiper the LED doesn't change brightness as smoothly as with PR2=24; it kinda flickers a bit.

    Code:
    ' 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
    
    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
    ' ***************************************************************
    ' 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                    
    
    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 for EUSART:
    ' *****************************************************************
    SPBRGH    = 1
    BAUDCON.3 = 1                ' Enable 16 bit baudrate generator
    ' *****************************************************************
    
    TRISA     = %00100010	     ' Make all pins output except for RA1 (trim pot input)
                                 ' and RA5 (EUSART Rc)
    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    = %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
        LCD_SP_CMD CON 124       ' special command character (for adjusting backlight brightness or toggling splash screen)
        LCD_BR_LVL CON 140       ' 140==40%
        LCD_SPLASH CON 9         ' toggles splash screen display
    #ENDIF
    
    ADCInVal       VAR BYTE      ; stores ADCIN result read from trim pot
    compVal        VAR BYTE      ; 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 BYTE [16] ' Array holding ADC Result
    
    LEDBrVal       VAR WORD
    
    i              VAR WORD
    
    ' Should only need to do this one time to adjust backlight brightness
    '#IFDEF USE_LCD_FOR_DEBUG
    '    HSEROUT [LCD_SP_CMD, LCD_BR_LVL]
    '    PAUSE 5
    '#ENDIF
    
    ' Should only need to do this one time to disable splash screen
    '#IFDEF USE_LCD_FOR_DEBUG
    '    HSEROUT [LCD_SP_CMD, LCD_SPLASH]
    '    PAUSE 5
    '#ENDIF
    
    #IFDEF USE_LCD_FOR_DEBUG
        pause 1000
        HSEROUT [LCD_INST, LCD_CLR, "LCD Init"]
        pause 5
        HSEROUT [LCD_INST, LCD_L2, "  SerLCD_V2_5"]
        pause 2000
    #ENDIF
    
    ' ***************************************************************
    ' 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     = 127               ; For 16Mhz OSC the desired output freq of 1954Hz
                                ; is achieved with this PR2 value (9-bit resolution
                                ; with 1:16 prescaler)
                                ; MAX DUTY = 100
                                
    MaxDuty       VAR WORD      ; According to Darrel:
                                ;   MaxDuty = (PR2 + 1) * 4
    
    MaxDuty = (PR2 + 1) * 4     ; 100
    MinDuty       CON 100       ; Minimum brightness for this application
    MaxADCVal     CON 255       ; 255 for 8-bit; 1023 for 10-bit
    
    FadeInPause   CON 50        ; Pause during LED fade in
    
    GOSUB Do_ADC
    compVal = ADCInVal          ; set initial compare value
    GOSUB Map_ADCInVal_to_PWM_Duty
    
    #IFDEF USE_LCD_FOR_DEBUG
        HSEROUT [LCD_INST, LCD_CLR]
        pause 5
        HSEROUT ["LEDBrVal=",DEC LEDBrVal,"  "]
        pause 750
    #ENDIF
    
    ' Fade in LED to set 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_ADC
        PAUSE 100
    
        If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn
            compVal = ADCInVal
            GOSUB Map_ADCInVal_to_PWM_Duty
            gosub ChngLEDBrightness
    
            #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:
    '	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	 
    '		DataW.LowByte=ADRESL
            DataW = ADRESH           ' Read variable from ADC and save
    		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
    
    
    '    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 Vref to duty range ************************************
    Map_ADCInVal_to_PWM_Duty:
    '   Arduino Map function to emulate:
    '   ===============================
    '   map(value, fromLow, fromHigh, toLow, toHigh)
    
    '   long map(long x, long in_min, long in_max, long out_min, long out_max)
    '   {
    '     return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    '   }
    
    '   TODO: If switching from onboard trim pot to ext trim pot and there isn't one
    '         one connected to the 3-pin connector, may need to make in_min greater 
    '         than 0
    
        LEDBrVal = (compVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty
    '    LEDBrVal = (compVal ** 39322) + MinDuty  ' Same as 100 + VrefInValue * 0.600006 but likely faster than 100+VrefInValue*6/10
        
    '    #IFDEF USE_LCD_FOR_DEBUG
    '        HSEROUT [LCD_INST, LCD_CLR]
    '        pause 5
    '        HSEROUT ["compVal=",DEC compVal,"  ",LCD_INST, LCD_L2]
    '        pause 500
    '        HSEROUT ["MotorDuty=",DEC MotorDuty,"  "]
    '    #ENDIF
    
    RETURN
    
    '*********** Set duty registers ****************************************
    ChngLEDBrightness:
        CCP1CON.4 = LEDBrVal.0
        CCP1CON.5 = LEDBrVal.1
        CCPR1L    = LEDBrVal >> 2
        
    RETURN

  3. #3
    Join Date
    Aug 2011
    Posts
    453


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    The problem is with this statement:
    Code:
    LEDBrVal = (compVal - 0) * (MaxDuty - MinDuty)/(MaxADCVal - 0) + MinDuty
    If compVal = 255 then that evaluates to:
    LEDBrVal = 255 * 412/255 + 100

    255 * 412 = 105060, which is larger than a word (which has a max of 65535)
    105060 is $19a64, so the upper portion gets tossed out and you're left with $9a64 = 39524

    LEDBrVal = 39524/255 + 100
    LEDBrVal = 154 + 100
    LEDBrVal = 254

  4. #4


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    That explains it, thanks very much. When I do the math by hand the 255's cancel each other out and then it's just 412+100.

    If I put brackets around the dividing part will that fix it?

  5. #5
    Join Date
    Aug 2011
    Posts
    453


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    If I put brackets around the dividing part will that fix it?
    That'll truncate the division, leaving you with 412/255 = 1

    You can use PBPL mode so that it uses LONGs, which should work but it'll be larger and slower if that matters. Otherwise your best bet might be to turn the equation into a lookup table.
    To be honest, I don't use PBP much, so maybe others can suggest something else.

  6. #6


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    Thanks again tumbleweed. I can't use PBPL without moving up to a 18F chip, and the minimum # of pins is then 18 (I only need two outputs so an 8-pin chip is ideal. Also, the PCB is pretty cramped now as it is).

    But a lookup table could be tricky given the 0-255 range of the 8-bit ADC input but a PWM duty cycle range of 512. Maybe I should switch to 10-bit ADC and just divide by 2?

  7. #7
    Join Date
    Mar 2003
    Location
    Commerce Michigan USA
    Posts
    1,166


    Did you find this post helpful? Yes | No

    Default Re: Weird ADC / Math Results with 9-Bit PWM

    Why not look at using the DIV32 command?
    Dave Purola,
    N8NTA
    EN82fn

Similar Threads

  1. Weird PWM Behaviour on 16F1825
    By RossWaddell in forum mel PIC BASIC Pro
    Replies: 14
    Last Post: - 26th October 2012, 21:59
  2. Averaging 16 bit values without using 32 bit math
    By sirvo in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 5th October 2007, 22:18
  3. Strangw results when using PWM command
    By malc-c in forum mel PIC BASIC Pro
    Replies: 20
    Last Post: - 10th July 2006, 12:14
  4. Strange Results in Math
    By CocaColaKid in forum mel PIC BASIC Pro
    Replies: 6
    Last Post: - 31st August 2005, 07:09
  5. PBP 16-bit ADC result math
    By sonic in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2005, 14:21

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