blob: 9591331d200225abf8c790ee129b8386681b9a65 [file] [log] [blame]
James Kuszmaul998d3032018-09-08 15:41:41 -07001#include <inttypes.h>
2#include <stdio.h>
3
4#include <atomic>
5
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "motors/core/kinetis.h"
James Kuszmaul998d3032018-09-08 15:41:41 -07007#include "motors/core/time.h"
8#include "motors/fet12/current_equalization.h"
9#include "motors/fet12/motor_controls.h"
10#include "motors/motor.h"
11#include "motors/peripheral/adc.h"
James Kuszmaul7c8aad62018-09-08 18:16:18 -070012#include "motors/peripheral/adc_dma.h"
James Kuszmaul998d3032018-09-08 15:41:41 -070013#include "motors/peripheral/can.h"
Brian Silverman4787a6e2018-10-06 16:00:54 -070014#include "motors/print/print.h"
James Kuszmaul998d3032018-09-08 15:41:41 -070015#include "motors/util.h"
James Kuszmaul998d3032018-09-08 15:41:41 -070016
Stephan Pleinesf63bde82024-01-13 15:59:33 -080017namespace frc971::motors {
James Kuszmaul998d3032018-09-08 15:41:41 -070018namespace {
19
20constexpr double Kv = 22000.0 * 2.0 * M_PI / 60.0 / 30.0 * 3.6;
21constexpr double kVcc = 31.5;
22constexpr double kIcc = 125.0;
23constexpr double kR = 0.0084;
24
25struct Fet12AdcReadings {
James Kuszmaul7c8aad62018-09-08 18:16:18 -070026 // Averages of the pairs of ADC DMA channels corresponding with each channel
27 // pair. Individual values in motor_currents correspond to current sensor
28 // values, rather than the actual currents themselves (and so they still need
29 // to be decoupled).
James Kuszmaul998d3032018-09-08 15:41:41 -070030 int16_t motor_currents[3];
31 int16_t throttle, fuse_voltage;
32};
33
34void AdcInitFet12() {
35 AdcInitCommon(AdcChannels::kB, AdcChannels::kA);
36
37 // M_CH0V ADC0_SE5b
38 PORTD_PCR1 = PORT_PCR_MUX(0);
39
40 // M_CH1V ADC0_SE7b
41 PORTD_PCR6 = PORT_PCR_MUX(0);
42
43 // M_CH2V ADC0_SE14
44 PORTC_PCR0 = PORT_PCR_MUX(0);
45
46 // M_CH0F ADC1_SE5a
47 PORTE_PCR1 = PORT_PCR_MUX(0);
48
49 // M_CH1F ADC1_SE6a
50 PORTE_PCR2 = PORT_PCR_MUX(0);
51
52 // M_CH2F ADC1_SE7a
53 PORTE_PCR3 = PORT_PCR_MUX(0);
54
55 // SENSE0 ADC0_SE23
56 // dedicated
57
58 // SENSE1 ADC0_SE13
59 PORTB_PCR3 = PORT_PCR_MUX(0);
60}
61
James Kuszmaul998d3032018-09-08 15:41:41 -070062::std::atomic<Motor *> global_motor{nullptr};
James Kuszmaul7c8aad62018-09-08 18:16:18 -070063::std::atomic<teensy::AdcDmaSampler *> global_adc_dma{nullptr};
64
James Kuszmaul998d3032018-09-08 15:41:41 -070065extern "C" {
66
James Kuszmaul998d3032018-09-08 15:41:41 -070067void *__stack_chk_guard = (void *)0x67111971;
68void __stack_chk_fail(void) {
69 while (true) {
70 GPIOC_PSOR = (1 << 5);
71 printf("Stack corruption detected\n");
72 delay(1000);
73 GPIOC_PCOR = (1 << 5);
74 delay(1000);
75 }
76}
77
James Kuszmaul998d3032018-09-08 15:41:41 -070078extern char *__brkval;
79extern uint32_t __bss_ram_start__[];
80extern uint32_t __heap_start__[];
81extern uint32_t __stack_end__[];
82
83struct DebugBuffer {
84 struct Sample {
85 ::std::array<int16_t, 3> currents;
86 ::std::array<int16_t, 3> commanded_currents;
87 ::std::array<uint16_t, 3> commands;
Brian Silverman37a95d62018-11-09 16:08:32 -080088 ::std::array<int16_t, 3> readings;
James Kuszmaul998d3032018-09-08 15:41:41 -070089 uint16_t position;
90 // Driver requested current.
91 float driver_request;
92 // Requested current.
93 int16_t total_command;
94
95 float est_omega;
96 float fuse_voltage;
97 int16_t fuse_current;
98
99 float fuse_badness;
100 uint32_t cycles_since_start;
101 };
102
103 // The amount of data in the buffer. This will never decrement. This will be
104 // transferred out the serial port after it fills up.
105 ::std::atomic<size_t> size{0};
106 ::std::atomic<uint32_t> count{0};
107 // The data.
108 ::std::array<Sample, 512> samples;
109};
110
111DebugBuffer global_debug_buffer;
112
113void ftm0_isr(void) {
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700114 static uint32_t i = 0;
115 teensy::AdcDmaSampler *const adc_dma =
116 global_adc_dma.load(::std::memory_order_relaxed);
117
James Kuszmaul998d3032018-09-08 15:41:41 -0700118 Fet12AdcReadings adc_readings;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700119 // TODO(Brian): Switch to the DMA interrupt instead of spinning.
120 while (!adc_dma->CheckDone()) {
James Kuszmaul998d3032018-09-08 15:41:41 -0700121 }
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700122
123 adc_readings.motor_currents[0] =
124 (adc_dma->adc_result(0, 0) + adc_dma->adc_result(0, 1)) / 2;
125 adc_readings.motor_currents[1] =
126 (adc_dma->adc_result(0, 2) + adc_dma->adc_result(1, 1)) / 2;
127 adc_readings.motor_currents[2] =
128 (adc_dma->adc_result(1, 0) + adc_dma->adc_result(1, 2)) / 2;
129 adc_readings.throttle = adc_dma->adc_result(0, 3);
James Kuszmaul998d3032018-09-08 15:41:41 -0700130 const ::std::array<float, 3> decoupled =
131 DecoupleCurrents(adc_readings.motor_currents);
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700132 adc_dma->Reset();
133 const uint32_t wrapped_encoder =
134 global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
Philipp Schrader790cb542023-07-05 21:06:52 -0700135 const BalancedReadings balanced = BalanceSimpleReadings(decoupled);
James Kuszmaul998d3032018-09-08 15:41:41 -0700136
James Kuszmaul521eb652018-10-17 19:09:33 -0700137#if 1
138
James Kuszmaul998d3032018-09-08 15:41:41 -0700139 static float fuse_badness = 0;
140
141 static uint32_t cycles_since_start = 0u;
142 ++cycles_since_start;
143#if 0
144 static int count = 0;
145 ++count;
146 static float currents[3] = {0.0f, 0.0f, 0.0f};
147 for (int ii = 0; ii < 3; ++ii) {
148 currents[ii] += static_cast<float>(adc_readings.motor_currents[ii]);
149 }
150
151 if (i == 0) {
152 printf(
153 "foo %d.0, %d.0, %d.0, %.3d %.3d %.3d, switching %d %d %d enc %d\n",
154 static_cast<int>(currents[0] / static_cast<float>(count)),
155 static_cast<int>(currents[1] / static_cast<float>(count)),
156 static_cast<int>(currents[2] / static_cast<float>(count)),
157 static_cast<int>(decoupled[0] * 1.0f),
158 static_cast<int>(decoupled[1] * 1.0f),
159 static_cast<int>(decoupled[2] * 1.0f),
160 global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(0),
161 global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(1),
162 global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(2),
163 static_cast<int>(
164 global_motor.load(::std::memory_order_relaxed)->wrapped_encoder()));
165 count = 0;
166 currents[0] = 0.0f;
167 currents[1] = 0.0f;
168 currents[2] = 0.0f;
169 }
170#endif
171#if 1
172 constexpr float kAlpha = 0.995f;
173 constexpr float kFuseAlpha = 0.95f;
174
175 // 3400 - 760
James Kuszmaulb7707432018-10-07 14:48:11 -0700176 // Start the throttle filter at 1.0f--once it converges to near zero, we set
177 // throttle_zeroed to true and only then do we start listening to throttle
178 // commands.
179 static float filtered_throttle = 1.0f;
180 static bool throttle_zeroed = false;
James Kuszmaul998d3032018-09-08 15:41:41 -0700181 constexpr int kMaxThrottle = 3400;
182 constexpr int kMinThrottle = 760;
183 const float throttle = ::std::max(
184 0.0f,
185 ::std::min(1.0f,
186 static_cast<float>(static_cast<int>(adc_readings.throttle) -
187 kMinThrottle) /
188 static_cast<float>(kMaxThrottle - kMinThrottle)));
189
190 // y(n) = x(n) + a * (y(n-1) - x(n))
191 filtered_throttle = throttle + kAlpha * (filtered_throttle - throttle);
James Kuszmaulb7707432018-10-07 14:48:11 -0700192 if (::std::abs(filtered_throttle) < 1e-2f) {
193 // Once the filter gets near zero once, we start paying attention to it;
194 // once it gets near zero once, never ignore it again.
195 throttle_zeroed = true;
196 }
James Kuszmaul998d3032018-09-08 15:41:41 -0700197
198 const float fuse_voltage = static_cast<float>(adc_readings.fuse_voltage);
199 static float filtered_fuse_voltage = 0.0f;
200
201 filtered_fuse_voltage =
202 fuse_voltage + kFuseAlpha * (filtered_fuse_voltage - fuse_voltage);
203
204 const float velocity =
205 global_motor.load(::std::memory_order_relaxed)->estimated_velocity();
206 const float bemf = velocity / (static_cast<float>(Kv) / 1.5f);
207 const float abs_bemf = ::std::abs(bemf);
208 constexpr float kPeakCurrent = 300.0f;
Brian Silverman37a95d62018-11-09 16:08:32 -0800209 constexpr float kLimitedCurrent = 70.0f;
James Kuszmaul998d3032018-09-08 15:41:41 -0700210 const float max_bat_cur =
211 fuse_badness > (kLimitedCurrent * kLimitedCurrent * 0.95f)
212 ? kLimitedCurrent
213 : static_cast<float>(kIcc);
214 const float throttle_limit = ::std::min(
215 kPeakCurrent,
Philipp Schrader790cb542023-07-05 21:06:52 -0700216 (-abs_bemf +
217 ::std::sqrt(static_cast<float>(
218 bemf * bemf + 4.0f * static_cast<float>(kR) * 1.5f *
219 static_cast<float>(kVcc) * max_bat_cur))) /
James Kuszmaul998d3032018-09-08 15:41:41 -0700220 (2.0f * 1.5f * static_cast<float>(kR)));
221
James Kuszmaul521eb652018-10-17 19:09:33 -0700222 constexpr float kNegativeCurrent = 100.0f;
Philipp Schrader790cb542023-07-05 21:06:52 -0700223 float goal_current = -::std::min(
224 ::std::max(filtered_throttle * (kPeakCurrent + kNegativeCurrent) -
225 kNegativeCurrent,
226 -throttle_limit),
227 throttle_limit);
James Kuszmaul998d3032018-09-08 15:41:41 -0700228
James Kuszmaulb7707432018-10-07 14:48:11 -0700229 if (!throttle_zeroed) {
230 goal_current = 0.0f;
231 }
James Kuszmaula1d94c82018-10-10 20:00:09 -0700232 // Note: current reduction is 12/70 belt, 15 / 54 on chain, and 10 inch
233 // diameter wheels, so cutoff of 500 electrical rad/sec * 1 mechanical rad / 2
234 // erad * 12 / 70 * 15 / 54 * 0.127 m = 1.5m/s = 3.4 mph
James Kuszmaul998d3032018-09-08 15:41:41 -0700235 if (velocity > -500) {
236 if (goal_current > 0.0f) {
237 goal_current = 0.0f;
238 }
239 }
James Kuszmaul521eb652018-10-17 19:09:33 -0700240
Philipp Schrader790cb542023-07-05 21:06:52 -0700241 // float goal_current =
242 //-::std::min(filtered_throttle * kPeakCurrent, throttle_limit);
James Kuszmaul521eb652018-10-17 19:09:33 -0700243 const float overall_measured_current =
244 global_motor.load(::std::memory_order_relaxed)
245 ->overall_measured_current();
James Kuszmaul998d3032018-09-08 15:41:41 -0700246 const float fuse_current =
James Kuszmaul521eb652018-10-17 19:09:33 -0700247 overall_measured_current *
248 (bemf + overall_measured_current * static_cast<float>(kR) * 1.5f) /
James Kuszmaul998d3032018-09-08 15:41:41 -0700249 static_cast<float>(kVcc);
250 const int16_t fuse_current_10 = static_cast<int16_t>(10.0f * fuse_current);
251 fuse_badness += 0.00002f * (fuse_current * fuse_current - fuse_badness);
252
Philipp Schrader790cb542023-07-05 21:06:52 -0700253 global_motor.load(::std::memory_order_relaxed)->SetGoalCurrent(goal_current);
James Kuszmaul998d3032018-09-08 15:41:41 -0700254 global_motor.load(::std::memory_order_relaxed)
Austin Schuh54c8c842019-04-07 13:54:23 -0700255 ->CurrentInterrupt(balanced, wrapped_encoder);
James Kuszmaul998d3032018-09-08 15:41:41 -0700256
257 global_debug_buffer.count.fetch_add(1);
258
James Kuszmaul521eb652018-10-17 19:09:33 -0700259 const bool trigger = false && i > 10000;
James Kuszmaul998d3032018-09-08 15:41:41 -0700260 // global_debug_buffer.count.load(::std::memory_order_relaxed) >= 0;
261 size_t buffer_size =
262 global_debug_buffer.size.load(::std::memory_order_relaxed);
263 if ((buffer_size > 0 || trigger) &&
264 buffer_size != global_debug_buffer.samples.size()) {
265 global_debug_buffer.samples[buffer_size].currents[0] =
266 static_cast<int16_t>(balanced.readings[0] * 10.0f);
267 global_debug_buffer.samples[buffer_size].currents[1] =
268 static_cast<int16_t>(balanced.readings[1] * 10.0f);
269 global_debug_buffer.samples[buffer_size].currents[2] =
270 static_cast<int16_t>(balanced.readings[2] * 10.0f);
271 global_debug_buffer.samples[buffer_size].position =
272 global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
273 global_debug_buffer.samples[buffer_size].est_omega =
274 global_motor.load(::std::memory_order_relaxed)->estimated_velocity();
275 global_debug_buffer.samples[buffer_size].commands[0] =
Philipp Schrader790cb542023-07-05 21:06:52 -0700276 global_motor.load(::std::memory_order_relaxed)
277 ->get_switching_points_cycles(0);
James Kuszmaul998d3032018-09-08 15:41:41 -0700278 global_debug_buffer.samples[buffer_size].commands[1] =
Philipp Schrader790cb542023-07-05 21:06:52 -0700279 global_motor.load(::std::memory_order_relaxed)
280 ->get_switching_points_cycles(1);
James Kuszmaul998d3032018-09-08 15:41:41 -0700281 global_debug_buffer.samples[buffer_size].commands[2] =
Philipp Schrader790cb542023-07-05 21:06:52 -0700282 global_motor.load(::std::memory_order_relaxed)
283 ->get_switching_points_cycles(2);
James Kuszmaul998d3032018-09-08 15:41:41 -0700284 global_debug_buffer.samples[buffer_size].commanded_currents[0] =
285 global_motor.load(::std::memory_order_relaxed)->i_goal(0);
286 global_debug_buffer.samples[buffer_size].commanded_currents[1] =
287 global_motor.load(::std::memory_order_relaxed)->i_goal(1);
288 global_debug_buffer.samples[buffer_size].commanded_currents[2] =
289 global_motor.load(::std::memory_order_relaxed)->i_goal(2);
290 global_debug_buffer.samples[buffer_size].total_command =
291 global_motor.load(::std::memory_order_relaxed)->goal_current();
292 global_debug_buffer.samples[buffer_size].fuse_voltage =
293 filtered_fuse_voltage;
294 global_debug_buffer.samples[buffer_size].fuse_current = fuse_current_10;
295 global_debug_buffer.samples[buffer_size].driver_request =
296 ::std::max(filtered_throttle * (kPeakCurrent + kNegativeCurrent) -
297 kNegativeCurrent,
298 0.0f);
299 global_debug_buffer.samples[buffer_size].fuse_badness = fuse_badness;
Philipp Schrader790cb542023-07-05 21:06:52 -0700300 global_debug_buffer.samples[buffer_size].cycles_since_start =
301 cycles_since_start;
James Kuszmaul998d3032018-09-08 15:41:41 -0700302
303 global_debug_buffer.size.fetch_add(1);
304 }
305
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700306 ++i;
James Kuszmaul998d3032018-09-08 15:41:41 -0700307 if (buffer_size == global_debug_buffer.samples.size()) {
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700308 i = 0;
James Kuszmaul998d3032018-09-08 15:41:41 -0700309 GPIOC_PCOR = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
310 GPIOD_PCOR = (1 << 4) | (1 << 5);
311
312 PERIPHERAL_BITBAND(GPIOC_PDDR, 1) = 1;
313 PERIPHERAL_BITBAND(GPIOC_PDDR, 2) = 1;
314 PERIPHERAL_BITBAND(GPIOC_PDDR, 3) = 1;
315 PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
316 PERIPHERAL_BITBAND(GPIOD_PDDR, 4) = 1;
317 PERIPHERAL_BITBAND(GPIOD_PDDR, 5) = 1;
318
319 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
320 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(1);
321 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
322 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
323 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
324 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
325 }
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700326#endif
327#else
James Kuszmaul521eb652018-10-17 19:09:33 -0700328 // Useful code when calculating resistance/inductance of motor
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700329 FTM0->SC &= ~FTM_SC_TOF;
330 FTM0->C0V = 0;
331 FTM0->C1V = 0;
332 FTM0->C2V = 0;
Brian Silverman37a95d62018-11-09 16:08:32 -0800333 FTM0->C3V = 20;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700334 FTM0->C4V = 0;
Brian Silverman37a95d62018-11-09 16:08:32 -0800335 FTM0->C5V = 0;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700336 FTM0->PWMLOAD = FTM_PWMLOAD_LDOK;
James Kuszmaul521eb652018-10-17 19:09:33 -0700337 (void)wrapped_encoder;
James Kuszmaul521eb652018-10-17 19:09:33 -0700338 size_t buffer_size =
339 global_debug_buffer.size.load(::std::memory_order_relaxed);
Brian Silverman37a95d62018-11-09 16:08:32 -0800340 // Setting this to true is helpful for calibrating inductance, and false is
341 // good for calibrating resistance.
342 constexpr bool start_immediately = true;
343 bool trigger = start_immediately || i > 20000;
James Kuszmaul521eb652018-10-17 19:09:33 -0700344 if ((trigger || buffer_size > 0) &&
345 buffer_size != global_debug_buffer.samples.size()) {
346 global_debug_buffer.samples[buffer_size].currents[0] =
347 static_cast<int16_t>(balanced.readings[0] * 10.0f);
348 global_debug_buffer.samples[buffer_size].currents[1] =
349 static_cast<int16_t>(balanced.readings[1] * 10.0f);
350 global_debug_buffer.samples[buffer_size].currents[2] =
351 static_cast<int16_t>(balanced.readings[2] * 10.0f);
352 global_debug_buffer.samples[buffer_size].commands[0] = FTM0->C1V;
353 global_debug_buffer.samples[buffer_size].commands[1] = FTM0->C3V;
354 global_debug_buffer.samples[buffer_size].commands[2] = FTM0->C5V;
Brian Silverman37a95d62018-11-09 16:08:32 -0800355 global_debug_buffer.samples[buffer_size].readings[0] =
356 adc_readings.motor_currents[0];
357 global_debug_buffer.samples[buffer_size].readings[1] =
358 adc_readings.motor_currents[1];
359 global_debug_buffer.samples[buffer_size].readings[2] =
360 adc_readings.motor_currents[2];
James Kuszmaul521eb652018-10-17 19:09:33 -0700361 global_debug_buffer.samples[buffer_size].position =
362 global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
363 global_debug_buffer.size.fetch_add(1);
364 }
365 if (buffer_size == global_debug_buffer.samples.size()) {
366 GPIOC_PCOR = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
367 GPIOD_PCOR = (1 << 4) | (1 << 5);
368
369 PERIPHERAL_BITBAND(GPIOC_PDDR, 1) = 1;
370 PERIPHERAL_BITBAND(GPIOC_PDDR, 2) = 1;
371 PERIPHERAL_BITBAND(GPIOC_PDDR, 3) = 1;
372 PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
373 PERIPHERAL_BITBAND(GPIOD_PDDR, 4) = 1;
374 PERIPHERAL_BITBAND(GPIOD_PDDR, 5) = 1;
375
376 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
377 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(1);
378 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
379 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
380 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
381 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
382 i = 0;
383 }
384 ++i;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700385#endif
James Kuszmaul998d3032018-09-08 15:41:41 -0700386}
387
388} // extern "C"
389
390void ConfigurePwmFtm(BigFTM *pwm_ftm) {
391 // Put them all into combine active-high mode, and all the low ones staying on
392 // all the time by default.
393 pwm_ftm->C0SC = FTM_CSC_ELSA;
394 pwm_ftm->C0V = 0;
395 pwm_ftm->C1SC = FTM_CSC_ELSA;
396 pwm_ftm->C1V = 0;
397 pwm_ftm->C2SC = FTM_CSC_ELSA;
398 pwm_ftm->C2V = 0;
399 pwm_ftm->C3SC = FTM_CSC_ELSA;
400 pwm_ftm->C3V = 0;
401 pwm_ftm->C4SC = FTM_CSC_ELSA;
402 pwm_ftm->C4V = 0;
403 pwm_ftm->C5SC = FTM_CSC_ELSA;
404 pwm_ftm->C5V = 0;
405 pwm_ftm->C6SC = FTM_CSC_ELSA;
406 pwm_ftm->C6V = 0;
407 pwm_ftm->C7SC = FTM_CSC_ELSA;
408 pwm_ftm->C7V = 0;
409
410 pwm_ftm->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ |
411 FTM_COMBINE_DTEN3 /* Enable deadtime */ |
412 FTM_COMBINE_COMP3 /* Make them complementary */ |
413 FTM_COMBINE_COMBINE3 /* Combine the channels */ |
414 FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ |
415 FTM_COMBINE_DTEN2 /* Enable deadtime */ |
416 FTM_COMBINE_COMP2 /* Make them complementary */ |
417 FTM_COMBINE_COMBINE2 /* Combine the channels */ |
418 FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ |
419 FTM_COMBINE_DTEN1 /* Enable deadtime */ |
420 FTM_COMBINE_COMP1 /* Make them complementary */ |
421 FTM_COMBINE_COMBINE1 /* Combine the channels */ |
422 FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */ |
423 FTM_COMBINE_DTEN0 /* Enable deadtime */ |
424 FTM_COMBINE_COMP0 /* Make them complementary */ |
425 FTM_COMBINE_COMBINE0 /* Combine the channels */;
426 // Safe state for all channels is low.
427 pwm_ftm->POL = 0;
428
429 // Set the deadtime.
430 pwm_ftm->DEADTIME =
431 FTM_DEADTIME_DTPS(0) /* Prescaler of 1 */ | FTM_DEADTIME_DTVAL(9);
432
433 pwm_ftm->CONF =
434 FTM_CONF_BDMMOD(1) /* Set everything to POLn during debug halt */;
435}
436
437// Zeros the encoder. This involves blocking for an arbitrary length of time
438// with interrupts disabled.
439void ZeroMotor() {
440#if 0
441 while (true) {
442 if (PERIPHERAL_BITBAND(GPIOB_PDIR, 11)) {
443 encoder_ftm_->CNT = 0;
444 break;
445 }
446 }
447#else
448 uint32_t scratch;
449 __disable_irq();
450 // Stuff all of this in an inline assembly statement so we can make sure the
451 // compiler doesn't decide sticking constant loads etc in the middle of
452 // the loop is a good idea, because that increases the latency of recognizing
453 // the index pulse edge which makes velocity affect the zeroing accuracy.
454 __asm__ __volatile__(
455 // A label to restart the loop.
456 "0:\n"
457 // Load the current PDIR value for the pin we care about.
458 "ldr %[scratch], [%[pdir_word]]\n"
459 // Terminate the loop if it's non-0.
460 "cbnz %[scratch], 1f\n"
461 // Go back around again.
462 "b 0b\n"
463 // A label to finish the loop.
464 "1:\n"
465 // Reset the count once we're down here. It doesn't actually matter what
466 // value we store because writing anything resets it to CNTIN (ie 0).
467 "str %[scratch], [%[cnt]]\n"
468 : [scratch] "=&l"(scratch)
Philipp Schrader790cb542023-07-05 21:06:52 -0700469 : [pdir_word] "l"(&PERIPHERAL_BITBAND(GPIOB_PDIR, 11)), [cnt] "l"(
470 &FTM1->CNT));
James Kuszmaul998d3032018-09-08 15:41:41 -0700471 __enable_irq();
472#endif
473}
474
475} // namespace
476
477extern "C" int main(void) {
478 // for background about this startup delay, please see these conversations
479 // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
480 // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
481 delay(400);
482
483 // Set all interrupts to the second-lowest priority to start with.
484 for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
485
486 // Now set priorities for all the ones we care about. They only have meaning
487 // relative to each other, which means centralizing them here makes it a lot
488 // more manageable.
489 NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0x3);
490 NVIC_SET_SANE_PRIORITY(IRQ_UART0_STATUS, 0xE);
491
492 // Set the LED's pin to output mode.
493 PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
494 PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
495
496#if 0
497 PERIPHERAL_BITBAND(GPIOA_PDDR, 15) = 1;
498 PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(1);
499#endif
500
501 // Set up the CAN pins.
502 PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
503 PORTB_PCR19 = PORT_PCR_DSE | PORT_PCR_MUX(2);
504
505 DMA.CR = M_DMA_EMLM;
506
507 PORTB_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(3);
508 PORTB_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(3);
509 SIM_SCGC4 |= SIM_SCGC4_UART0;
Brian Silverman4787a6e2018-10-06 16:00:54 -0700510
511 PrintingParameters printing_parameters;
512 printing_parameters.stdout_uart_module = &UART0;
513 printing_parameters.stdout_uart_module_clock_frequency = F_CPU;
514 printing_parameters.stdout_uart_status_interrupt = IRQ_UART0_STATUS;
515 printing_parameters.dedicated_usb = true;
516 const ::std::unique_ptr<PrintingImplementation> printing =
517 CreatePrinting(printing_parameters);
518 printing->Initialize();
James Kuszmaul998d3032018-09-08 15:41:41 -0700519
520 AdcInitFet12();
521 MathInit();
522 delay(100);
523 can_init(0, 1);
524
525 MotorControlsImplementation controls;
526
527 delay(100);
528
529 // Index pin
530 PORTB_PCR11 = PORT_PCR_MUX(1);
531 // FTM1_QD_PH{A,B}
532 PORTB_PCR0 = PORT_PCR_MUX(6);
533 PORTB_PCR1 = PORT_PCR_MUX(6);
534
535 // FTM0_CH[0-5]
536 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
537 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
538 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
539 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
540 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
541 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(4);
542
543 Motor motor(FTM0, FTM1, &controls, {&FTM0->C0V, &FTM0->C2V, &FTM0->C4V});
544 motor.set_encoder_offset(810);
545 motor.set_deadtime_compensation(9);
546 ConfigurePwmFtm(FTM0);
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700547
Brian Silvermana1d84822018-09-15 17:18:49 -0700548 // TODO(Brian): Figure out how to avoid duplicating this code to slave one FTM
549 // to another.
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700550 FTM2->CONF = FTM_CONF_GTBEEN;
551 FTM2->MODE = FTM_MODE_WPDIS;
552 FTM2->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
553 FTM2->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
554 FTM2->CNTIN = 0;
555 FTM2->CNT = 0;
556 // TODO(Brian): Don't duplicate this.
557 FTM2->MOD = BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY;
558 FTM2->OUTINIT = 0;
559 // All of the channels are active high.
560 FTM2->POL = 0;
561 FTM2->SYNCONF = FTM_SYNCONF_HWWRBUF | FTM_SYNCONF_SWWRBUF |
562 FTM_SYNCONF_SWRSTCNT | FTM_SYNCONF_SYNCMODE;
563 // Don't want any intermediate loading points.
564 FTM2->PWMLOAD = 0;
565
566 // Need to set them to some kind of output mode so we can actually change
567 // them.
568 FTM2->C0SC = FTM_CSC_MSA;
569 FTM2->C1SC = FTM_CSC_MSA;
570
571 // This has to happen after messing with SYNCONF, and should happen after
572 // messing with various other things so the values can get flushed out of the
573 // buffers.
Philipp Schrader790cb542023-07-05 21:06:52 -0700574 FTM2->SYNC = FTM_SYNC_SWSYNC /* Flush everything out right now */ |
575 FTM_SYNC_CNTMAX /* Load new values at the end of the cycle */;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700576 // Wait for the software synchronization to finish.
577 while (FTM2->SYNC & FTM_SYNC_SWSYNC) {
578 }
579 FTM2->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
Philipp Schrader790cb542023-07-05 21:06:52 -0700580 FTM_SC_PS(0) /* Don't prescale the clock */;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700581 // TODO:
Philipp Schrader790cb542023-07-05 21:06:52 -0700582 // FTM2->MODE &= ~FTM_MODE_WPDIS;
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700583
584 FTM2->EXTTRIG = FTM_EXTTRIG_CH0TRIG | FTM_EXTTRIG_CH1TRIG;
585
Brian Silvermana1d84822018-09-15 17:18:49 -0700586 // TODO(Brian): Don't duplicate the timer's MOD value.
587 teensy::AdcDmaSampler adc_dma{BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY};
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700588 // ADC0_Dx0 is 1-0
589 // ADC0_Dx2 is 1-2
590 // ADC0_Dx3 is 2-0
591 // ADC1_Dx0 is 2-0
592 // ADC1_Dx3 is 1-0
593 // Sample 0: 1-2,2-0
594 // Sample 1: 1-2,1-0
595 // Sample 2: 1-0,2-0
596 // Sample 3: 23(SENSE0),18(VIN)
597 adc_dma.set_adc0_samples({V_ADC_ADCH(2) | M_ADC_DIFF,
598 V_ADC_ADCH(2) | M_ADC_DIFF,
599 V_ADC_ADCH(0) | M_ADC_DIFF, V_ADC_ADCH(23)});
600 adc_dma.set_adc1_samples({V_ADC_ADCH(0) | M_ADC_DIFF,
601 V_ADC_ADCH(3) | M_ADC_DIFF,
602 V_ADC_ADCH(0) | M_ADC_DIFF, V_ADC_ADCH(18)});
603 adc_dma.set_ftm_delays({&FTM2->C0V, &FTM2->C1V});
604 adc_dma.set_pdb_input(PDB_IN_FTM2);
605
606 adc_dma.Initialize();
607 FTM0->CONF = FTM_CONF_GTBEEN;
James Kuszmaul998d3032018-09-08 15:41:41 -0700608 motor.Init();
609 global_motor.store(&motor, ::std::memory_order_relaxed);
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700610 global_adc_dma.store(&adc_dma, ::std::memory_order_relaxed);
611
James Kuszmaul998d3032018-09-08 15:41:41 -0700612 // Output triggers to things like the PDBs on initialization.
613 FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
614 // Don't let any memory accesses sneak past here, because we actually
615 // need everything to be starting up.
Philipp Schrader790cb542023-07-05 21:06:52 -0700616 __asm__("" ::: "memory");
James Kuszmaul998d3032018-09-08 15:41:41 -0700617
618 // Give everything a chance to get going.
619 delay(100);
620
621 printf("Ram start: %p\n", __bss_ram_start__);
622 printf("Heap start: %p\n", __heap_start__);
623 printf("Heap end: %p\n", __brkval);
624 printf("Stack start: %p\n", __stack_end__);
625
626 printf("Going silent to zero motors...\n");
627 // Give the print a chance to make it out.
628 delay(100);
629 ZeroMotor();
630
631 motor.set_encoder_multiplier(-1);
632 motor.set_encoder_calibration_offset(
James Kuszmaul521eb652018-10-17 19:09:33 -0700633 364 /*from running constant phases*/ - 26 /*average offset from lstsq*/ -
634 14 /* compensation for going backwards */);
James Kuszmaul998d3032018-09-08 15:41:41 -0700635
636 printf("Zeroed motor!\n");
637 // Give stuff a chance to recover from interrupts-disabled.
638 delay(100);
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700639 adc_dma.Reset();
James Kuszmaul998d3032018-09-08 15:41:41 -0700640 motor.Start();
James Kuszmaul7c8aad62018-09-08 18:16:18 -0700641 // Now poke the GTB to actually start both timers.
642 FTM0->CONF = FTM_CONF_GTBEEN | FTM_CONF_GTBEOUT;
643
James Kuszmaul998d3032018-09-08 15:41:41 -0700644 NVIC_ENABLE_IRQ(IRQ_FTM0);
645 GPIOC_PSOR = 1 << 5;
646
Brian Silverman37a95d62018-11-09 16:08:32 -0800647 constexpr bool dump_full_sample = false;
James Kuszmaul521eb652018-10-17 19:09:33 -0700648 constexpr bool dump_resist_calib = false;
Brian Silverman37a95d62018-11-09 16:08:32 -0800649 constexpr bool repeat_calib = true;
James Kuszmaul998d3032018-09-08 15:41:41 -0700650 while (true) {
James Kuszmaul521eb652018-10-17 19:09:33 -0700651 if (dump_resist_calib || dump_full_sample) {
James Kuszmaul998d3032018-09-08 15:41:41 -0700652 PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
653 PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
654 PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
655 PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
656 PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
657 PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(4);
658 motor.Reset();
659 }
660 global_debug_buffer.size.store(0);
661 global_debug_buffer.count.store(0);
662 while (global_debug_buffer.size.load(::std::memory_order_relaxed) <
663 global_debug_buffer.samples.size()) {
664 }
James Kuszmaul521eb652018-10-17 19:09:33 -0700665 if (dump_resist_calib) {
666 // Useful prints for when calibrating resistance/inductance of motor
667 for (size_t i = 0; i < global_debug_buffer.samples.size(); ++i) {
668 const auto &sample = global_debug_buffer.samples[i];
Brian Silverman37a95d62018-11-09 16:08:32 -0800669#if 1
Philipp Schrader790cb542023-07-05 21:06:52 -0700670 printf("%u, %d, %d, %d, %u, %u, %u, %u\n", i, sample.currents[0],
671 sample.currents[1], sample.currents[2], sample.commands[0],
672 sample.commands[1], sample.commands[2], sample.position);
Brian Silverman37a95d62018-11-09 16:08:32 -0800673#else
674 printf("%" PRIu16 ",%" PRIu16 ",%" PRIu16 ",%" PRId16 ",%" PRId16
675 ",%" PRId16 "\n",
676 sample.commands[0], sample.commands[1], sample.commands[2],
677 sample.readings[0], sample.readings[1], sample.readings[2]);
678#endif
James Kuszmaul521eb652018-10-17 19:09:33 -0700679 }
680 } else if (dump_full_sample) {
James Kuszmaul998d3032018-09-08 15:41:41 -0700681 printf("Dumping data\n");
682 for (size_t i = 0; i < global_debug_buffer.samples.size(); ++i) {
683 const auto &sample = global_debug_buffer.samples[i];
684
685 printf("%u, %d, %d, %d, %u, %u, %u, %u, %d, %d, %d, %d\n", i,
686 sample.currents[0], sample.currents[1], sample.currents[2],
687 sample.commands[0], sample.commands[1], sample.commands[2],
688 sample.position, static_cast<int>(sample.est_omega),
689 sample.commanded_currents[0], sample.commanded_currents[1],
690 sample.commanded_currents[2]);
691 }
692 printf("Done dumping data\n");
693 } else {
Philipp Schrader790cb542023-07-05 21:06:52 -0700694 // const auto &sample = global_debug_buffer.samples.back();
James Kuszmaul998d3032018-09-08 15:41:41 -0700695 const DebugBuffer::Sample sample = global_debug_buffer.samples[0];
696#if 1
697 printf("%" PRIu32
698 ", %d, %d, %d, %u, %u, %u, %u, %d, %d, %d, %d, %d, %d, %d\n",
699 sample.cycles_since_start, sample.currents[0], sample.currents[1],
700 sample.currents[2], sample.commands[0], sample.commands[1],
701 sample.commands[2], sample.position,
702 static_cast<int>(sample.est_omega), sample.commanded_currents[0],
703 sample.commanded_currents[1], sample.commanded_currents[2],
704 sample.total_command, static_cast<int>(sample.driver_request),
705 static_cast<int>(sample.fuse_badness));
706#else
707 printf("%d, %d\n", static_cast<int>(sample.fuse_voltage),
708 sample.fuse_current);
709#endif
710 }
Brian Silverman37a95d62018-11-09 16:08:32 -0800711 if (!repeat_calib) {
712 while (true) {
713 }
714 }
James Kuszmaul998d3032018-09-08 15:41:41 -0700715 }
716
717 return 0;
718}
719
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800720} // namespace frc971::motors