board/teensy40: Add ADC support (#1458)

machine/teensy40: add ADC support
Этот коммит содержится в:
ardnew 2022-04-13 10:12:31 -05:00 коммит произвёл GitHub
родитель f3afc7bbf3
коммит 2dc46a851b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23

Просмотреть файл

@ -883,3 +883,179 @@ func (p Pin) getMuxMode(config PinConfig) uint32 {
panic("machine: invalid pin mode")
}
}
// maximum ADC value for the currently configured resolution (used for scaling)
var adcMaximum uint32
// InitADC is not used by this machine. Use `(ADC).Configure()`.
func InitADC() {}
// Configure initializes the receiver's ADC peripheral and pin for analog input.
func (a ADC) Configure(config ADCConfig) {
// if not specified, use defaults: 10-bit resolution, 4 samples/conversion
const (
defaultResolution = uint32(10)
defaultSamples = uint32(4)
)
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
resolution, samples := config.Resolution, config.Samples
if 0 == resolution {
resolution = defaultResolution
}
if 0 == samples {
samples = defaultSamples
}
if resolution > 12 {
resolution = 12 // maximum resolution of 12 bits
}
adcMaximum = (uint32(1) << resolution) - 1
mode, average := a.mode(resolution, samples)
nxp.ADC1.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC1
nxp.ADC2.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC2
// begin calibration
nxp.ADC1.GC.Set(average | nxp.ADC_GC_CAL)
nxp.ADC2.GC.Set(average | nxp.ADC_GC_CAL)
for a.isCalibrating() {
} // wait for calibration
}
// Get performs a single ADC conversion, returning a 16-bit unsigned integer.
// The value returned will be scaled (uniformly distributed) if necessary so
// that it is always in the range [0..65535], regardless of the ADC's configured
// bit size (resolution).
func (a ADC) Get() uint16 {
if ch1, ch2, ok := a.Pin.getADCChannel(); ok {
for a.isCalibrating() {
} // wait for calibration
var val uint32
if noADCChannel != ch1 {
nxp.ADC1.HC0.Set(uint32(ch1))
for !nxp.ADC1.HS.HasBits(nxp.ADC_HS_COCO0) {
}
val = nxp.ADC1.R0.Get() & 0xFFFF
} else {
nxp.ADC2.HC0.Set(uint32(ch2))
for !nxp.ADC2.HS.HasBits(nxp.ADC_HS_COCO0) {
}
val = nxp.ADC2.R0.Get() & 0xFFFF
}
// should never be zero, but just in case, use UINT16_MAX so that the scalar
// gets factored out of the conversion result, leaving the original reading
// to be returned unaltered/unscaled.
if adcMaximum == 0 {
adcMaximum = 0xFFFF
}
// scale up to a 16-bit value
return uint16((val * 0xFFFF) / adcMaximum)
}
return 0
}
// mode constructs bit masks for mode and average - used in ADC configuration
// registers - from a given ADC bit size (resolution) and sample count.
func (a ADC) mode(resolution, samples uint32) (mode, average uint32) {
// use asynchronous clock (ADACK) (0 = IPG, 1 = IPG/2, or 3 = ADACK)
mode = (nxp.ADC_CFG_ADICLK_ADICLK_3 << nxp.ADC_CFG_ADICLK_Pos) & nxp.ADC_CFG_ADICLK_Msk
// input clock DIV2 (0 = DIV1, 1 = DIV2, 2 = DIV4, or 3 = DIV8)
mode |= (nxp.ADC_CFG_ADIV_ADIV_1 << nxp.ADC_CFG_ADIV_Pos) & nxp.ADC_CFG_ADIV_Msk
switch resolution {
case 8: // 8-bit conversion, sample period (ADC clocks) = 8
mode |= (nxp.ADC_CFG_MODE_MODE_0 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
case 12: // 12-bit conversion, sample period (ADC clocks) = 24
mode |= (nxp.ADC_CFG_MODE_MODE_2 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
mode |= nxp.ADC_CFG_ADLSMP
default: // 10-bit conversion, sample period (ADC clocks) = 20
mode |= (nxp.ADC_CFG_MODE_MODE_1 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_2 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
mode |= nxp.ADC_CFG_ADLSMP
}
if samples >= 4 {
if samples >= 32 {
// 32 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_3 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else if samples >= 16 {
// 16 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_2 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else if samples >= 8 {
// 8 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_1 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else {
// 4 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_0 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
}
average = nxp.ADC_GC_AVGE
}
return mode, average
}
// isCalibrating returns true if and only if either one (or both) of ADC1 and
// ADC2 have their calibrating flags set. ADC reads must wait until these flags
// are clear before attempting a conversion.
func (a ADC) isCalibrating() bool {
return nxp.ADC1.GC.HasBits(nxp.ADC_GC_CAL) || nxp.ADC2.GC.HasBits(nxp.ADC_GC_CAL)
}
const noADCChannel = uint8(0xFF)
// getADCChannel returns the input channel for ADC1/ADC2 of the receiver Pin p.
func (p Pin) getADCChannel() (adc1, adc2 uint8, ok bool) {
switch p {
case PA12: // [AD_B0_12]: ADC1_IN1 ~
return 1, noADCChannel, true
case PA13: // [AD_B0_13]: ADC1_IN2 ~
return 2, noADCChannel, true
case PA14: // [AD_B0_14]: ADC1_IN3 ~
return 3, noADCChannel, true
case PA15: // [AD_B0_15]: ADC1_IN4 ~
return 4, noADCChannel, true
case PA16: // [AD_B1_00]: ADC1_IN5 ADC2_IN5
return 5, 5, true
case PA17: // [AD_B1_01]: ADC1_IN6 ADC2_IN6
return 6, 6, true
case PA18: // [AD_B1_02]: ADC1_IN7 ADC2_IN7
return 7, 7, true
case PA19: // [AD_B1_03]: ADC1_IN8 ADC2_IN8
return 8, 8, true
case PA20: // [AD_B1_04]: ADC1_IN9 ADC2_IN9
return 9, 9, true
case PA21: // [AD_B1_05]: ADC1_IN10 ADC2_IN10
return 10, 10, true
case PA22: // [AD_B1_06]: ADC1_IN11 ADC2_IN11
return 11, 11, true
case PA23: // [AD_B1_07]: ADC1_IN12 ADC2_IN12
return 12, 12, true
case PA24: // [AD_B1_08]: ADC1_IN13 ADC2_IN13
return 13, 13, true
case PA25: // [AD_B1_09]: ADC1_IN14 ADC2_IN14
return 14, 14, true
case PA26: // [AD_B1_10]: ADC1_IN15 ADC2_IN15
return 15, 15, true
case PA27: // [AD_B1_11]: ADC1_IN0 ADC2_IN0
return 16, 16, true
case PA28: // [AD_B1_12]: ~ ADC2_IN1
return noADCChannel, 1, true
case PA29: // [AD_B1_13]: ~ ADC2_IN2
return noADCChannel, 2, true
case PA30: // [AD_B1_14]: ~ ADC2_IN3
return noADCChannel, 3, true
case PA31: // [AD_B1_15]: ~ ADC2_IN4
return noADCChannel, 4, true
default:
return noADCChannel, noADCChannel, false
}
}