ADC and Interrupt on Change


Closed Thread
Results 1 to 3 of 3
  1. #1

    Default ADC and Interrupt on Change

    I currently have a project which checks for changes on a trim pot as part of the main loop (the trim pot/ADC value then controls the brightness of a LED). It samples 16 readings and then sorts them and takes the average of the middle 8. This works well for getting a pretty stable ADC value but I'm wondering if I can move this to an interrupt-on-change interrupt routine to free up the main loop to do other stuff (that might include PAUSE). Does it make sense to use Darrel's DT_INTS-14.bas & IOC_INT with ADC or would it just be firing way too often?

  2. #2
    Join Date
    May 2013
    Location
    australia
    Posts
    2,389


    Did you find this post helpful? Yes | No

    Default Re: ADC and Interrupt on Change

    Does it make sense to use Darrel's DT_INTS-14.bas & IOC_INT with ADC or would it just be firing way too often?
    no IOC is change of logic level ie 0 to 1 or 1 to 0 not change in adc value

    if your adc readings fluctuate that much are your adc settings appropriate ?

    why not quantise your adc readings to a finite range of pwm values

    see these examples about int driven adc measurements
    http://www.picbasic.co.uk/forum/showthread.php?t=21072
    Warning I'm not a teacher

  3. #3


    Did you find this post helpful? Yes | No

    Default Re: ADC and Interrupt on Change

    Quote Originally Posted by richard View Post
    no IOC is change of logic level ie 0 to 1 or 1 to 0 not change in adc value

    if your adc readings fluctuate that much are your adc settings appropriate ?

    why not quantise your adc readings to a finite range of pwm values

    see these examples about int driven adc measurements
    http://www.picbasic.co.uk/forum/showthread.php?t=21072
    Thanks richard - that's what I suspected. The reason I wanted to move the ADC check to an IOC_INT was I had code in the Main loop to pulsate a LED which had PAUSEs in it which meant that you could change the trim pot but it would be a while before it read it (and hence appear jerky). So, instead I'm now using DT's ELAPSED_INT.bas and checking the 'Seconds' variable to decide if I want to pulsate the LED. This seems to work much better with pretty good response time in terms of reading the changed trim pot value.

    BTW, I am scaling the ads readings to a range of PWM steps and then using a 'gamma correction' table to set the actual PWM value:

    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 (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
    
    ' ***************************************************************
    ' Includes
    ' ***************************************************************
    
    INCLUDE "DT_INTS-14.bas"    ' Base Interrupt System
    INCLUDE "ReEnterPBP.bas"    ' Include if using PBP interrupts
    INCLUDE "Elapsed_INT.bas"   ' Contains subs for doing elapsed time
                                ' --> copy files to PBP main folder 
                                ' (i.e. c:\pbp3)
    
    ASM
    INT_LIST  macro    ; IntSource,        Label,  Type, ResetFlag?
            INT_Handler   TMR1_INT,  _ClockCount,   PBP,  yes
        endm
        INT_CREATE            ; Creates the interrupt processor
    
        INT_ENABLE  TMR1_INT  ; Enable Timer 1 Interrupts  
    ENDASM
    
    ' LED Fade Linearization
    ' ======================
    ' Increasing the duty cycle by discrete integer amounts leads to a logarithmic
    ' increase in brightness; the LED appears to quickly reach maximum brightness
    ' long before it reaches the max duty cycle value. To make the fade in look more
    ' linear (like an incandescent bulb), the data table below uses 'gamma correction'
     ' to genterate appropriate duty cycle values.
     
    ' Array size = 100
    ' Total PWM steps: 512
    ' Gamma correction: 0.75 (try values in the range of 0.5 - 2.0)
    ' # of values per line: 15
    
    '-----[The data table]--------------------------------------------------------
    DataWord       VAR WORD
    numSteps       CON 100
    DataTable      CON EXT
    GOTO OverData                ; Make sure data doesn't try to execute
    ASM
    DataTable
      DW   0,  1,  1,  2,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4
      DW   5,  5,  5,  6,  6,  7,  7,  8,  8,  9,  9, 10, 11, 12, 12
      DW  13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 26, 27, 29, 31
      DW  32, 34, 36, 38, 41, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70
      DW  74, 78, 82, 87, 91, 96,101,107,112,118,124,131,138,145,152
      DW 160,168,177,186,196,205,216,227,238,250,263,276,289,304,319
      DW 334,351,368,386,404,424,445,466,488,511
    endasm
    OverData:
    '-----[Continue with code]----------------------------------------------------
    
    ' ***************************************************************
    ' 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 = 512; must match max PWM steps from data
                                ; table above
                                
    PWMDuty       VAR WORD      ; Duty cycle variable for PWM
    
    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
    
    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
    
    LEDBrIndx     VAR WORD
    
    i             VAR WORD
    PulseLimit    VAR WORD
    LEDPulse      VAR BIT       ; 1=fully on; 0= start pulsating effect
    
    LCDPause      CON 5         ; Pause for LCD display
    FadeInPause   VAR BYTE      ; Pause during LED fade in
    
    #IFDEF USE_LCD_FOR_DEBUG
        FadeInPause = 12
    #ELSE
        FadeInPause = 40 + LCDPause  ; was 15 + LCDPause
    #ENDIF
    
    #IFDEF USE_LCD_FOR_DEBUG
        pause 1000
        HSEROUT [LCD_INST, LCD_CLR, "LCD Init"]
        pause LCDPause
        HSEROUT [LCD_INST, LCD_L2, "  SerLCD_V2_5"]
        pause 2000
    #ENDIF
    
    ' ************************************************
    ' Get Vref from ADC to set brightness step index
    ' ************************************************ 
    GOSUB Do_ADC
    compVal = ADCInVal          ; set initial compare value
    GOSUB Map_ADCInVal_to_PWM_Steps
    
    #IFDEF USE_LCD_FOR_DEBUG
        HSEROUT [LCD_INST, LCD_CLR]
        pause LCDPause
        HSEROUT ["LEDBrIndx=",DEC LEDBrIndx,"  "]
        pause 750
    #ELSE
        PAUSE 500               ; should match motor spin up
    #ENDIF
    
    ' Fade in LED to set brightness
    FOR i = 0 to (LEDBrIndx  - 1)      ; max is 'numSteps' from data table above
        ReadCODE  (DataTable + i), DataWord
        PWMDuty = DataWord
    
        GOSUB ChngLEDBrightness 
             
        #IFDEF USE_LCD_FOR_DEBUG
            HSEROUT [LCD_INST, LCD_CLR]
            pause LCDPause
            HSEROUT ["PWMDuty=",DEC PWMDuty,"  "]
        #ENDIF
       
        pause FadeInPause       ; should match motor spin up
    NEXT i
    
    LEDPulse = 1               ' set initial value
    Gosub ResetTime            ' Reset Time to  0d-00:00:00.00
    Gosub StartTimer           ' Start the Elapsed Timer
    
    Main:
    '   Check trim pot value
        GOSUB Do_ADC
    '    PAUSE 100
    
    '   If trim pot value has changed, update LED brightness level
        If (compVal > (ADCInVal + 1)) or (compVal < (ADCInVal - 1)) THEn
            compVal = ADCInVal
            GOSUB Map_ADCInVal_to_PWM_Steps
    
            ReadCODE  (DataTable + (LEDBrIndx - 1)), DataWord
            PWMDuty = DataWord
    
            gosub ChngLEDBrightness
    
            #IFDEF USE_LCD_FOR_DEBUG
                HSEROUT [LCD_INST, LCD_CLR]
                pause LCDPause
                HSEROUT ["new LEDBrIndx", LCD_INST, LCD_L2, DEC LEDBrIndx, "  "]
            #ENDIF
    
            Gosub ResetTime           ' Reset Time to  0d-00:00:00.00
        ENDIF
    
    '    PulseLimit = 3*(LEDBrIndx/4) ' 75%
        PulseLimit = LEDBrIndx/2     ' 50%
    '    PulseLimit = LEDBrIndx/4     ' 25%
    
    '   Pulse LEDs
        if (Seconds = 2) AND (LEDPulse = 1) then  
        '   Fade down LEDs to a preset minimum    
            FOR i = (LEDBrIndx - 1) to PulseLimit STEP -1
                ReadCODE  (DataTable + i), DataWord
                PWMDuty = DataWord
            
                GOSUB ChngLEDBrightness 
                     
                #IFDEF USE_LCD_FOR_DEBUG
                    HSEROUT [LCD_INST, LCD_CLR]
                    pause LCDPause
                    HSEROUT ["PWMDuty=",DEC PWMDuty,"  "]
                #ENDIF
               
                pause 25
            NEXT i
    
            LEDPulse = 0
            Gosub ResetTime           ' Reset Time to  0d-00:00:00.00
           'LCDout $FE,$C0, dec Days,"d-",dec2 Hours,":",dec2 Minutes,":",dec2 Seconds
        ELSEIF (Ticks = 25) and (LEDPulse = 0) THEN
        '   Fade up LEDs back to trim pot setting    
            FOR i = PulseLimit to (LEDBrIndx  - 1)
                ReadCODE  (DataTable + i), DataWord
                PWMDuty = DataWord
            
                GOSUB ChngLEDBrightness 
                     
                #IFDEF USE_LCD_FOR_DEBUG
                    HSEROUT [LCD_INST, LCD_CLR]
                    pause LCDPause
                    HSEROUT ["PWMDuty=",DEC PWMDuty,"  "]
                #ENDIF
               
                pause 25
            NEXT i 
    
            LEDPulse = 1
            Gosub ResetTime           ' Reset Time to  0d-00:00:00.00
           'LCDout $FE,$C0, dec Days,"d-",dec2 Hours,":",dec2 Minutes,":",dec2 Seconds
        endif    
    
    GOTO Main
    
    Do_ADC:
    '   Function to 'average out' changing ADC values when the trim pot swiper arm
    '   isn't moving to get a steady value
    
    '	Stuff 16 Element WORD Array full of ADC values
    '	----------------------------------------------
    	For CounterA = 0 to (16 - 1)
            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 < (16 - 1) 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
    
    return
    
    '*********** Map Vref to PWM step **************************************
    Map_ADCInVal_to_PWM_Steps:
    '   Map ADC value to the range of accetable PWM steps, as the two ranges may
    '   not be the same( e.g. ADC: 0-255, PWM: 50-100)
    
    '   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;
    '   }
    
    '   Need to split up the operations to get full value using DIV32
    '   50% PWM step range
        'LEDBrIndx = ((compVal - 0) * (numSteps - numSteps/2))
        'LEDBrIndx = DIV32 (MaxADCVal - 0) + numSteps/2
    
    '   90% PWM step range
        LEDBrIndx = ((compVal - 0) * (numSteps - numSteps/10))
        LEDBrIndx = DIV32 (MaxADCVal - 0) + numSteps/10
    
    RETURN
    
    '*********** Set duty registers ****************************************
    ChngLEDBrightness:
        CCP1CON.4 = PWMDuty.0
        CCP1CON.5 = PWMDuty.1
        CCPR1L    = PWMDuty >> 2
        
    RETURN
    Last edited by RossWaddell; - 26th June 2016 at 00:39.

Similar Threads

  1. DT-INt and Interrupt on Change
    By Luckyborg in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 24th April 2013, 21:23
  2. RB Change Interrupt understanding
    By PickyBiker in forum mel PIC BASIC Pro
    Replies: 3
    Last Post: - 13th April 2010, 14:48
  3. Interrupt-on-Change-pin!
    By PICante in forum mel PIC BASIC Pro
    Replies: 7
    Last Post: - 11th February 2009, 20:22
  4. Change On Interrupt, PIC16F884
    By elec_mech in forum mel PIC BASIC Pro
    Replies: 17
    Last Post: - 14th November 2008, 17:25
  5. Interrupt On Change - question
    By kevj in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 12th July 2008, 23:20

Members who have read this thread : 1

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