blob: 6cd07424831bc800cb759a458a1223951f61db0b [file] [log] [blame]
#ifndef MOTORS_PERIPHERAL_ADC_DMA_H_
#define MOTORS_PERIPHERAL_ADC_DMA_H_
#include <array>
#include <stdint.h>
#include "motors/core/kinetis.h"
#include "motors/peripheral/configuration.h"
#include "motors/util.h"
namespace frc971 {
namespace teensy {
// This class manages configuring the hardware to automatically capture various
// sensor values periodically with tight timing. Currently it's only 4 samples
// on each of ADC0 and ADC1.
// TODO(Brian): Capture the encoder value (and velocity?) too.
//
// Uses:
// * Both PDBs
// * Both ADCs
// * ADC_RESULT_DMA_CHANNEL and ADC_RECONFIGURE_DMA_CHANNEL
class AdcDmaSampler {
public:
static constexpr int kNumberAdcSamples = 4;
// Corresponding samples in adc0_samples and adc1_samples happen
// simultaneously. Second sample happens at the cycle boundary. Elements
// should include the appropriate ADCH and DIFF values.
AdcDmaSampler(int counts_per_cycle);
// Must be called before Initialize().
void set_adc0_samples(
const ::std::array<uint32_t, kNumberAdcSamples> &adc0_samples);
void set_adc1_samples(
const ::std::array<uint32_t, kNumberAdcSamples> &adc1_samples);
// pdb_input is the trigger-in index for PDB0 to use the attached FTM. This
// FTM must be set to do trigger-outs on exactly two of its channels, which
// will be dedicated to this object. This FTM must also start counting at 0 at
// the beginning of each cycle and have MOD set to match one cycle time.
void set_pdb_input(int pdb_input) { pdb_input_ = pdb_input; }
// ftm_delays is pointers to the two FTM.SCn registers which are set up to do
// trigger-outs.
void set_ftm_delays(const ::std::array<volatile uint32_t *, 2> &ftm_delays) {
ftm_delays_ = ftm_delays;
}
AdcDmaSampler(const AdcDmaSampler &) = delete;
AdcDmaSampler &operator=(const AdcDmaSampler &) = delete;
void Initialize();
// Checks if the current cycle has finished reading out results.
// After this returns true, it is safe to read the results until Reset() is
// called.
bool CheckDone() {
const uint32_t mask = 1 << reconfigure_dma_channel(1);
if (!(DMA.INT & mask)) {
return false;
}
DmaMemoryBarrier();
DMA.INT = mask;
(void)DMA.INT;
return true;
}
// Must be called after CheckDone() returns true each time.
void Reset();
int16_t adc_result(int adc, int i) { return adc_results_[adc][i]; }
private:
void InitializePdbChannel(KINETIS_PDB_CHANNEL_t *channel);
static constexpr int result_dma_channel(int adc) {
if (adc == 0) {
return ADC_RESULT_DMA_CHANNEL0;
}
return ADC_RESULT_DMA_CHANNEL1;
}
static KINETIS_TCD_t *result_dma(int adc) {
return &DMA.TCD[result_dma_channel(adc)];
}
static constexpr int reconfigure_dma_channel(int adc) {
if (adc == 0) {
return ADC_RECONFIGURE_DMA_CHANNEL0;
}
return ADC_RECONFIGURE_DMA_CHANNEL1;
}
static KINETIS_TCD_t *reconfigure_dma(int adc) {
return &DMA.TCD[reconfigure_dma_channel(adc)];
}
static constexpr int encoder_value_dma_channel() {
return ENCODER_VALUE_DMA_CHANNEL;
}
const int counts_per_cycle_;
::std::array<::std::array<volatile uint32_t, kNumberAdcSamples + 2>, 2>
adc_sc1s_{};
::std::array<::std::array<volatile uint16_t, kNumberAdcSamples>, 2>
adc_results_{};
int pdb_input_ = 0xF;
::std::array<volatile uint32_t *, 2> ftm_delays_{nullptr, nullptr};
};
} // namespace teensy
} // namespace frc971
#endif // MOTORS_PERIPHERAL_ADC_DMA_H_