blob: d966e6a104764805596beaa306a3eadacce70f05 [file] [log] [blame]
Brian Silverman8d3816a2017-07-03 18:52:15 -07001#include "motors/motor.h"
2
3#include <limits.h>
Brian Silverman19ea60f2018-01-03 21:43:15 -08004#include <stdio.h>
5#include <inttypes.h>
Brian Silverman8d3816a2017-07-03 18:52:15 -07006
7#include <array>
8
9#include "motors/peripheral/configuration.h"
Brian Silverman8d3816a2017-07-03 18:52:15 -070010#include "motors/peripheral/can.h"
11
Brian Silverman19ea60f2018-01-03 21:43:15 -080012extern "C" float analog_ratio(uint16_t reading);
13
Brian Silverman8d3816a2017-07-03 18:52:15 -070014namespace frc971 {
15namespace salsa {
Brian Silverman8d3816a2017-07-03 18:52:15 -070016
Brian Silverman19ea60f2018-01-03 21:43:15 -080017Motor::Motor(BigFTM *pwm_ftm, LittleFTM *encoder_ftm, MotorControls *controls,
18 const ::std::array<volatile uint32_t *, 3> &output_registers)
19 : pwm_ftm_(pwm_ftm),
20 encoder_ftm_(encoder_ftm),
21 controls_(controls),
22 output_registers_(output_registers) {
Brian Silverman8d3816a2017-07-03 18:52:15 -070023 // PWMSYNC doesn't matter because we set SYNCMODE down below.
24 pwm_ftm_->MODE = FTM_MODE_WPDIS;
25 pwm_ftm_->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
26 encoder_ftm_->MODE = FTM_MODE_WPDIS;
27 encoder_ftm_->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
28
29 pwm_ftm_->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
30 encoder_ftm_->SC =
31 FTM_SC_CLKS(1) /* Use the system clock (not sure it matters) */ |
32 FTM_SC_PS(0) /* Don't prescale the clock (not sure it matters) */;
Brian Silverman19ea60f2018-01-03 21:43:15 -080033}
Brian Silverman8d3816a2017-07-03 18:52:15 -070034
Brian Silverman19ea60f2018-01-03 21:43:15 -080035static_assert((BUS_CLOCK_FREQUENCY % SWITCHING_FREQUENCY) == 0,
36 "Switching frequency needs to divide the bus clock frequency");
37
38static_assert(BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY < UINT16_MAX,
39 "Need to prescale");
40
41void Motor::Init() {
Brian Silverman8d3816a2017-07-03 18:52:15 -070042 pwm_ftm_->CNTIN = encoder_ftm_->CNTIN = 0;
43 pwm_ftm_->CNT = encoder_ftm_->CNT = 0;
44
Brian Silverman19ea60f2018-01-03 21:43:15 -080045 pwm_ftm_->MOD = counts_per_cycle() - 1;
46 encoder_ftm_->MOD = controls_->mechanical_counts_per_revolution() - 1;
Brian Silverman8d3816a2017-07-03 18:52:15 -070047
48 // I think you have to set this to something other than 0 for the quadrature
49 // encoder mode to actually work? This is "input capture on rising edge only",
50 // which should be fine.
51 encoder_ftm_->C0SC = FTM_CSC_ELSA;
52 encoder_ftm_->C1SC = FTM_CSC_ELSA;
53
54 // Initialize all the channels to 0.
Brian Silverman8d3816a2017-07-03 18:52:15 -070055 pwm_ftm_->OUTINIT = 0;
56
Brian Silverman19ea60f2018-01-03 21:43:15 -080057 // All of the channels are active high (low-side ones with separate high/low
58 // drives are defaulted on elsewhere).
Brian Silverman8d3816a2017-07-03 18:52:15 -070059 pwm_ftm_->POL = 0;
60
61 encoder_ftm_->FILTER = FTM_FILTER_CH0FVAL(0) /* No filter */ |
62 FTM_FILTER_CH1FVAL(0) /* No filter */;
63
64 // Could set PHAFLTREN and PHBFLTREN here to enable the filters.
65 encoder_ftm_->QDCTRL = FTM_QDCTRL_QUADEN;
66
67 pwm_ftm_->SYNCONF =
68 FTM_SYNCONF_HWWRBUF /* Hardware trigger flushes switching points */ |
69 FTM_SYNCONF_SWWRBUF /* Software trigger flushes switching points */ |
70 FTM_SYNCONF_SWRSTCNT /* Software trigger resets the count */ |
71 FTM_SYNCONF_SYNCMODE /* Use the new synchronization mode */;
72 encoder_ftm_->SYNCONF =
73 FTM_SYNCONF_SWWRBUF /* Software trigger flushes MOD */ |
74 FTM_SYNCONF_SWRSTCNT /* Software trigger resets the count */ |
75 FTM_SYNCONF_SYNCMODE /* Use the new synchronization mode */;
76
77 // Don't want any intermediate loading points.
78 pwm_ftm_->PWMLOAD = 0;
79
80 // This has to happen after messing with SYNCONF, and should happen after
81 // messing with various other things so the values can get flushed out of the
82 // buffers.
83 pwm_ftm_->SYNC =
84 FTM_SYNC_SWSYNC /* Flush everything out right now */ |
85 FTM_SYNC_CNTMAX /* Load new values at the end of the cycle */;
86 encoder_ftm_->SYNC = FTM_SYNC_SWSYNC /* Flush everything out right now */;
87
88 // Wait for the software synchronization to finish.
89 while (pwm_ftm_->SYNC & FTM_SYNC_SWSYNC) {
90 }
91 while (encoder_ftm_->SYNC & FTM_SYNC_SWSYNC) {
92 }
93}
94
Brian Silverman8d3816a2017-07-03 18:52:15 -070095void Motor::Start() {
96 pwm_ftm_->SC = FTM_SC_TOIE /* Interrupt on overflow */ |
97 FTM_SC_CLKS(1) /* Use the system clock */ |
98 FTM_SC_PS(0) /* Don't prescale the clock */;
99 pwm_ftm_->MODE &= ~FTM_MODE_WPDIS;
100 encoder_ftm_->MODE &= ~FTM_MODE_WPDIS;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700101}
102
Brian Silverman19ea60f2018-01-03 21:43:15 -0800103#define USE_ABSOLUTE_CUTOFF 0
104#define DO_CONTROLS 1
105#define DO_FIXED_PULSE 0
Brian Silverman8d3816a2017-07-03 18:52:15 -0700106
Brian Silverman19ea60f2018-01-03 21:43:15 -0800107#define USE_CUTOFF 1
108#define PRINT_READINGS 0
109#define PRINT_ALL_READINGS 0
110#define TAKE_SAMPLE 0
111#define SAMPLE_UNTIL_DONE 0
112#define DO_STEP_RESPONSE 0
113#define DO_PULSE_SWEEP 0
114#define PRINT_TIMING 0
Brian Silverman8d3816a2017-07-03 18:52:15 -0700115
Brian Silverman19ea60f2018-01-03 21:43:15 -0800116void Motor::HandleInterrupt(const BalancedReadings &balanced,
117 uint32_t captured_wrapped_encoder) {
118 pwm_ftm_->SC &= ~FTM_SC_TOF;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700119
Brian Silverman19ea60f2018-01-03 21:43:15 -0800120#if PRINT_TIMING
121 const uint32_t start_nanos = nanos();
Brian Silverman8d3816a2017-07-03 18:52:15 -0700122#endif
123
Brian Silverman19ea60f2018-01-03 21:43:15 -0800124 if (!time_after(time_add(last_current_set_time_, 100000), micros())) {
125 goal_current_ = 0;
126 }
127
Brian Silverman8d3816a2017-07-03 18:52:15 -0700128#if DO_CONTROLS
Brian Silverman8d3816a2017-07-03 18:52:15 -0700129 const ::std::array<uint32_t, 3> switching_points =
Brian Silverman19ea60f2018-01-03 21:43:15 -0800130 controls_->DoIteration(balanced.readings, captured_wrapped_encoder, goal_current_);
131#if USE_CUTOFF
132 //constexpr uint32_t kMax = 2945;
133 constexpr uint32_t kMax = 1445;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700134 static bool done = false;
135 bool done_now = false;
136 if (switching_points[0] > kMax || switching_points[1] > kMax ||
137 switching_points[2] > kMax) {
138 done_now = true;
139 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700140#if USE_ABSOLUTE_CUTOFF
141 static unsigned int current_done_count = 0;
142 bool current_done = false;
143 for (int phase = 0; phase < 3; ++phase) {
144 const float scaled_reading =
Brian Silverman19ea60f2018-01-03 21:43:15 -0800145 controls_->scale_current_reading(balanced.readings[phase]);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700146 static constexpr float kMaxBalancedCurrent = 190.0f;
147 if (scaled_reading > kMaxBalancedCurrent ||
148 scaled_reading < -kMaxBalancedCurrent) {
149 current_done = true;
150 }
151 }
152 if (current_done) {
153 if (current_done_count > 5) {
154 done_now = true;
155 }
156 ++current_done_count;
157 } else {
158 current_done_count = 0;
159 }
160#endif
161 if (done_now && !done) {
162 printf("done now\n");
163 printf("switching_points %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
164 switching_points[0], switching_points[1], switching_points[2]);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700165 printf("balanced %" PRIu16 " %" PRIu16 " %" PRIu16 "\n",
166 static_cast<uint16_t>(balanced.readings[0]),
167 static_cast<uint16_t>(balanced.readings[1]),
168 static_cast<uint16_t>(balanced.readings[2]));
169 done = true;
170 }
171 if (!done) {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800172#else
173 if (true) {
174#endif
175 output_registers_[0][0] = CalculateOnTime(switching_points[0]);
176 output_registers_[0][2] = CalculateOffTime(switching_points[0]);
177 output_registers_[1][0] = CalculateOnTime(switching_points[1]);
178 output_registers_[1][2] = CalculateOffTime(switching_points[1]);
179 output_registers_[2][0] = CalculateOnTime(switching_points[2]);
180 output_registers_[2][2] = CalculateOffTime(switching_points[2]);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700181 flip_time_offset_ = !flip_time_offset_;
182 } else {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800183 output_registers_[0][0] = 0;
184 output_registers_[0][2] = 0;
185 output_registers_[1][0] = 0;
186 output_registers_[1][2] = 0;
187 output_registers_[2][0] = 0;
188 output_registers_[2][2] = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700189 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700190#elif DO_FIXED_PULSE
Brian Silverman19ea60f2018-01-03 21:43:15 -0800191 // Step through all the phases one by one in a loop. This should slowly move
192 // the trigger.
193 // If we fire phase 1, we should go to 0 radians.
194 // If we fire phase 2, we should go to -2.0 * PI / 3.0 radians.
195 // If we fire phase 3, we should go to 2.0 * PI / 3.0 radians.
196 // These numbers were confirmed by the python motor simulation.
197 static int phase_to_fire_count = -500000;
198 static int phase_to_fire = 0;
199 ++phase_to_fire_count;
200 if (phase_to_fire_count > 200000) {
201 phase_to_fire_count = 0;
202 ++phase_to_fire;
203 if (phase_to_fire > 2) {
204 phase_to_fire = 0;
205 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700206 }
Brian Silverman19ea60f2018-01-03 21:43:15 -0800207
208 // An on-width of 60 with 30V in means about 50A through the motor and about
209 // 30W total power dumped by the motor for the big one.
210 // For the small one, an on-width of 120/3000 with 14V in means about 2A
211 // through the motor.
212 constexpr int kPhaseFireWidth = 60;
213 output_registers_[0][0] = 0;
214 output_registers_[0][2] = phase_to_fire == 0 ? kPhaseFireWidth : 0;
215 output_registers_[1][0] = 0;
216 output_registers_[1][2] = phase_to_fire == 1 ? kPhaseFireWidth : 0;
217 output_registers_[2][0] = 0;
218 output_registers_[2][2] = phase_to_fire == 2 ? kPhaseFireWidth : 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700219#endif
220
Brian Silverman8d3816a2017-07-03 18:52:15 -0700221#if PRINT_READINGS
222 static int i = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800223 if (i == 1000) {
Brian Silverman8d3816a2017-07-03 18:52:15 -0700224 i = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800225 SmallInitReadings readings;
226 {
227 DisableInterrupts disable_interrupts;
228 readings = AdcReadSmallInit(disable_interrupts);
229 }
230 printf(
231 "enc %d %d %d\n", captured_wrapped_encoder,
232 static_cast<int>((1.0f - analog_ratio(readings.motor0_abs)) * 7000.0f),
233 static_cast<int>(captured_wrapped_encoder * 7.0f / 4096.0f * 1000.0f));
234 } else if (i == 200) {
235#ifdef DO_CONTROLS
236 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", switching_points[0],
237 switching_points[1], switching_points[2]);
238#else
239 printf("out %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", output_registers_[0][2],
240 output_registers_[1][2], output_registers_[2][2]);
241#endif
242 //printf("0=%f\n", (double)balanced.readings[0]);
243 } else if (i == 400) {
244 //printf("1=%f\n", (double)balanced.readings[1]);
245 } else if (i == 600) {
246 //printf("2=%f\n", (double)balanced.readings[2]);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700247 } else {
248 //printf("%" PRIu32 " to %" PRIu32 "\n", start_count, end_count);
249 }
250 ++i;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700251#elif PRINT_ALL_READINGS
252 printf("ref=%" PRIu16 " 0.0=%" PRIu16 " 1.0=%" PRIu16 " 2.0=%" PRIu16
253 " in=%" PRIu16 " 0.1=%" PRIu16 " 1.1=%" PRIu16 " 2.1=%" PRIu16 "\n",
254 adc_readings.motor_current_ref, adc_readings.motor_currents[0][0],
255 adc_readings.motor_currents[1][0], adc_readings.motor_currents[2][0],
256 adc_readings.input_voltage, adc_readings.motor_currents[0][1],
257 adc_readings.motor_currents[1][1], adc_readings.motor_currents[2][1]);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700258#elif TAKE_SAMPLE
259#if 0
260 constexpr int kStartupWait = 50000;
261#elif 0
262 constexpr int kStartupWait = 0;
263#elif 0
264 constexpr int kStartupWait = 30000;
265#elif 1
266 constexpr int kStartupWait = 2 * 20000;
267#endif
268 constexpr int kSubsampling = 1;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800269 //constexpr int kPoints = 5000;
270 constexpr int kPoints = 1000;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700271 constexpr int kSamplingEnd = kStartupWait + kPoints * kSubsampling;
272 (void)kSamplingEnd;
273 static int j = 0;
274 static int16_t data[kPoints][11];
275 static int written = 0;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800276 static bool done_writing = false;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700277 static_assert((kStartupWait % kSubsampling) == 0, "foo");
278 static_assert((kPoints % kSubsampling) == 0, "foo");
279 if (j < kStartupWait) {
280 // Wait to be started up.
281 ++j;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800282#if SAMPLE_UNTIL_DONE
Brian Silverman8d3816a2017-07-03 18:52:15 -0700283 } else if (!done) {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800284#else
285 } else if (j < kSamplingEnd && (j % kSubsampling) == 0) {
Brian Silverman8d3816a2017-07-03 18:52:15 -0700286#endif
287 {
288 const int index = ((j - kStartupWait) / kSubsampling) % kPoints;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800289 auto &point = data[index];
Brian Silverman8d3816a2017-07-03 18:52:15 -0700290#if 0
291 point[0] = adc_readings.motor_currents[0][0];
292 point[1] = adc_readings.motor_currents[1][0];
293 point[2] = adc_readings.motor_currents[2][0];
294 point[3] = adc_readings.motor_currents[0][1];
295 point[4] = adc_readings.motor_currents[1][1];
296 point[5] = adc_readings.motor_currents[2][1];
297#else
Brian Silverman19ea60f2018-01-03 21:43:15 -0800298 point[0] = balanced.readings[0];
299 point[1] = balanced.readings[1];
300 point[2] = balanced.readings[2];
Brian Silverman8d3816a2017-07-03 18:52:15 -0700301#if 1
302 point[3] = controls_->Debug(0);
303 point[4] = controls_->Debug(1);
304 point[5] = controls_->Debug(2);
305 point[6] = controls_->Debug(3);
306 point[7] = controls_->Debug(4);
307 point[8] = controls_->Debug(5);
308 point[9] = controls_->Debug(6);
309 point[10] = controls_->Debug(7);
310#else
Brian Silverman19ea60f2018-01-03 21:43:15 -0800311#if 0
Brian Silverman8d3816a2017-07-03 18:52:15 -0700312 point[3] = adc_readings.motor_currents[0][0];
313 point[4] = adc_readings.motor_currents[1][0];
314 point[5] = adc_readings.motor_currents[2][0];
315 point[6] = adc_readings.motor_currents[0][1];
316 point[7] = adc_readings.motor_currents[1][1];
317 point[8] = adc_readings.motor_currents[2][1];
Brian Silverman19ea60f2018-01-03 21:43:15 -0800318#else
319 point[3] = 0;
320 point[4] = 0;
321 point[5] = 0;
322 point[6] = 0;
323 point[7] = 0;
324 point[8] = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700325#endif
Brian Silverman19ea60f2018-01-03 21:43:15 -0800326 point[9] = pwm_ftm_->C2V;
327 point[10] = pwm_ftm_->C3V;
328#endif
Brian Silverman8d3816a2017-07-03 18:52:15 -0700329#if 0
330 point[3] = pwm_ftm_->C1V - pwm_ftm_->C0V;
331 point[4] = pwm_ftm_->C3V - pwm_ftm_->C2V;
332 point[5] = pwm_ftm_->C5V - pwm_ftm_->C4V;
333#endif
334#endif
Brian Silverman19ea60f2018-01-03 21:43:15 -0800335 point[9] = captured_wrapped_encoder;
336 SmallInitReadings readings;
337 {
338 DisableInterrupts disable_interrupts;
339 readings = AdcReadSmallInit(disable_interrupts);
340 }
341 point[10] = readings.motor0_abs;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700342 }
343
Brian Silverman8d3816a2017-07-03 18:52:15 -0700344#if DO_STEP_RESPONSE
345 // Step response
Brian Silverman19ea60f2018-01-03 21:43:15 -0800346 if (j > kStartupWait + 200 / kSubsampling) {
347 pwm_ftm_->C3V = 240;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700348 }
Brian Silverman8d3816a2017-07-03 18:52:15 -0700349#elif DO_PULSE_SWEEP
350 // Sweep the pulse through the ADC sampling points.
351 static constexpr int kMax = 2500;
352 static constexpr int kExtraWait = 1500;
353 if (j > kStartupWait && j < kStartupWait + kExtraWait) {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800354 pwm_ftm_->C2V = 0;
355 pwm_ftm_->C3V = 240;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700356 } else if (j < kStartupWait + kMax + kExtraWait) {
357 uint32_t start = j - kStartupWait - kExtraWait;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800358 pwm_ftm_->C2V = start;
359 pwm_ftm_->C3V = start + 240;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700360 } else {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800361 pwm_ftm_->C2V = 0;
362 pwm_ftm_->C3V = 0;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700363 }
364#endif
365
366 ++j;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800367#if SAMPLE_UNTIL_DONE
368 } else if (false) {
369#else
Brian Silverman8d3816a2017-07-03 18:52:15 -0700370 } else if (j < kSamplingEnd) {
371 ++j;
372 } else if (j == kSamplingEnd) {
Brian Silverman8d3816a2017-07-03 18:52:15 -0700373#endif
374 printf("finished\n");
375 ++j;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800376#if SAMPLE_UNTIL_DONE
Brian Silverman8d3816a2017-07-03 18:52:15 -0700377 } else if (done) {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800378#else
379 } else {
Brian Silverman8d3816a2017-07-03 18:52:15 -0700380#endif
381 // Time to write the data out.
Brian Silverman19ea60f2018-01-03 21:43:15 -0800382 if (written < (int)sizeof(data) && debug_tty_ != nullptr) {
Brian Silverman8d3816a2017-07-03 18:52:15 -0700383 int to_write = sizeof(data) - written;
Brian Silverman19ea60f2018-01-03 21:43:15 -0800384 if (to_write > 64) {
385 to_write = 64;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700386 }
Brian Silverman19ea60f2018-01-03 21:43:15 -0800387 int result = debug_tty_->Write(((const char *)data) + written, to_write);
Brian Silverman8d3816a2017-07-03 18:52:15 -0700388 if (result >= 0) {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800389 written += result;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700390 } else {
Brian Silverman19ea60f2018-01-03 21:43:15 -0800391 printf("error\n");
Brian Silverman8d3816a2017-07-03 18:52:15 -0700392 }
Brian Silverman19ea60f2018-01-03 21:43:15 -0800393 }
394 if (!done_writing && written >= (int)sizeof(data) &&
395 debug_tty_->write_queue_empty()) {
396 printf("done writing %d\n", written);
397 done_writing = true;
Brian Silverman8d3816a2017-07-03 18:52:15 -0700398 }
399 }
400#endif
Brian Silverman8d3816a2017-07-03 18:52:15 -0700401 (void)balanced;
402
403 // Tell the hardware to use the new switching points.
Brian Silverman19ea60f2018-01-03 21:43:15 -0800404 // TODO(Brian): Somehow verify that we consistently hit the first or second
405 // timer-cycle with the new values (if there's two).
Brian Silverman8d3816a2017-07-03 18:52:15 -0700406 pwm_ftm_->PWMLOAD = FTM_PWMLOAD_LDOK;
407
Brian Silverman19ea60f2018-01-03 21:43:15 -0800408#if PRINT_TIMING
409 static int print_timing_count = 0;
410 static uint32_t print_timing_total = 0;
411 print_timing_total += time_subtract(nanos(), start_nanos);
412 if (++print_timing_count == 1000) {
413 printf("took %" PRIu32 "/%d\n", print_timing_total, print_timing_count);
414 print_timing_count = 0;
415 print_timing_total = 0;
416 }
417#endif
418
Brian Silverman8d3816a2017-07-03 18:52:15 -0700419 // If another cycle has already started, turn the light on right now.
420 if (pwm_ftm_->SC & FTM_SC_TOF) {
421 GPIOC_PSOR = 1 << 5;
422 }
423}
424
Brian Silverman19ea60f2018-01-03 21:43:15 -0800425uint32_t Motor::CalculateOnTime(uint32_t width) const {
426 if (width > 0) {
427 width += deadtime_compensation_;
428 if (flip_time_offset_) {
429 width += 1;
430 }
431 }
432 return (counts_per_cycle() - width) / 2;
433}
434
435uint32_t Motor::CalculateOffTime(uint32_t width) const {
436 if (width > 0) {
437 width += deadtime_compensation_;
438 if (!flip_time_offset_) {
439 width += 1;
440 }
441 }
442 return (counts_per_cycle() + width) / 2;
443}
444
Brian Silverman8d3816a2017-07-03 18:52:15 -0700445} // namespace salsa
446} // namespace frc971