blob: 6cd07424831bc800cb759a458a1223951f61db0b [file] [log] [blame]
Brian Silvermancabadaf2018-09-03 19:36:44 -07001#ifndef MOTORS_PERIPHERAL_ADC_DMA_H_
2#define MOTORS_PERIPHERAL_ADC_DMA_H_
3
4#include <array>
5
6#include <stdint.h>
7
8#include "motors/core/kinetis.h"
9#include "motors/peripheral/configuration.h"
10#include "motors/util.h"
11
12namespace frc971 {
13namespace teensy {
14
15// This class manages configuring the hardware to automatically capture various
16// sensor values periodically with tight timing. Currently it's only 4 samples
17// on each of ADC0 and ADC1.
18// TODO(Brian): Capture the encoder value (and velocity?) too.
19//
20// Uses:
21// * Both PDBs
22// * Both ADCs
23// * ADC_RESULT_DMA_CHANNEL and ADC_RECONFIGURE_DMA_CHANNEL
24class AdcDmaSampler {
25 public:
26 static constexpr int kNumberAdcSamples = 4;
27
28 // Corresponding samples in adc0_samples and adc1_samples happen
29 // simultaneously. Second sample happens at the cycle boundary. Elements
30 // should include the appropriate ADCH and DIFF values.
Brian Silvermana1d84822018-09-15 17:18:49 -070031 AdcDmaSampler(int counts_per_cycle);
Brian Silvermancabadaf2018-09-03 19:36:44 -070032
33 // Must be called before Initialize().
34 void set_adc0_samples(
35 const ::std::array<uint32_t, kNumberAdcSamples> &adc0_samples);
36 void set_adc1_samples(
37 const ::std::array<uint32_t, kNumberAdcSamples> &adc1_samples);
38 // pdb_input is the trigger-in index for PDB0 to use the attached FTM. This
39 // FTM must be set to do trigger-outs on exactly two of its channels, which
40 // will be dedicated to this object. This FTM must also start counting at 0 at
41 // the beginning of each cycle and have MOD set to match one cycle time.
42 void set_pdb_input(int pdb_input) { pdb_input_ = pdb_input; }
43 // ftm_delays is pointers to the two FTM.SCn registers which are set up to do
44 // trigger-outs.
45 void set_ftm_delays(const ::std::array<volatile uint32_t *, 2> &ftm_delays) {
46 ftm_delays_ = ftm_delays;
47 }
48
49 AdcDmaSampler(const AdcDmaSampler &) = delete;
50 AdcDmaSampler &operator=(const AdcDmaSampler &) = delete;
51
52 void Initialize();
53
54 // Checks if the current cycle has finished reading out results.
55 // After this returns true, it is safe to read the results until Reset() is
56 // called.
57 bool CheckDone() {
58 const uint32_t mask = 1 << reconfigure_dma_channel(1);
59 if (!(DMA.INT & mask)) {
60 return false;
61 }
62 DmaMemoryBarrier();
63 DMA.INT = mask;
64 (void)DMA.INT;
65 return true;
66 }
67
68 // Must be called after CheckDone() returns true each time.
69 void Reset();
70
71 int16_t adc_result(int adc, int i) { return adc_results_[adc][i]; }
72
73 private:
74 void InitializePdbChannel(KINETIS_PDB_CHANNEL_t *channel);
75
76 static constexpr int result_dma_channel(int adc) {
77 if (adc == 0) {
78 return ADC_RESULT_DMA_CHANNEL0;
79 }
80 return ADC_RESULT_DMA_CHANNEL1;
81 }
82 static KINETIS_TCD_t *result_dma(int adc) {
83 return &DMA.TCD[result_dma_channel(adc)];
84 }
85
86 static constexpr int reconfigure_dma_channel(int adc) {
87 if (adc == 0) {
88 return ADC_RECONFIGURE_DMA_CHANNEL0;
89 }
90 return ADC_RECONFIGURE_DMA_CHANNEL1;
91 }
92 static KINETIS_TCD_t *reconfigure_dma(int adc) {
93 return &DMA.TCD[reconfigure_dma_channel(adc)];
94 }
95
96 static constexpr int encoder_value_dma_channel() {
97 return ENCODER_VALUE_DMA_CHANNEL;
98 }
99
Brian Silvermana1d84822018-09-15 17:18:49 -0700100 const int counts_per_cycle_;
101
Brian Silvermancabadaf2018-09-03 19:36:44 -0700102 ::std::array<::std::array<volatile uint32_t, kNumberAdcSamples + 2>, 2>
103 adc_sc1s_{};
104 ::std::array<::std::array<volatile uint16_t, kNumberAdcSamples>, 2>
105 adc_results_{};
106
107 int pdb_input_ = 0xF;
108 ::std::array<volatile uint32_t *, 2> ftm_delays_{nullptr, nullptr};
109};
110
111} // namespace teensy
112} // namespace frc971
113
114#endif // MOTORS_PERIPHERAL_ADC_DMA_H_