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