Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
AltAnalogIn/AltAnalogIn_KL25Z.cpp
- Committer:
- arnoz
- Date:
- 2021-10-01
- Revision:
- 116:7a67265d7c19
- Parent:
- 100:1ff35c07217c
File content as of revision 116:7a67265d7c19:
#if defined(TARGET_KLXX) || defined(TARGET_K20D50M) #include "AltAnalogIn.h" #include "clk_freqs.h" #ifdef TARGET_K20D50M static const PinMap PinMap_ADC[] = { {PTC2, ADC0_SE4b, 0}, {PTD1, ADC0_SE5b, 0}, {PTD5, ADC0_SE6b, 0}, {PTD6, ADC0_SE7b, 0}, {PTB0, ADC0_SE8, 0}, {PTB1, ADC0_SE9, 0}, {PTB2, ADC0_SE12, 0}, {PTB3, ADC0_SE13, 0}, {PTC0, ADC0_SE14, 0}, {PTC1, ADC0_SE15, 0}, {NC, NC, 0} }; #endif // statics int AltAnalogIn::lastMux = -1; uint32_t AltAnalogIn::lastId = 0; AltAnalogIn::AltAnalogIn(PinName pin, bool continuous, int long_sample_clocks, int averaging, int sample_bits) { // set our unique ID static uint32_t nextID = 1; id = nextID++; // presume no DMA or interrupts dma = 0; sc1_aien = 0; // do nothing if explicitly not connected if (pin == NC) return; // validate the sample bit size, and figure the ADC_xxBIT code for it uint32_t adc_xxbit = ADC_8BIT; switch (sample_bits) { case 8: adc_xxbit = ADC_8BIT; break; case 10: adc_xxbit = ADC_10BIT; break; case 12: adc_xxbit = ADC_12BIT; break; case 16: adc_xxbit = ADC_16BIT; break; default: error("invalid sample size for AltAnalogIn - must be 8, 10, 12, or 16 bits"); } // validate the long sample mode uint32_t cfg1_adlsmp = ADC_CFG1_ADLSMP; uint32_t cfg2_adlsts = ADC_CFG2_ADLSTS(3); switch (long_sample_clocks) { case 0: // disable long sample mode cfg1_adlsmp = 0; cfg2_adlsts = ADC_CFG2_ADLSTS(3); break; case 6: cfg1_adlsmp = ADC_CFG1_ADLSMP; // enable long sample mode cfg2_adlsts = ADC_CFG2_ADLSTS(3); // Long sample time mode 3 -> 6 ADCK cycles total break; case 10: cfg1_adlsmp = ADC_CFG1_ADLSMP; // enable long sample mode cfg2_adlsts = ADC_CFG2_ADLSTS(2); // Long sample time mode 2 -> 10 ADCK cycles total break; case 16: cfg1_adlsmp = ADC_CFG1_ADLSMP; // enable long sample mode cfg2_adlsts = ADC_CFG2_ADLSTS(1); // Long sample time mode 1 -> 16 ADCK cycles total break; case 24: cfg1_adlsmp = ADC_CFG1_ADLSMP; // enable long sample mode cfg2_adlsts = ADC_CFG2_ADLSTS(0); // Long sample time mode 0 -> 24 ADCK cycles total break; default: error("invalid long sample mode clock count - must be 0 (disabled), 6, 10, 16, or 24"); } // figure the averaging bits uint32_t sc3_avg = 0; switch (averaging) { case 0: case 1: // 0/1 = no averaging sc3_avg = 0; break; case 4: sc3_avg = ADC_SC3_AVGE | ADC_SC3_AVGS_4; break; case 8: sc3_avg = ADC_SC3_AVGE | ADC_SC3_AVGS_8; break; case 16: sc3_avg = ADC_SC3_AVGE | ADC_SC3_AVGS_16; break; case 32: sc3_avg = ADC_SC3_AVGE | ADC_SC3_AVGS_32; break; default: error("invalid ADC averaging count: must be 1, 4, 8, 16, or 32"); } // figure our ADC number ADCnumber = (ADCName)pinmap_peripheral(pin, PinMap_ADC); if (ADCnumber == (ADCName)NC) { error("ADC pin mapping failed"); } // figure our multiplexer channel (A or B) ADCmux = (ADCnumber >> CHANNELS_A_SHIFT) ^ 1; // enable the ADC0 clock in the system control module SIM->SCGC6 |= SIM_SCGC6_ADC0_MASK; // enable the port clock gate for the port containing our GPIO pin uint32_t port = (uint32_t)pin >> PORT_SHIFT; SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTA_SHIFT + port); // Figure the maximum clock frequency. In 12-bit mode or less, we can // run the ADC at up to 18 MHz per the KL25Z data sheet. (16-bit mode // is limited to 12 MHz.) int clkdiv = 0; uint32_t adcfreq = bus_frequency(); uint32_t maxfreq = sample_bits <= 12 ? MAX_FADC_12BIT : MAX_FADC_16BIT; for ( ; adcfreq > maxfreq ; adcfreq /= 2, clkdiv += 1) ; // The "high speed configuration" bit is required if the ADC clock // frequency is above a certain threshold. The actual threshold is // poorly documented: the reference manual only says that it's required // when running the ADC at "high speed" but doesn't define how high // "high" is. The only numerical figure I can find is in the Freescale // ADC sample time calculator tool (a Windows program downloadable from // the Freescale site), which has a little notation on the checkbox for // the ADHSC bit that says to use it when the ADC clock is 8 MHz or // higher. // // Note that this bit is somewhat confusingly named. It doesn't mean // "make the ADC go faster". It actually means just the opposite. // What it really means is that the external clock is running so fast // that the ADC has to pad out its sample time slightly to compensate, // by adding a couple of extra clock cycles to each sampling interval. const uint32_t ADHSC_SPEED_LIMIT = 8000000; uint32_t adhsc_bit = (adcfreq >= ADHSC_SPEED_LIMIT ? ADC_CFG2_ADHSC_MASK : 0); // map the GPIO pin in the system multiplexer to the ADC pinmap_pinout(pin, PinMap_ADC); // set up the ADC control registers - these are common to all users of this class ADC0->CFG1 = ADC_CFG1_ADIV(clkdiv) // Clock Divide Select (as calculated above) | cfg1_adlsmp // Long sample time | ADC_CFG1_MODE(adc_xxbit) // Sample precision | ADC_CFG1_ADICLK(0); // Input Clock = bus clock ADC0->CFG2 = adhsc_bit // High-Speed Configuration, if needed | cfg2_adlsts; // long sample time mode // Figure our SC1 register bits sc1 = ADC_SC1_ADCH(ADCnumber & ~(1 << CHANNELS_A_SHIFT)) | sc1_aien; // figure our SC2 register bits sc2 = ADC_SC2_REFSEL(0); // Default Voltage Reference // Set our SC3 bits. The defaults (0 bits) are calibration mode off, // single sample, averaging disabled. sc3 = (continuous ? ADC_SC3_CONTINUOUS : 0) // enable continuous mode if desired | sc3_avg; // sample averaging mode bits } void AltAnalogIn::calibrate() { // Select our channel to set up the MUX and SC2/SC3 registers. This // will set up the clock source and sample time we'll use to take // actual samples. selectChannel(); // Make sure DMA is disabled on the channel, so that we can see COCO. // Also make sure that software triggering is in effect. ADC0->SC2 &= ~(ADC_SC2_DMAEN | ADC_SC2_ADTRG); // clear any past calibration results ADC0->SC3 |= ADC_SC3_CALF; // select 32X averaging mode for highest accuracy, and begin calibration ADC0->SC3 = (sc3 & ~ADC_SC3_AVGS_MASK) | ADC_SC3_AVGS_32 | ADC_SC3_CAL; // Wait for calibration to finish, but not more than 10ms, just in // case something goes wrong in the setup. Timer t; t.start(); uint32_t t0 = t.read_us(); while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) == 0 && static_cast<uint32_t>(t.read_us() - t0) < 10000) ; // debugging // printf("ADC calibration %s, run time %u us\r\n", // (ADC0->SC3 & ADC_SC3_CALF) != 0 ? "error" : "ok", // static_cast<uint32_t>(t.read_us() - t0)); // Check results if ((ADC0->SC3 & ADC_SC3_CALF) == 0) { // Success - calculate the plus-side calibration results and store // in the PG register. (This procedure is from reference manual.) uint16_t sum = 0; sum += ADC0->CLP0; sum += ADC0->CLP1; sum += ADC0->CLP2; sum += ADC0->CLP3; sum += ADC0->CLP4; sum += ADC0->CLPS; sum /= 2; sum |= 0x8000; ADC0->PG = sum; // do the same for the minus-side results sum = 0; sum += ADC0->CLM0; sum += ADC0->CLM1; sum += ADC0->CLM2; sum += ADC0->CLM3; sum += ADC0->CLM4; sum += ADC0->CLMS; sum /= 2; sum |= 0x8000; ADC0->MG = sum; } // Clear any error (this is one of those perverse cases where we clear // a bit in a peripheral by writing 1 to the bit) ADC0->SC3 |= ADC_SC3_CALF; // restore our normal SC2 and SC3 settings ADC0->SC2 = sc2; ADC0->SC3 = sc3; // un-select the channel so that we reset all registers next time unselectChannel(); } void AltAnalogIn::enableInterrupts() { sc1_aien = ADC_SC1_AIEN; sc1 |= ADC_SC1_AIEN; } void AltAnalogIn::initDMA(SimpleDMA *dma) { // remember the DMA interface object this->dma = dma; // set to read from the ADC result register dma->source(&ADC0->R[0], false, 8); // set to trigger on the ADC dma->trigger(Trigger_ADC0); // enable DMA in our SC2 bits sc2 |= ADC_SC2_DMAEN; } void AltAnalogIn::setTriggerTPM(int tpmUnitNumber) { // select my channel selectChannel(); // set the hardware trigger for the ADC to the specified TPM unit SIM->SOPT7 = ADC0ALTTRGEN | ADC0TRGSEL_TPM(tpmUnitNumber); // set the ADC to hardware trigger mode ADC0->SC2 = sc2 | ADC_SC2_ADTRG; // set SC1a and SC1b ADC0->SC1[0] = sc1; ADC0->SC1[1] = sc1; } #endif //defined TARGET_KLXX