blob: 6722640d3225c2ca86098d7ccc6d8716bdc702cf [file] [log] [blame]
#include "motors/core/kinetis.h"
#include <inttypes.h>
#include <stdio.h>
#include <atomic>
#include "motors/core/time.h"
#include "motors/fet12/current_equalization.h"
#include "motors/fet12/motor_controls.h"
#include "motors/motor.h"
#include "motors/peripheral/adc.h"
#include "motors/peripheral/adc_dma.h"
#include "motors/peripheral/can.h"
#include "motors/print/print.h"
#include "motors/util.h"
namespace frc971 {
namespace motors {
namespace {
constexpr double Kv = 22000.0 * 2.0 * M_PI / 60.0 / 30.0 * 3.6;
constexpr double kVcc = 31.5;
constexpr double kIcc = 125.0;
constexpr double kR = 0.0084;
struct Fet12AdcReadings {
// Averages of the pairs of ADC DMA channels corresponding with each channel
// pair. Individual values in motor_currents correspond to current sensor
// values, rather than the actual currents themselves (and so they still need
// to be decoupled).
int16_t motor_currents[3];
int16_t throttle, fuse_voltage;
};
void AdcInitFet12() {
AdcInitCommon(AdcChannels::kB, AdcChannels::kA);
// M_CH0V ADC0_SE5b
PORTD_PCR1 = PORT_PCR_MUX(0);
// M_CH1V ADC0_SE7b
PORTD_PCR6 = PORT_PCR_MUX(0);
// M_CH2V ADC0_SE14
PORTC_PCR0 = PORT_PCR_MUX(0);
// M_CH0F ADC1_SE5a
PORTE_PCR1 = PORT_PCR_MUX(0);
// M_CH1F ADC1_SE6a
PORTE_PCR2 = PORT_PCR_MUX(0);
// M_CH2F ADC1_SE7a
PORTE_PCR3 = PORT_PCR_MUX(0);
// SENSE0 ADC0_SE23
// dedicated
// SENSE1 ADC0_SE13
PORTB_PCR3 = PORT_PCR_MUX(0);
}
::std::atomic<Motor *> global_motor{nullptr};
::std::atomic<teensy::AdcDmaSampler *> global_adc_dma{nullptr};
extern "C" {
void *__stack_chk_guard = (void *)0x67111971;
void __stack_chk_fail(void) {
while (true) {
GPIOC_PSOR = (1 << 5);
printf("Stack corruption detected\n");
delay(1000);
GPIOC_PCOR = (1 << 5);
delay(1000);
}
}
extern char *__brkval;
extern uint32_t __bss_ram_start__[];
extern uint32_t __heap_start__[];
extern uint32_t __stack_end__[];
struct DebugBuffer {
struct Sample {
::std::array<int16_t, 3> currents;
::std::array<int16_t, 3> commanded_currents;
::std::array<uint16_t, 3> commands;
uint16_t position;
// Driver requested current.
float driver_request;
// Requested current.
int16_t total_command;
float est_omega;
float fuse_voltage;
int16_t fuse_current;
float fuse_badness;
uint32_t cycles_since_start;
};
// The amount of data in the buffer. This will never decrement. This will be
// transferred out the serial port after it fills up.
::std::atomic<size_t> size{0};
::std::atomic<uint32_t> count{0};
// The data.
::std::array<Sample, 512> samples;
};
DebugBuffer global_debug_buffer;
void ftm0_isr(void) {
static uint32_t i = 0;
teensy::AdcDmaSampler *const adc_dma =
global_adc_dma.load(::std::memory_order_relaxed);
Fet12AdcReadings adc_readings;
// TODO(Brian): Switch to the DMA interrupt instead of spinning.
while (!adc_dma->CheckDone()) {
}
adc_readings.motor_currents[0] =
(adc_dma->adc_result(0, 0) + adc_dma->adc_result(0, 1)) / 2;
adc_readings.motor_currents[1] =
(adc_dma->adc_result(0, 2) + adc_dma->adc_result(1, 1)) / 2;
adc_readings.motor_currents[2] =
(adc_dma->adc_result(1, 0) + adc_dma->adc_result(1, 2)) / 2;
adc_readings.throttle = adc_dma->adc_result(0, 3);
const ::std::array<float, 3> decoupled =
DecoupleCurrents(adc_readings.motor_currents);
adc_dma->Reset();
const uint32_t wrapped_encoder =
global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
const BalancedReadings balanced =
BalanceSimpleReadings(decoupled);
#if 1
static float fuse_badness = 0;
static uint32_t cycles_since_start = 0u;
++cycles_since_start;
#if 0
static int count = 0;
++count;
static float currents[3] = {0.0f, 0.0f, 0.0f};
for (int ii = 0; ii < 3; ++ii) {
currents[ii] += static_cast<float>(adc_readings.motor_currents[ii]);
}
if (i == 0) {
printf(
"foo %d.0, %d.0, %d.0, %.3d %.3d %.3d, switching %d %d %d enc %d\n",
static_cast<int>(currents[0] / static_cast<float>(count)),
static_cast<int>(currents[1] / static_cast<float>(count)),
static_cast<int>(currents[2] / static_cast<float>(count)),
static_cast<int>(decoupled[0] * 1.0f),
static_cast<int>(decoupled[1] * 1.0f),
static_cast<int>(decoupled[2] * 1.0f),
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(0),
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(1),
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(2),
static_cast<int>(
global_motor.load(::std::memory_order_relaxed)->wrapped_encoder()));
count = 0;
currents[0] = 0.0f;
currents[1] = 0.0f;
currents[2] = 0.0f;
}
#endif
#if 1
constexpr float kAlpha = 0.995f;
constexpr float kFuseAlpha = 0.95f;
// 3400 - 760
// Start the throttle filter at 1.0f--once it converges to near zero, we set
// throttle_zeroed to true and only then do we start listening to throttle
// commands.
static float filtered_throttle = 1.0f;
static bool throttle_zeroed = false;
constexpr int kMaxThrottle = 3400;
constexpr int kMinThrottle = 760;
const float throttle = ::std::max(
0.0f,
::std::min(1.0f,
static_cast<float>(static_cast<int>(adc_readings.throttle) -
kMinThrottle) /
static_cast<float>(kMaxThrottle - kMinThrottle)));
// y(n) = x(n) + a * (y(n-1) - x(n))
filtered_throttle = throttle + kAlpha * (filtered_throttle - throttle);
if (::std::abs(filtered_throttle) < 1e-2f) {
// Once the filter gets near zero once, we start paying attention to it;
// once it gets near zero once, never ignore it again.
throttle_zeroed = true;
}
const float fuse_voltage = static_cast<float>(adc_readings.fuse_voltage);
static float filtered_fuse_voltage = 0.0f;
filtered_fuse_voltage =
fuse_voltage + kFuseAlpha * (filtered_fuse_voltage - fuse_voltage);
const float velocity =
global_motor.load(::std::memory_order_relaxed)->estimated_velocity();
const float bemf = velocity / (static_cast<float>(Kv) / 1.5f);
const float abs_bemf = ::std::abs(bemf);
constexpr float kPeakCurrent = 300.0f;
constexpr float kLimitedCurrent = 75.0f;
const float max_bat_cur =
fuse_badness > (kLimitedCurrent * kLimitedCurrent * 0.95f)
? kLimitedCurrent
: static_cast<float>(kIcc);
const float throttle_limit = ::std::min(
kPeakCurrent,
(-abs_bemf + ::std::sqrt(static_cast<float>(
bemf * bemf +
4.0f * static_cast<float>(kR) * 1.5f *
static_cast<float>(kVcc) * max_bat_cur))) /
(2.0f * 1.5f * static_cast<float>(kR)));
constexpr float kNegativeCurrent = 100.0f;
float goal_current =
-::std::min(
::std::max(filtered_throttle * (kPeakCurrent + kNegativeCurrent) -
kNegativeCurrent,
-throttle_limit),
throttle_limit);
if (!throttle_zeroed) {
goal_current = 0.0f;
}
// Note: current reduction is 12/70 belt, 15 / 54 on chain, and 10 inch
// diameter wheels, so cutoff of 500 electrical rad/sec * 1 mechanical rad / 2
// erad * 12 / 70 * 15 / 54 * 0.127 m = 1.5m/s = 3.4 mph
if (velocity > -500) {
if (goal_current > 0.0f) {
goal_current = 0.0f;
}
}
//float goal_current =
//-::std::min(filtered_throttle * kPeakCurrent, throttle_limit);
const float overall_measured_current =
global_motor.load(::std::memory_order_relaxed)
->overall_measured_current();
const float fuse_current =
overall_measured_current *
(bemf + overall_measured_current * static_cast<float>(kR) * 1.5f) /
static_cast<float>(kVcc);
const int16_t fuse_current_10 = static_cast<int16_t>(10.0f * fuse_current);
fuse_badness += 0.00002f * (fuse_current * fuse_current - fuse_badness);
global_motor.load(::std::memory_order_relaxed)
->SetGoalCurrent(goal_current);
global_motor.load(::std::memory_order_relaxed)
->CurrentInterrupt(balanced, wrapped_encoder);
global_debug_buffer.count.fetch_add(1);
const bool trigger = false && i > 10000;
// global_debug_buffer.count.load(::std::memory_order_relaxed) >= 0;
size_t buffer_size =
global_debug_buffer.size.load(::std::memory_order_relaxed);
if ((buffer_size > 0 || trigger) &&
buffer_size != global_debug_buffer.samples.size()) {
global_debug_buffer.samples[buffer_size].currents[0] =
static_cast<int16_t>(balanced.readings[0] * 10.0f);
global_debug_buffer.samples[buffer_size].currents[1] =
static_cast<int16_t>(balanced.readings[1] * 10.0f);
global_debug_buffer.samples[buffer_size].currents[2] =
static_cast<int16_t>(balanced.readings[2] * 10.0f);
global_debug_buffer.samples[buffer_size].position =
global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
global_debug_buffer.samples[buffer_size].est_omega =
global_motor.load(::std::memory_order_relaxed)->estimated_velocity();
global_debug_buffer.samples[buffer_size].commands[0] =
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(0);
global_debug_buffer.samples[buffer_size].commands[1] =
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(1);
global_debug_buffer.samples[buffer_size].commands[2] =
global_motor.load(::std::memory_order_relaxed)->get_switching_points_cycles(2);
global_debug_buffer.samples[buffer_size].commanded_currents[0] =
global_motor.load(::std::memory_order_relaxed)->i_goal(0);
global_debug_buffer.samples[buffer_size].commanded_currents[1] =
global_motor.load(::std::memory_order_relaxed)->i_goal(1);
global_debug_buffer.samples[buffer_size].commanded_currents[2] =
global_motor.load(::std::memory_order_relaxed)->i_goal(2);
global_debug_buffer.samples[buffer_size].total_command =
global_motor.load(::std::memory_order_relaxed)->goal_current();
global_debug_buffer.samples[buffer_size].fuse_voltage =
filtered_fuse_voltage;
global_debug_buffer.samples[buffer_size].fuse_current = fuse_current_10;
global_debug_buffer.samples[buffer_size].driver_request =
::std::max(filtered_throttle * (kPeakCurrent + kNegativeCurrent) -
kNegativeCurrent,
0.0f);
global_debug_buffer.samples[buffer_size].fuse_badness = fuse_badness;
global_debug_buffer.samples[buffer_size].cycles_since_start = cycles_since_start;
global_debug_buffer.size.fetch_add(1);
}
++i;
if (buffer_size == global_debug_buffer.samples.size()) {
i = 0;
GPIOC_PCOR = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
GPIOD_PCOR = (1 << 4) | (1 << 5);
PERIPHERAL_BITBAND(GPIOC_PDDR, 1) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 2) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 3) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
PERIPHERAL_BITBAND(GPIOD_PDDR, 4) = 1;
PERIPHERAL_BITBAND(GPIOD_PDDR, 5) = 1;
PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
}
#else
#endif
#else
// Useful code when calculating resistance/inductance of motor
FTM0->SC &= ~FTM_SC_TOF;
FTM0->C0V = 0;
FTM0->C1V = 0;
FTM0->C2V = 0;
FTM0->C3V = 0;
FTM0->C4V = 0;
FTM0->C5V = 10;
FTM0->PWMLOAD = FTM_PWMLOAD_LDOK;
(void)wrapped_encoder;
(void)real_throttle;
size_t buffer_size =
global_debug_buffer.size.load(::std::memory_order_relaxed);
bool trigger = true || i > 20000;
if ((trigger || buffer_size > 0) &&
buffer_size != global_debug_buffer.samples.size()) {
global_debug_buffer.samples[buffer_size].currents[0] =
static_cast<int16_t>(balanced.readings[0] * 10.0f);
global_debug_buffer.samples[buffer_size].currents[1] =
static_cast<int16_t>(balanced.readings[1] * 10.0f);
global_debug_buffer.samples[buffer_size].currents[2] =
static_cast<int16_t>(balanced.readings[2] * 10.0f);
global_debug_buffer.samples[buffer_size].commands[0] = FTM0->C1V;
global_debug_buffer.samples[buffer_size].commands[1] = FTM0->C3V;
global_debug_buffer.samples[buffer_size].commands[2] = FTM0->C5V;
global_debug_buffer.samples[buffer_size].position =
global_motor.load(::std::memory_order_relaxed)->wrapped_encoder();
global_debug_buffer.size.fetch_add(1);
}
if (buffer_size == global_debug_buffer.samples.size()) {
GPIOC_PCOR = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
GPIOD_PCOR = (1 << 4) | (1 << 5);
PERIPHERAL_BITBAND(GPIOC_PDDR, 1) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 2) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 3) = 1;
PERIPHERAL_BITBAND(GPIOC_PDDR, 4) = 1;
PERIPHERAL_BITBAND(GPIOD_PDDR, 4) = 1;
PERIPHERAL_BITBAND(GPIOD_PDDR, 5) = 1;
PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(1);
PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
i = 0;
}
++i;
#endif
}
} // extern "C"
void ConfigurePwmFtm(BigFTM *pwm_ftm) {
// Put them all into combine active-high mode, and all the low ones staying on
// all the time by default.
pwm_ftm->C0SC = FTM_CSC_ELSA;
pwm_ftm->C0V = 0;
pwm_ftm->C1SC = FTM_CSC_ELSA;
pwm_ftm->C1V = 0;
pwm_ftm->C2SC = FTM_CSC_ELSA;
pwm_ftm->C2V = 0;
pwm_ftm->C3SC = FTM_CSC_ELSA;
pwm_ftm->C3V = 0;
pwm_ftm->C4SC = FTM_CSC_ELSA;
pwm_ftm->C4V = 0;
pwm_ftm->C5SC = FTM_CSC_ELSA;
pwm_ftm->C5V = 0;
pwm_ftm->C6SC = FTM_CSC_ELSA;
pwm_ftm->C6V = 0;
pwm_ftm->C7SC = FTM_CSC_ELSA;
pwm_ftm->C7V = 0;
pwm_ftm->COMBINE = FTM_COMBINE_SYNCEN3 /* Synchronize updates usefully */ |
FTM_COMBINE_DTEN3 /* Enable deadtime */ |
FTM_COMBINE_COMP3 /* Make them complementary */ |
FTM_COMBINE_COMBINE3 /* Combine the channels */ |
FTM_COMBINE_SYNCEN2 /* Synchronize updates usefully */ |
FTM_COMBINE_DTEN2 /* Enable deadtime */ |
FTM_COMBINE_COMP2 /* Make them complementary */ |
FTM_COMBINE_COMBINE2 /* Combine the channels */ |
FTM_COMBINE_SYNCEN1 /* Synchronize updates usefully */ |
FTM_COMBINE_DTEN1 /* Enable deadtime */ |
FTM_COMBINE_COMP1 /* Make them complementary */ |
FTM_COMBINE_COMBINE1 /* Combine the channels */ |
FTM_COMBINE_SYNCEN0 /* Synchronize updates usefully */ |
FTM_COMBINE_DTEN0 /* Enable deadtime */ |
FTM_COMBINE_COMP0 /* Make them complementary */ |
FTM_COMBINE_COMBINE0 /* Combine the channels */;
// Safe state for all channels is low.
pwm_ftm->POL = 0;
// Set the deadtime.
pwm_ftm->DEADTIME =
FTM_DEADTIME_DTPS(0) /* Prescaler of 1 */ | FTM_DEADTIME_DTVAL(9);
pwm_ftm->CONF =
FTM_CONF_BDMMOD(1) /* Set everything to POLn during debug halt */;
}
// Zeros the encoder. This involves blocking for an arbitrary length of time
// with interrupts disabled.
void ZeroMotor() {
#if 0
while (true) {
if (PERIPHERAL_BITBAND(GPIOB_PDIR, 11)) {
encoder_ftm_->CNT = 0;
break;
}
}
#else
uint32_t scratch;
__disable_irq();
// Stuff all of this in an inline assembly statement so we can make sure the
// compiler doesn't decide sticking constant loads etc in the middle of
// the loop is a good idea, because that increases the latency of recognizing
// the index pulse edge which makes velocity affect the zeroing accuracy.
__asm__ __volatile__(
// A label to restart the loop.
"0:\n"
// Load the current PDIR value for the pin we care about.
"ldr %[scratch], [%[pdir_word]]\n"
// Terminate the loop if it's non-0.
"cbnz %[scratch], 1f\n"
// Go back around again.
"b 0b\n"
// A label to finish the loop.
"1:\n"
// Reset the count once we're down here. It doesn't actually matter what
// value we store because writing anything resets it to CNTIN (ie 0).
"str %[scratch], [%[cnt]]\n"
: [scratch] "=&l"(scratch)
: [pdir_word] "l"(&PERIPHERAL_BITBAND(GPIOB_PDIR, 11)),
[cnt] "l"(&FTM1->CNT));
__enable_irq();
#endif
}
} // namespace
extern "C" int main(void) {
// for background about this startup delay, please see these conversations
// https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
// https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
delay(400);
// Set all interrupts to the second-lowest priority to start with.
for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
// Now set priorities for all the ones we care about. They only have meaning
// relative to each other, which means centralizing them here makes it a lot
// more manageable.
NVIC_SET_SANE_PRIORITY(IRQ_FTM0, 0x3);
NVIC_SET_SANE_PRIORITY(IRQ_UART0_STATUS, 0xE);
// Set the LED's pin to output mode.
PERIPHERAL_BITBAND(GPIOC_PDDR, 5) = 1;
PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
#if 0
PERIPHERAL_BITBAND(GPIOA_PDDR, 15) = 1;
PORTA_PCR15 = PORT_PCR_DSE | PORT_PCR_MUX(1);
#endif
// Set up the CAN pins.
PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
PORTB_PCR19 = PORT_PCR_DSE | PORT_PCR_MUX(2);
DMA.CR = M_DMA_EMLM;
PORTB_PCR16 = PORT_PCR_DSE | PORT_PCR_MUX(3);
PORTB_PCR17 = PORT_PCR_DSE | PORT_PCR_MUX(3);
SIM_SCGC4 |= SIM_SCGC4_UART0;
PrintingParameters printing_parameters;
printing_parameters.stdout_uart_module = &UART0;
printing_parameters.stdout_uart_module_clock_frequency = F_CPU;
printing_parameters.stdout_uart_status_interrupt = IRQ_UART0_STATUS;
printing_parameters.dedicated_usb = true;
const ::std::unique_ptr<PrintingImplementation> printing =
CreatePrinting(printing_parameters);
printing->Initialize();
AdcInitFet12();
MathInit();
delay(100);
can_init(0, 1);
MotorControlsImplementation controls;
delay(100);
// Index pin
PORTB_PCR11 = PORT_PCR_MUX(1);
// FTM1_QD_PH{A,B}
PORTB_PCR0 = PORT_PCR_MUX(6);
PORTB_PCR1 = PORT_PCR_MUX(6);
// FTM0_CH[0-5]
PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(4);
Motor motor(FTM0, FTM1, &controls, {&FTM0->C0V, &FTM0->C2V, &FTM0->C4V});
motor.set_encoder_offset(810);
motor.set_deadtime_compensation(9);
ConfigurePwmFtm(FTM0);
// TODO(Brian): Figure out how to avoid duplicating this code to slave one FTM
// to another.
FTM2->CONF = FTM_CONF_GTBEEN;
FTM2->MODE = FTM_MODE_WPDIS;
FTM2->MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN;
FTM2->SC = FTM_SC_CLKS(0) /* Disable counting for now */;
FTM2->CNTIN = 0;
FTM2->CNT = 0;
// TODO(Brian): Don't duplicate this.
FTM2->MOD = BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY;
FTM2->OUTINIT = 0;
// All of the channels are active high.
FTM2->POL = 0;
FTM2->SYNCONF = FTM_SYNCONF_HWWRBUF | FTM_SYNCONF_SWWRBUF |
FTM_SYNCONF_SWRSTCNT | FTM_SYNCONF_SYNCMODE;
// Don't want any intermediate loading points.
FTM2->PWMLOAD = 0;
// Need to set them to some kind of output mode so we can actually change
// them.
FTM2->C0SC = FTM_CSC_MSA;
FTM2->C1SC = FTM_CSC_MSA;
// This has to happen after messing with SYNCONF, and should happen after
// messing with various other things so the values can get flushed out of the
// buffers.
FTM2->SYNC =
FTM_SYNC_SWSYNC /* Flush everything out right now */ |
FTM_SYNC_CNTMAX /* Load new values at the end of the cycle */;
// Wait for the software synchronization to finish.
while (FTM2->SYNC & FTM_SYNC_SWSYNC) {
}
FTM2->SC = FTM_SC_CLKS(1) /* Use the system clock */ |
FTM_SC_PS(0) /* Don't prescale the clock */;
// TODO:
//FTM2->MODE &= ~FTM_MODE_WPDIS;
FTM2->EXTTRIG = FTM_EXTTRIG_CH0TRIG | FTM_EXTTRIG_CH1TRIG;
// TODO(Brian): Don't duplicate the timer's MOD value.
teensy::AdcDmaSampler adc_dma{BUS_CLOCK_FREQUENCY / SWITCHING_FREQUENCY};
// ADC0_Dx0 is 1-0
// ADC0_Dx2 is 1-2
// ADC0_Dx3 is 2-0
// ADC1_Dx0 is 2-0
// ADC1_Dx3 is 1-0
// Sample 0: 1-2,2-0
// Sample 1: 1-2,1-0
// Sample 2: 1-0,2-0
// Sample 3: 23(SENSE0),18(VIN)
adc_dma.set_adc0_samples({V_ADC_ADCH(2) | M_ADC_DIFF,
V_ADC_ADCH(2) | M_ADC_DIFF,
V_ADC_ADCH(0) | M_ADC_DIFF, V_ADC_ADCH(23)});
adc_dma.set_adc1_samples({V_ADC_ADCH(0) | M_ADC_DIFF,
V_ADC_ADCH(3) | M_ADC_DIFF,
V_ADC_ADCH(0) | M_ADC_DIFF, V_ADC_ADCH(18)});
adc_dma.set_ftm_delays({&FTM2->C0V, &FTM2->C1V});
adc_dma.set_pdb_input(PDB_IN_FTM2);
adc_dma.Initialize();
FTM0->CONF = FTM_CONF_GTBEEN;
motor.Init();
global_motor.store(&motor, ::std::memory_order_relaxed);
global_adc_dma.store(&adc_dma, ::std::memory_order_relaxed);
// Output triggers to things like the PDBs on initialization.
FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN;
// Don't let any memory accesses sneak past here, because we actually
// need everything to be starting up.
__asm__("" :: : "memory");
// Give everything a chance to get going.
delay(100);
printf("Ram start: %p\n", __bss_ram_start__);
printf("Heap start: %p\n", __heap_start__);
printf("Heap end: %p\n", __brkval);
printf("Stack start: %p\n", __stack_end__);
printf("Going silent to zero motors...\n");
// Give the print a chance to make it out.
delay(100);
ZeroMotor();
motor.set_encoder_multiplier(-1);
motor.set_encoder_calibration_offset(
364 /*from running constant phases*/ - 26 /*average offset from lstsq*/ -
14 /* compensation for going backwards */);
printf("Zeroed motor!\n");
// Give stuff a chance to recover from interrupts-disabled.
delay(100);
adc_dma.Reset();
motor.Start();
// Now poke the GTB to actually start both timers.
FTM0->CONF = FTM_CONF_GTBEEN | FTM_CONF_GTBEOUT;
NVIC_ENABLE_IRQ(IRQ_FTM0);
GPIOC_PSOR = 1 << 5;
constexpr bool dump_full_sample = true;
constexpr bool dump_resist_calib = false;
while (true) {
if (dump_resist_calib || dump_full_sample) {
PORTC_PCR1 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR2 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR3 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTC_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTD_PCR4 = PORT_PCR_DSE | PORT_PCR_MUX(4);
PORTD_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(4);
motor.Reset();
}
global_debug_buffer.size.store(0);
global_debug_buffer.count.store(0);
while (global_debug_buffer.size.load(::std::memory_order_relaxed) <
global_debug_buffer.samples.size()) {
}
if (dump_resist_calib) {
// Useful prints for when calibrating resistance/inductance of motor
for (size_t i = 0; i < global_debug_buffer.samples.size(); ++i) {
const auto &sample = global_debug_buffer.samples[i];
printf("%u, %d, %d, %d, %u, %u, %u, %u\n", i,
sample.currents[0], sample.currents[1], sample.currents[2],
sample.commands[0], sample.commands[1], sample.commands[2],
sample.position);
}
} else if (dump_full_sample) {
printf("Dumping data\n");
for (size_t i = 0; i < global_debug_buffer.samples.size(); ++i) {
const auto &sample = global_debug_buffer.samples[i];
printf("%u, %d, %d, %d, %u, %u, %u, %u, %d, %d, %d, %d\n", i,
sample.currents[0], sample.currents[1], sample.currents[2],
sample.commands[0], sample.commands[1], sample.commands[2],
sample.position, static_cast<int>(sample.est_omega),
sample.commanded_currents[0], sample.commanded_currents[1],
sample.commanded_currents[2]);
}
printf("Done dumping data\n");
} else {
//const auto &sample = global_debug_buffer.samples.back();
const DebugBuffer::Sample sample = global_debug_buffer.samples[0];
#if 1
printf("%" PRIu32
", %d, %d, %d, %u, %u, %u, %u, %d, %d, %d, %d, %d, %d, %d\n",
sample.cycles_since_start, sample.currents[0], sample.currents[1],
sample.currents[2], sample.commands[0], sample.commands[1],
sample.commands[2], sample.position,
static_cast<int>(sample.est_omega), sample.commanded_currents[0],
sample.commanded_currents[1], sample.commanded_currents[2],
sample.total_command, static_cast<int>(sample.driver_request),
static_cast<int>(sample.fuse_badness));
#else
printf("%d, %d\n", static_cast<int>(sample.fuse_voltage),
sample.fuse_current);
#endif
}
}
return 0;
}
} // namespace motors
} // namespace frc971