ADCC - adc module for K and Q families

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
Jerry Messina
Swordfish Developer
Posts: 1473
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

ADCC - adc module for K and Q families

Post by Jerry Messina » Thu Feb 16, 2023 2:32 pm

Here's a new module for the ADC in the K and Q devices with an ADCC peripheral,
which is used on the K40, K42, K83, and the entire Q family.

Currently the module just supports basic operation of the ADC (no filtering, math, etc)

Code: Select all

//
//------------------------------------------------------------------------------
// Name    : ADCC.bas
// Author  : Jerry Messina
// Date    : 13 FEB 2023
// Version : 1.00
//
// ADCC/ADC2 analog-to-digital converter with computation peripheral
// used by K40, K42, K83, Q10, Q40, Q41, Q43, Q71, Q83, and Q84
//
// ver 1.00 13 FEB 2023
//  - initial version with basic legacy mode support
//------------------------------------------------------------------------------
//
module ADCC

// check for _adc2 peripheral
#if not defined(_adc2) or (_adc2 = 0)
  #error _device + " does not support the ADCC/ADC2 peripheral"
#endif

// determine adcc peripheral version
#if _device in (18F24K40, 18F25K40, 18F26K40, 18F27K40) or _
    _device in (18F45K40, 18F46K40, 18F47K40) or _
    _device in (18F65K40, 18F66K40, 18F67K40) or _
    _device in (18LF24K40, 18LF25K40, 18LF26K40, 18LF27K40) or _
    _device in (18LF45K40, 18LF46K40, 18LF47K40) or _
    _device in (18LF65K40, 18LF66K40, 18LF67K40) or _
    _device in (18F24Q10, 18F25Q10, 18F26Q10, 18F27Q10) or _
    _device in (18F44Q10, 18F45Q10, 18F46Q10, 18F47Q10)
  #option _ADCC_VERSION = 1
#elseif _device in (18F25K83, 18F26K83, 18LF25K83, 18LF26K83)
  #option _ADCC_VERSION = 2
#else
  #option _ADCC_VERSION = 0
#endif
public const ADCC_VERSION = _ADCC_VERSION

// justification 0=left, 1=right (default)
#option _ADCC_JUSTIFY = 1
#if not(_ADCC_JUSTIFY in (0, 1))
  #error _ADCC_JUSTIFY, "_ADCC_JUSTIFY invalid"
#endif
public const ADCC_JUSTIFY = _ADCC_JUSTIFY

// software acq time, in usec
// set to 0 to use hardware ADACQ register and call SetAcq() 
#option _ADCC_ACQ_TIME = 10
public const ADCC_ACQ_TIME = _ADCC_ACQ_TIME

// hdw ADACQ register size
#if (_ADCC_VERSION = 1)            // K40 or Q10
  #define _ADCC_ADACQ_SIZE = byte
  public type ADACQ_T = byte
#else
  #define _ADCC_ADACQ_SIZE = word
  public type ADACQ_T = word
#endif

// automatically discharge the sample cap in SetChan
//  = 0: do not discharge
//  > 0: discharge to VSS and wait n usecs
// see 'ADC channel selection' discussion below
#option _ADCC_DISCHARGE_CAP = 0     // in usecs (0=disable)
#if not(_ADCC_DISCHARGE_CAP in (0 to 255))
  #error _ADCC_DISCHARGE_CAP, "_ADCC_DISCHARGE time invalid"
#endif
public const ADCC_DISCHARGE_CAP = _ADCC_DISCHARGE_CAP  

// ADCC VSS input channel (analog GND)
#if (_ADCC_VERSION = 1) or (_ADCC_VERSION = 2)    // K40, Q10, or K83
  #option _ADCC_VSS_CHANNEL = %111100
#else
  #option _ADCC_VSS_CHANNEL = %111011
#endif
public const ADCC_VSS_CHANNEL = _ADCC_VSS_CHANNEL

// ADCON0 bits
public const
    ADON = 7,        // ADC enable, 0=ADC disabled, 1=ADC enabled
    ADCONT = 6,      // continuous operation, 0=single, 1=cont/retrigger
    ADCS = 4,        // clock select, 0=Fosc(uses ADCLK), 1=FRC
    ADFM = 2,        // justification, 0=left justified, 1=right justified
    ADGO = 0         // conversion status, 0=done, 1=start/in process
#if (_ADCC_JUSTIFY = 1)
  const ADCC_DEFAULT_ADCON0 = (1<<ADON) + (1<<ADCS) + (1<<ADFM)
#else
  const ADCC_DEFAULT_ADCON0 = (1<<ADON) + (1<<ADCS)
#endif

// ADREF constants
public const
    ADPREF_VDD = %00,       // bits 1 and 0
    ADPREF_EXT = %10,
    ADPREF_FVR = %11,
    ADNREF_VSS = 0<<4,      // bit 4
    ADNREF_EXT = 1<<4
// Vref = Vdd & Vss
const ADCC_DEFAULT_ADREF = ADPREF_VDD + ADNREF_VSS
    
// adc result register alias   
public dim
    wADRES as ADRESL.AsWord,            // 16 bit ADC result
    wADPREV as ADPREVL.AsWord           // previous result (not currently used)
    
// ADC channel selection (from the datasheet)
// The ADPCH register determines which channel is connected to the Sample-and-Hold circuit for conversion. 
// When switching channels, it is recommended to have some acquisition time (ADACQ register) before starting the next
// conversion.
// To reduce the chance of measurement error, it is recommended to discharge the Sample-and-Hold capacitor
// when switching between ADC channels by starting a conversion on a channel connected to VSS
// and terminating the conversion after the acquisition time has elapsed.
//
public inline sub dischargeSampleCap()
    ADPCH = ADCC_VSS_CHANNEL        // ADC input = VSS
    delayus(ADCC_DISCHARGE_CAP)     // wait, in usecs
end sub

public sub SetChan(chan as byte)
  #if (_ADCC_DISCHARGE_CAP > 0)
    dischargeSampleCap()
  #endif
    ADPCH = chan                // select channel
  #if (_ADCC_ACQ_TIME > 0)      // use software acq time
    delayus(ADCC_ACQ_TIME)      // wait acq time
  #endif
end sub

// convert the currently selected channel
// result in ADRESH:ADRESL
public function Read() as wADRES
    ADCON0.bits(ADGO) = 1

    while (ADCON0.bits(ADGO) = 1)
    end while
    
    // adc result (ADRESH:ADRESL)
    result = wADRES
end function

// set chan and convert
// result in ADRESH:ADRESL
public function Read(chan as byte) as wADRES 
    SetChan(chan)
    result = Read()
end function

// set ADACQ acq time and ADCLK (requires ADCON0.ADCS=0)
public sub SetAcq(pADACQ as ADACQ_T, pADCLK as byte = 0)
  #if (_ADCC_ADACQ_SIZE = byte)
    ADACQ = pADACQ
  #elseif (_ADCC_ADACQ_SIZE = word)
    ADACQL = pADACQ.bytes(0)
    ADACQH = pADACQ.bytes(1)
  #endif

    ADCLK = pADCLK
end sub
    
// init the ADCC settings
public sub Init(pADCON0 as byte=ADCC_DEFAULT_ADCON0, pADREF as byte=ADCC_DEFAULT_ADREF)
    ADCON1 = $00        // precharge=0, single sample
    ADCON2 = $00        // legacy mode, no filtering, ADRES->ADPREV
    ADCON3 = $00        // no math functions
    ADREF = pADREF      // ADC Reference Select
    ADCAP = 0           // default S&H capacitance
    ADRPT = 0           // no repeat measurements
    ADCNT = 0           // repeat count = 0
    ADACT = 0           // auto-conversion disabled
    SetAcq(0, 0)        // default to software acq time, ADRC clock

    ADCON0 = pADCON0    // ADC Control Register 0 (enable, cont mode, clock select, justification, go)
    
    dischargeSampleCap()
end sub

end module
example:

Code: Select all

device = 18F26Q43
clock = 64

include "intosc.bas"

include "adcc.bas"

dim w as word
dim i as byte

// initialize the ADCC 
ADCC.Init()

// read chan 1
w = ADCC.Read(1)

// avg four readings from chan 2
ADCC.SetChan(2)
w = 0
for i = 1 to 4
    w = w + ADCC.Read()
next 
w = w/4
This module will be included in the next update, which should be available soon.

Post Reply