diff --git a/examples/hardware_specific_examples/SAMD_examples/README.md b/examples/hardware_specific_examples/SAMD_examples/README.md
new file mode 100644
index 00000000..d1a52584
--- /dev/null
+++ b/examples/hardware_specific_examples/SAMD_examples/README.md
@@ -0,0 +1,62 @@
+
+# SAMD Support
+
+SimpleFOC supports many SAMD21 MCUs, really any SAMD21 supported by Arduino core should work.
+
+## Pin assignments
+
+The SAMD chips have some very powerful PWM features, but do not have flexible pin assignments.
+
+You should be able to use *most* (but not all!), pin combinations for attaching your motor's PWM pins. Please ignore the board descriptions and pinout diagrammes regarding PWM-pins on SAMD boards. They are pretty much all incorrect to varying degrees of awfulness.
+
+On SAMD we use TCC and TC timer peripherals (built into the SAMD chip) to control the PWM. Depending on the chip there are various timer units, whose PWM outputs are attached to various different pins, and it is all very complicated. Luckily SimpleFOC sets it all up automatically *if* there is a compatible configuration for those pins.
+
+Not all timers are created equal. The TCC timers are pretty awesome for PWM motor control, while the TC timers are just ok for the job. So to get best performance, you want to use just TCC timer pins if you can.
+
+By enabling
+
+```
+ #define SIMPLEFOC_SAMD_DEBUG
+```
+
+in drivers/hardware_specific/samd_mcu.cpp
+you will see a table of pin assignments printed on the serial console, as well as the timers SimpleFOC was able to find and configure on the pins you specified. You can use this to optimize your choice of pins if you want.
+
+You can configure up to 12 pins for PWM motor control, i.e. 6x 2-PWM motors, 4x 3-PWM motors, 3x 4-PWM motors or 2x 6-PWM motors.
+
+## PWM control modes
+
+All modes (3-PWM, 6-PWM, Stepper 2-PWM and Stepper 4-PWM) are supported.
+
+For 2-, 3- amd 4- PWM, any valid pin-combinations can be used. If you stick to TCC timers rather than using TC timers, then you'll get getter PWM waveforms. If you use pins which are all on the same TCC unit, you'll get the best result, with the PWM signals all perfectly aligned as well.
+
+For 6-PWM, the situation is much more complicated:
+TC timers cannot be used for 6-PWM, only TCC timers.
+
+For Hardware Dead-Time insertion, you must use H and L pins for one phase from the same TCC unit, and on the same channel, but using complementary WOs (Waveform Outputs, i.e. PWM output pins). Check the table to find pins on the same channel (like TCC0-0) but complementary WOs (like TCC0-0[0] and TCC0-0[4] or TCC1-0[0] and TCC1-0[2]).
+
+For Software Dead-Time insertion, you must use the same TCC and different channels for the H and L pins of the same phase.
+
+Note: in all of the above note that you *cannot* set the timers or WOs used - they are fixed, and determined by the pins you selected. SimpleFOC will find the best combination of timers given the pins, trying to use TCC timers before TC, and trying to keep things on the same timers as much as possible. If you configure multiple motors, it will take into account the pins already assigned to other motors.
+So it is matter of choosing the right pins, nothing else.
+
+Note also: Unfortunately you can't set the PWM frequency. It is currently fixed at 24KHz. This is a tradeoff between limiting PWM resolution vs
+increasing frequency, and also due to keeping the pin assignemts flexible, which would not be possible if we ran the timers at different rates.
+
+## Status
+
+Currently, SAMD21 is supported, and SAMD51 is unsupported. SAMD51 support is in progress.
+
+Boards tested:
+
+ * Arduino Nano 33 IoT
+ * Arduino MKR1000
+ * Arduino MKR1010 Wifi
+ * Seeduino XIAO
+ * Feather M0 Basic
+
+Environments tested:
+
+ * Arduino IDE
+ * Arduino Pro IDE
+ * Sloeber
diff --git a/examples/hardware_specific_examples/SAMD_examples/magnetic_sensor/nano33IoT_velocity_control.ino b/examples/hardware_specific_examples/SAMD_examples/magnetic_sensor/nano33IoT_velocity_control.ino
new file mode 100644
index 00000000..a7fef55e
--- /dev/null
+++ b/examples/hardware_specific_examples/SAMD_examples/magnetic_sensor/nano33IoT_velocity_control.ino
@@ -0,0 +1,65 @@
+
+// show the infos for SAMD pin assignment on serial console
+// set this #define SIMPLEFOC_SAMD_DEBUG in drivers/hardware_specific/samd21_mcu.h
+
+
+#include "Arduino.h"
+#include
+#include
+#include
+
+// this is for an AS5048B absolute magnetic encoder on I2C address 0x41
+MagneticSensorI2C sensor = MagneticSensorI2C(0x41, 14, 0xFE, 8);
+
+// small BLDC gimbal motor, 7 pole-pairs
+BLDCMotor motor = BLDCMotor(7);
+// 3-PWM driving on pins 6, 5 and 8 - these are all on the same timer unit (TCC0), but different channels
+BLDCDriver3PWM driver = BLDCDriver3PWM(6,5,8);
+
+// velocity set point variable
+float target_velocity = 2.0;
+// instantiate the commander
+Commander command = Commander(SerialUSB);
+void doTarget(char* cmd) { command.variable(&target_velocity, cmd); }
+
+
+void setup() {
+ Serial.begin(115200);
+ delay(1000);
+ Serial.println("Initializing...");
+
+ sensor.init();
+ Wire.setClock(400000);
+ motor.linkSensor(&sensor);
+ driver.voltage_power_supply = 9;
+ driver.init();
+ motor.linkDriver(&driver);
+ motor.controller = MotionControlType::velocity;
+ motor.PID_velocity.P = 0.2;
+ motor.PID_velocity.I = 20;
+ motor.PID_velocity.D = 0.001;
+ motor.PID_velocity.output_ramp = 1000;
+ motor.LPF_velocity.Tf = 0.01;
+ motor.voltage_limit = 9;
+ //motor.P_angle.P = 20;
+ motor.init();
+ motor.initFOC();
+
+ // add target command T
+ command.add('T', doTarget, "target velocity");
+
+ Serial.println(F("Motor ready."));
+ Serial.println(F("Set the target velocity using serial terminal:"));
+ delay(100);
+}
+
+
+
+void loop() {
+// Serial.print("Sensor: ");
+// Serial.println(sensor.getAngle());
+ motor.loopFOC();
+ motor.move(target_velocity);
+ // user communication
+ command.run();
+}
diff --git a/src/communication/Commander.cpp b/src/communication/Commander.cpp
index 97a76bb3..ed7108f8 100644
--- a/src/communication/Commander.cpp
+++ b/src/communication/Commander.cpp
@@ -1,7 +1,7 @@
#include "Commander.h"
-Commander::Commander(HardwareSerial& serial){
+Commander::Commander(Stream& serial){
com_port = &serial;
}
Commander::Commander(){
@@ -430,4 +430,4 @@ void Commander::printVerbose(const __FlashStringHelper *message){
}
void Commander::printError(){
print(F("err"));
-}
\ No newline at end of file
+}
diff --git a/src/communication/Commander.h b/src/communication/Commander.h
index 03657094..f0228dcf 100644
--- a/src/communication/Commander.h
+++ b/src/communication/Commander.h
@@ -39,7 +39,7 @@ class Commander
*
* @param serial - Serial com port instance
*/
- Commander(HardwareSerial &serial);
+ Commander(Stream &serial);
Commander();
/**
@@ -90,7 +90,7 @@ class Commander
uint8_t decimal_places = 3; //!< number of decimal places to be used when displaying numbers
// monitoring functions
- HardwareSerial* com_port = nullptr; //!< Serial terminal variable if provided
+ Stream* com_port = nullptr; //!< Serial terminal variable if provided
/**
*
diff --git a/src/drivers/hardware_specific/generic_mcu.cpp b/src/drivers/hardware_specific/generic_mcu.cpp
index d55b9ff3..73aa3c9f 100644
--- a/src/drivers/hardware_specific/generic_mcu.cpp
+++ b/src/drivers/hardware_specific/generic_mcu.cpp
@@ -12,6 +12,8 @@
#elif defined(_STM32_DEF_) // or stm32
+#elif defined(_SAMD21_) // samd21 for the moment, samd51 in progress...
+
#else
// function setting the high pwm frequency to the supplied pins
@@ -86,4 +88,4 @@ void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, float dead_zone, i
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/drivers/hardware_specific/samd21_mcu.cpp b/src/drivers/hardware_specific/samd21_mcu.cpp
new file mode 100644
index 00000000..cdbd82f8
--- /dev/null
+++ b/src/drivers/hardware_specific/samd21_mcu.cpp
@@ -0,0 +1,965 @@
+
+
+#if defined(ARDUINO_ARCH_SAMD)
+//#if defined(_SAMD21_)
+
+
+#include "../hardware_api.h"
+#include "wiring_private.h"
+
+#include "./samd21_wo_associations.h"
+
+
+#define SIMPLEFOC_SAMD_DEBUG
+
+
+
+#ifndef SIMPLEFOC_SAMD_ALLOW_DIFFERENT_TCCS
+#define SIMPLEFOC_SAMD_ALLOW_DIFFERENT_TCCS false
+#endif
+
+#ifndef SIMPLEFOC_SAMD_PWM_RESOLUTION
+#define SIMPLEFOC_SAMD_PWM_RESOLUTION 1000
+#define SIMPLEFOC_SAMD_PWM_TC_RESOLUTION 250
+#endif
+
+#ifndef SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS
+#define SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS 12
+#endif
+
+
+
+// Wait for synchronization of registers between the clock domains
+static __inline__ void syncTCC(Tcc* TCCx) __attribute__((always_inline, unused));
+static void syncTCC(Tcc* TCCx) {
+ while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK);
+}
+
+
+
+struct tccConfiguration {
+ uint8_t pin;
+ uint8_t alternate; // 1=true, 0=false
+ uint8_t wo;
+ union tccChanInfo {
+ struct {
+ int8_t chan;
+ int8_t tccn;
+ };
+ uint16_t chaninfo;
+ } tcc;
+};
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+#include "./samd_debug.h"
+#endif
+
+
+
+
+/**
+ * Global state
+ */
+tccConfiguration tccPinConfigurations[SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS];
+uint8_t numTccPinConfigurations = 0;
+bool SAMDClockConfigured = false;
+bool tccConfigured[TCC_INST_NUM+TC_INST_NUM];
+
+/**
+ * Configure Clock 4 - we want all simplefoc PWMs to use the same clock. This ensures that
+ * any compatible pin combination can be used without having to worry about configuring different
+ * clocks.
+ */
+void configureSAMDClock() {
+ if (!SAMDClockConfigured) {
+ SAMDClockConfigured = true; // mark clock as configured
+ for (int i=0;iSTATUS.bit.SYNCBUSY); // Wait for synchronization
+
+ REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
+ GCLK_GENCTRL_GENEN | // Enable GCLK4
+ GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
+ GCLK_GENCTRL_ID(4); // Select GCLK4
+ while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Configured clock...");
+#endif
+ }
+}
+
+
+
+
+/**
+ * Configure a TCC unit
+ * pwm_frequency is fixed at 24kHz for now. We could go slower, but going
+ * faster won't be possible without sacrificing resolution.
+ */
+void configureTCC(tccConfiguration& tccConfig, long pwm_frequency, bool negate=false, float hw6pwm=-1) {
+ // TODO for the moment we ignore the frequency...
+ if (!tccConfigured[tccConfig.tcc.tccn]) {
+ uint32_t GCLK_CLKCTRL_ID_ofthistcc = -1;
+ switch (tccConfig.tcc.tccn>>1) {
+ case 0:
+ GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TCC0_TCC1);//GCLK_CLKCTRL_ID_TCC0_TCC1;
+ break;
+ case 1:
+ GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TCC2_TC3);//GCLK_CLKCTRL_ID_TCC2_TC3;
+ break;
+ case 2:
+ GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TC4_TC5);//GCLK_CLKCTRL_ID_TC4_TC5;
+ break;
+ case 3:
+ GCLK_CLKCTRL_ID_ofthistcc = GCLK_CLKCTRL_ID(GCM_TC6_TC7);
+ break;
+ default:
+ return;
+ }
+
+ // Feed GCLK4 to TCC
+ REG_GCLK_CLKCTRL = (uint16_t) GCLK_CLKCTRL_CLKEN | // Enable GCLK4
+ GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
+ GCLK_CLKCTRL_ID_ofthistcc; // Feed GCLK4 to tcc
+ while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
+
+ tccConfigured[tccConfig.tcc.tccn] = true;
+
+ if (tccConfig.tcc.tccn>=TCC_INST_NUM) {
+ Tc* tc = (Tc*)GetTC(tccConfig.tcc.chaninfo);
+
+ // disable
+ tc->COUNT8.CTRLA.bit.ENABLE = 0;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ // unfortunately we need the 8-bit counter mode to use the PER register...
+ tc->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM ;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ // meaning prescaler of 8, since TC-Unit has no up/down mode, and has resolution of 250 rather than 1000...
+ tc->COUNT8.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV8_Val ;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ // period is 250, period cannot be higher than 256!
+ tc->COUNT8.PER.reg = SIMPLEFOC_SAMD_PWM_TC_RESOLUTION-1;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ // initial duty cycle is 0
+ tc->COUNT8.CC[tccConfig.tcc.chan].reg = 0;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ // enable
+ tc->COUNT8.CTRLA.bit.ENABLE = 1;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print("Initialized TC ");
+ Serial.println(tccConfig.tcc.tccn);
+#endif
+ }
+ else {
+ Tcc* tcc = (Tcc*)GetTC(tccConfig.tcc.chaninfo);
+
+ uint8_t invenMask = ~(1<DRVCTRL.vec.INVEN = (tcc->DRVCTRL.vec.INVEN&invenMask)|invenVal;
+ syncTCC(tcc); // wait for sync
+
+ tcc->WAVE.reg |= TCC_WAVE_POL(0xF)|TCC_WAVEB_WAVEGENB_DSBOTH; // Set wave form configuration
+ while ( tcc->SYNCBUSY.bit.WAVE == 1 ); // wait for sync
+
+ if (hw6pwm>0.0) {
+ tcc->WEXCTRL.vec.DTIEN |= (1<WEXCTRL.bit.DTLS = hw6pwm*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1);
+ tcc->WEXCTRL.bit.DTHS = hw6pwm*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1);
+ syncTCC(tcc); // wait for sync
+ }
+
+ tcc->PER.reg = SIMPLEFOC_SAMD_PWM_RESOLUTION - 1; // Set counter Top using the PER register
+ while ( tcc->SYNCBUSY.bit.PER == 1 ); // wait for sync
+
+ // set all channels to 0%
+ uint8_t chanCount = (tccConfig.tcc.tccn==1||tccConfig.tcc.tccn==2)?2:4;
+ for (int i=0;iCC[i].reg = 0; // start off at 0% duty cycle
+ uint32_t chanbit = 0x1<<(TCC_SYNCBUSY_CC0_Pos+i);
+ while ( (tcc->SYNCBUSY.reg & chanbit) > 0 );
+ }
+
+ // enable double buffering
+ //tcc->CTRLBCLR.bit.LUPD = 1;
+ //while ( tcc->SYNCBUSY.bit.CTRLB == 1 );
+
+ // Enable TC
+ tcc->CTRLA.reg |= TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER_DIV1; //48Mhz/1=48Mhz/2(up/down)=24MHz/1024=24KHz
+ while ( tcc->SYNCBUSY.bit.ENABLE == 1 ); // wait for sync
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print(" Initialized TCC ");
+ Serial.print(tccConfig.tcc.tccn);
+ Serial.print("-");
+ Serial.print(tccConfig.tcc.chan);
+ Serial.print("[");
+ Serial.print(tccConfig.wo);
+ Serial.println("]");
+#endif
+ }
+ }
+ else if (tccConfig.tcc.tccnCTRLA.bit.ENABLE = 0;
+ while ( tcc->SYNCBUSY.bit.ENABLE == 1 );
+
+ uint8_t invenMask = ~(1<DRVCTRL.vec.INVEN = (tcc->DRVCTRL.vec.INVEN&invenMask)|invenVal;
+ syncTCC(tcc); // wait for sync
+
+ if (hw6pwm>0.0) {
+ tcc->WEXCTRL.vec.DTIEN |= (1<WEXCTRL.bit.DTLS = hw6pwm*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1);
+ tcc->WEXCTRL.bit.DTHS = hw6pwm*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1);
+ syncTCC(tcc); // wait for sync
+ }
+
+ tcc->CTRLA.bit.ENABLE = 1;
+ while ( tcc->SYNCBUSY.bit.ENABLE == 1 );
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print("(Re-)Initialized TCC ");
+ Serial.print(tccConfig.tcc.tccn);
+ Serial.print("-");
+ Serial.print(tccConfig.tcc.chan);
+ Serial.print("[");
+ Serial.print(tccConfig.wo);
+ Serial.println("]");
+#endif
+ }
+
+
+}
+
+
+
+
+
+/**
+ * Attach the TCC to the pin
+ */
+bool attachTCC(tccConfiguration& tccConfig) {
+ if (numTccPinConfigurations>=SIMPLEFOC_SAMD_MAX_TCC_PINCONFIGURATIONS)
+ return false;
+ pinMode(tccConfig.pin, OUTPUT);
+ pinPeripheral(tccConfig.pin, (tccConfig.alternate==1)?EPioType::PIO_TIMER_ALT:EPioType::PIO_TIMER);
+ tccPinConfigurations[numTccPinConfigurations++] = tccConfig;
+ return true;
+}
+
+
+
+
+
+
+/**
+ * Check if the configuration is in use already.
+ */
+bool inUse(tccConfiguration& tccConfig) {
+ for (int i=0;i=TCC_INST_NUM)
+ return false;
+
+ if (pinAh.tcc.chan==pinBh.tcc.chan || pinAh.tcc.chan==pinBl.tcc.chan || pinAh.tcc.chan==pinCh.tcc.chan || pinAh.tcc.chan==pinCl.tcc.chan)
+ return false;
+ if (pinBh.tcc.chan==pinCh.tcc.chan || pinBh.tcc.chan==pinCl.tcc.chan)
+ return false;
+ if (pinAl.tcc.chan==pinBh.tcc.chan || pinAl.tcc.chan==pinBl.tcc.chan || pinAl.tcc.chan==pinCh.tcc.chan || pinAl.tcc.chan==pinCl.tcc.chan)
+ return false;
+ if (pinBl.tcc.chan==pinCh.tcc.chan || pinBl.tcc.chan==pinCl.tcc.chan)
+ return false;
+
+ if (pinAh.tcc.chan!=pinAl.tcc.chan || pinBh.tcc.chan!=pinBl.tcc.chan || pinCh.tcc.chan!=pinCl.tcc.chan)
+ return false;
+ if (pinAh.wo==pinAl.wo || pinBh.wo==pinBl.wo || pinCh.wo!=pinCl.wo)
+ return false;
+
+ return true;
+}
+
+
+
+
+bool checkPeripheralPermutationCompatible(tccConfiguration pins[], uint8_t num) {
+ for (int i=0;i=TCC_INST_NUM || pinAl.tcc.tccn>=TCC_INST_NUM || pinBh.tcc.tccn>=TCC_INST_NUM
+ || pinBl.tcc.tccn>=TCC_INST_NUM || pinCh.tcc.tccn>=TCC_INST_NUM || pinCl.tcc.tccn>=TCC_INST_NUM)
+ return false;
+
+ // check we're not in use
+ if (inUse(pinAh) || inUse(pinAl) || inUse(pinBh) || inUse(pinBl) || inUse(pinCh) || inUse(pinCl))
+ return false;
+
+ // check pins are all different tccs/channels
+ if (pinAh.tcc.chaninfo==pinBh.tcc.chaninfo || pinAh.tcc.chaninfo==pinBl.tcc.chaninfo || pinAh.tcc.chaninfo==pinCh.tcc.chaninfo || pinAh.tcc.chaninfo==pinCl.tcc.chaninfo)
+ return false;
+ if (pinAl.tcc.chaninfo==pinBh.tcc.chaninfo || pinAl.tcc.chaninfo==pinBl.tcc.chaninfo || pinAl.tcc.chaninfo==pinCh.tcc.chaninfo || pinAl.tcc.chaninfo==pinCl.tcc.chaninfo)
+ return false;
+ if (pinBh.tcc.chaninfo==pinCh.tcc.chaninfo || pinBh.tcc.chaninfo==pinCl.tcc.chaninfo)
+ return false;
+ if (pinBl.tcc.chaninfo==pinCh.tcc.chaninfo || pinBl.tcc.chaninfo==pinCl.tcc.chaninfo)
+ return false;
+
+ // check H/L pins are on same timer
+ if (pinAh.tcc.tccn!=pinAl.tcc.tccn || pinBh.tcc.tccn!=pinBl.tcc.tccn || pinCh.tcc.tccn!=pinCl.tcc.tccn)
+ return false;
+
+ // check H/L pins aren't on both the same timer and wo
+ if (pinAh.wo==pinAl.wo || pinBh.wo==pinBl.wo || pinCh.wo==pinCl.wo)
+ return false;
+
+ return true;
+}
+
+
+
+
+
+int checkHardware6PWM(const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
+ for (int i=0;i<64;i++) {
+ tccConfiguration pinAh = getTCCChannelNr(pinA_h, (i>>0&0x01)==0x1);
+ tccConfiguration pinAl = getTCCChannelNr(pinA_l, (i>>1&0x01)==0x1);
+ tccConfiguration pinBh = getTCCChannelNr(pinB_h, (i>>2&0x01)==0x1);
+ tccConfiguration pinBl = getTCCChannelNr(pinB_l, (i>>3&0x01)==0x1);
+ tccConfiguration pinCh = getTCCChannelNr(pinC_h, (i>>4&0x01)==0x1);
+ tccConfiguration pinCl = getTCCChannelNr(pinC_l, (i>>5&0x01)==0x1);
+ if (checkPeripheralPermutationSameTCC6(pinAh, pinAl, pinBh, pinBl, pinCh, pinCl))
+ return i;
+ }
+ return -1;
+}
+
+
+
+
+int checkSoftware6PWM(const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
+ for (int i=0;i<64;i++) {
+ tccConfiguration pinAh = getTCCChannelNr(pinA_h, (i>>0&0x01)==0x1);
+ tccConfiguration pinAl = getTCCChannelNr(pinA_l, (i>>1&0x01)==0x1);
+ tccConfiguration pinBh = getTCCChannelNr(pinB_h, (i>>2&0x01)==0x1);
+ tccConfiguration pinBl = getTCCChannelNr(pinB_l, (i>>3&0x01)==0x1);
+ tccConfiguration pinCh = getTCCChannelNr(pinC_h, (i>>4&0x01)==0x1);
+ tccConfiguration pinCl = getTCCChannelNr(pinC_l, (i>>5&0x01)==0x1);
+ if (checkPeripheralPermutationCompatible6(pinAh, pinAl, pinBh, pinBl, pinCh, pinCl))
+ return i;
+ }
+ return -1;
+}
+
+
+
+int scorePermutation(tccConfiguration pins[], uint8_t num) {
+ uint32_t usedtccs = 0;
+ for (int i=0;i>1;
+ }
+ for (int i=0;i>1;
+ }
+ return score;
+}
+
+
+
+
+
+int checkPermutations(uint8_t num, int pins[], bool (*checkFunc)(tccConfiguration[], uint8_t) ) {
+ tccConfiguration tccConfs[num];
+ int best = -1;
+ int bestscore = 1000000;
+ for (int i=0;i<(0x1<>j)&0x01)==0x1);
+ if (checkFunc(tccConfs, num)) {
+ int score = scorePermutation(tccConfs, num);
+ if (scoreCC[chan].reg = (uint32_t)((SIMPLEFOC_SAMD_PWM_RESOLUTION-1) * dc);
+ uint32_t chanbit = 0x1<<(TCC_SYNCBUSY_CC0_Pos+chan);
+ while ( (tcc->SYNCBUSY.reg & chanbit) > 0 );
+ // set via CCB
+// tcc->CCB[chan].reg = (uint32_t)((SIMPLEFOC_SAMD_PWM_RESOLUTION-1) * dc);
+// tcc->STATUS.vec.CCBV = tcc->STATUS.vec.CCBV | (1<SYNCBUSY.reg & chanbit) > 0 );
+ }
+ else {
+ Tc* tc = (Tc*)GetTC(chaninfo);
+ //tc->COUNT16.CC[chan].reg = (uint32_t)((SIMPLEFOC_SAMD_PWM_RESOLUTION-1) * dc);
+ tc->COUNT8.CC[chan].reg = (uint8_t)((SIMPLEFOC_SAMD_PWM_TC_RESOLUTION-1) * dc);;
+ while ( tc->COUNT8.STATUS.bit.SYNCBUSY == 1 );
+ }
+}
+
+
+
+
+
+
+
+/**
+ * Configuring PWM frequency, resolution and alignment
+ * - Stepper driver - 2PWM setting
+ * - hardware specific
+ *
+ * @param pwm_frequency - frequency in hertz - if applicable
+ * @param pinA pinA bldc driver
+ * @param pinB pinB bldc driver
+ */
+void _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ printAllPinInfos();
+#endif
+ int pins[2] = { pinA, pinB };
+ int compatibility = checkPermutations(2, pins, checkPeripheralPermutationCompatible);
+ if (compatibility<0) {
+ // no result!
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Bad combination!");
+#endif
+ return;
+ }
+
+ tccConfiguration tccConfs[2] = { getTCCChannelNr(pinA, (compatibility>>0&0x01)==0x1),
+ getTCCChannelNr(pinB, (compatibility>>1&0x01)==0x1) };
+
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print("Found configuration: (score=");
+ Serial.print(scorePermutation(tccConfs, 2));
+ Serial.println(")");
+ printTCCConfiguration(tccConfs[0]);
+ printTCCConfiguration(tccConfs[1]);
+#endif
+
+ // attach pins to timer peripherals
+ attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
+ attachTCC(tccConfs[1]);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Attached pins...");
+#endif
+
+ // set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
+ // e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
+ configureSAMDClock();
+
+ // configure the TCC (waveform, top-value, pre-scaler = frequency)
+ configureTCC(tccConfs[0], pwm_frequency);
+ configureTCC(tccConfs[1], pwm_frequency);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Configured TCCs...");
+#endif
+
+ return; // Someone with a stepper-setup who can test it?
+}
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Configuring PWM frequency, resolution and alignment
+ * - BLDC driver - 3PWM setting
+ * - hardware specific
+ *
+ * SAMD21 will support up to 2 BLDC motors in 3-PWM:
+ * one on TCC0 using 3 of the channels 0,1,2 or 3
+ * one on TCC3 using 3 of the channels 0,1,2 or 3
+ * i.e. 8 different pins can be used, but only 4 different signals (WO[x]) on those 8 pins
+ * WO[0] and WO[4] are the same
+ * WO[1] and WO[5] are the same
+ * WO[2] and WO[6] are the same
+ * WO[3] and WO[7] are the same
+ *
+ * If you're on the Arduino Nano33 IoT, please see the Nano33 IoT pinout diagram to see which TCC0/WO[x]
+ * signal is on which pin of the Nano. You can drive one motor on TCC0. For other boards, consult their documentation.
+ *
+ * Note:
+ * That's if we want to keep the signals strictly in sync.
+ *
+ * If we can accept out-of-sync PWMs on the different phases, we could drive up to 4 BLDCs in 3-PWM mode,
+ * using all the TCC channels. (TCC0 & TCC3 - 4 channels each, TCC1 & TCC2 - 2 channels each)
+ *
+ * All channels will use the same resolution, prescaler and clock, but they will have different start-times leading
+ * to misaligned signals.
+ *
+ *
+ * @param pwm_frequency - frequency in hertz - if applicable
+ * @param pinA pinA bldc driver
+ * @param pinB pinB bldc driver
+ * @param pinC pinC bldc driver
+ */
+void _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC) {
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ printAllPinInfos();
+#endif
+ int pins[3] = { pinA, pinB, pinC };
+ int compatibility = checkPermutations(3, pins, checkPeripheralPermutationCompatible);
+ if (compatibility<0) {
+ // no result!
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Bad combination!");
+#endif
+ return;
+ }
+
+ tccConfiguration tccConfs[3] = { getTCCChannelNr(pinA, (compatibility>>0&0x01)==0x1),
+ getTCCChannelNr(pinB, (compatibility>>1&0x01)==0x1),
+ getTCCChannelNr(pinC, (compatibility>>2&0x01)==0x1) };
+
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print("Found configuration: (score=");
+ Serial.print(scorePermutation(tccConfs, 3));
+ Serial.println(")");
+ printTCCConfiguration(tccConfs[0]);
+ printTCCConfiguration(tccConfs[1]);
+ printTCCConfiguration(tccConfs[2]);
+#endif
+
+ // attach pins to timer peripherals
+ attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
+ attachTCC(tccConfs[1]);
+ attachTCC(tccConfs[2]);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Attached pins...");
+#endif
+
+ // set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
+ // e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
+ configureSAMDClock();
+
+ // configure the TCC (waveform, top-value, pre-scaler = frequency)
+ configureTCC(tccConfs[0], pwm_frequency);
+ configureTCC(tccConfs[1], pwm_frequency);
+ configureTCC(tccConfs[2], pwm_frequency);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Configured TCCs...");
+#endif
+
+}
+
+
+
+
+
+
+
+
+/**
+ * Configuring PWM frequency, resolution and alignment
+ * - Stepper driver - 4PWM setting
+ * - hardware specific
+ *
+ * @param pwm_frequency - frequency in hertz - if applicable
+ * @param pin1A pin1A stepper driver
+ * @param pin1B pin1B stepper driver
+ * @param pin2A pin2A stepper driver
+ * @param pin2B pin2B stepper driver
+ */
+void _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) {
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ printAllPinInfos();
+#endif
+ int pins[4] = { pin1A, pin1B, pin2A, pin2B };
+ int compatibility = checkPermutations(4, pins, checkPeripheralPermutationCompatible);
+ if (compatibility<0) {
+ // no result!
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Bad combination!");
+#endif
+ return;
+ }
+
+ tccConfiguration tccConfs[4] = { getTCCChannelNr(pin1A, (compatibility>>0&0x01)==0x1),
+ getTCCChannelNr(pin1B, (compatibility>>1&0x01)==0x1),
+ getTCCChannelNr(pin2A, (compatibility>>2&0x01)==0x1),
+ getTCCChannelNr(pin2B, (compatibility>>3&0x01)==0x1) };
+
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.print("Found configuration: (score=");
+ Serial.print(scorePermutation(tccConfs, 4));
+ Serial.println(")");
+ printTCCConfiguration(tccConfs[0]);
+ printTCCConfiguration(tccConfs[1]);
+ printTCCConfiguration(tccConfs[2]);
+ printTCCConfiguration(tccConfs[3]);
+#endif
+
+ // attach pins to timer peripherals
+ attachTCC(tccConfs[0]); // in theory this can fail, but there is no way to signal it...
+ attachTCC(tccConfs[1]);
+ attachTCC(tccConfs[2]);
+ attachTCC(tccConfs[3]);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Attached pins...");
+#endif
+
+ // set up clock - Note: if we did this right it should be possible to get all TCC units synchronized?
+ // e.g. attach all the timers, start them, and then start the clock... but this would require API-changes in SimpleFOC...
+ configureSAMDClock();
+
+ // configure the TCC (waveform, top-value, pre-scaler = frequency)
+ configureTCC(tccConfs[0], pwm_frequency);
+ configureTCC(tccConfs[1], pwm_frequency);
+ configureTCC(tccConfs[2], pwm_frequency);
+ configureTCC(tccConfs[3], pwm_frequency);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Configured TCCs...");
+#endif
+
+ return; // Someone with a stepper-setup who can test it?
+}
+
+
+
+
+
+
+
+
+
+/**
+ * Configuring PWM frequency, resolution and alignment
+ * - BLDC driver - 6PWM setting
+ * - hardware specific
+ *
+ * SAMD21 will support up to 2 BLDC motors in 6-PWM:
+ * one on TCC0 using 3 of the channels 0,1,2 or 3
+ * one on TCC3 using 3 of the channels 0,1,2 or 3
+ * i.e. 6 out of 8 pins must be used, in the following high/low side pairs:
+ * WO[0] & WO[4] (high side & low side)
+ * WO[1] & WO[5]
+ * WO[2] & WO[6]
+ * WO[3] & WO[7]
+ *
+ * If you're on the Arduino Nano33 IoT, please see the Nano33 IoT pinout diagram to see which TCC0/WO[x]
+ * signal is on which pin of the Nano. You can drive 1 BLDC on TCC0. For other boards, consult their documentation.
+ *
+ *
+ * @param pwm_frequency - frequency in hertz - if applicable
+ * @param dead_zone duty cycle protection zone [0, 1] - both low and high side low - if applicable
+ * @param pinA_h pinA high-side bldc driver
+ * @param pinA_l pinA low-side bldc driver
+ * @param pinB_h pinA high-side bldc driver
+ * @param pinB_l pinA low-side bldc driver
+ * @param pinC_h pinA high-side bldc driver
+ * @param pinC_l pinA low-side bldc driver
+ *
+ * @return 0 if config good, -1 if failed
+ */
+int _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) {
+ // we want to use a TCC channel with 1 non-inverted and 1 inverted output for each phase, with dead-time insertion
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ printAllPinInfos();
+#endif
+ int compatibility = checkHardware6PWM(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
+ if (compatibility<0) {
+ compatibility = checkSoftware6PWM(pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l);
+ if (compatibility<0) {
+ // no result!
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Bad combination!");
+#endif
+ return -1;
+ }
+ }
+
+ tccConfiguration pinAh = getTCCChannelNr(pinA_h, (compatibility>>0&0x01)==0x1);
+ tccConfiguration pinAl = getTCCChannelNr(pinA_l, (compatibility>>1&0x01)==0x1);
+ tccConfiguration pinBh = getTCCChannelNr(pinB_h, (compatibility>>2&0x01)==0x1);
+ tccConfiguration pinBl = getTCCChannelNr(pinB_l, (compatibility>>3&0x01)==0x1);
+ tccConfiguration pinCh = getTCCChannelNr(pinC_h, (compatibility>>4&0x01)==0x1);
+ tccConfiguration pinCl = getTCCChannelNr(pinC_l, (compatibility>>5&0x01)==0x1);
+
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Found configuration: ");
+ printTCCConfiguration(pinAh);
+ printTCCConfiguration(pinAl);
+ printTCCConfiguration(pinBh);
+ printTCCConfiguration(pinBl);
+ printTCCConfiguration(pinCh);
+ printTCCConfiguration(pinCl);
+#endif
+
+ // attach pins to timer peripherals
+ bool allAttached = true;
+ allAttached |= attachTCC(pinAh); // in theory this can fail, but there is no way to signal it...
+ allAttached |= attachTCC(pinAl);
+ allAttached |= attachTCC(pinBh);
+ allAttached |= attachTCC(pinBl);
+ allAttached |= attachTCC(pinCh);
+ allAttached |= attachTCC(pinCl);
+ if (!allAttached)
+ return -1;
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Attached pins...");
+#endif
+ // set up clock - if we did this right it should be possible to get all TCC units synchronized?
+ // e.g. attach all the timers, start them, and then start the clock... but this would require API changes in SimpleFOC driver API
+ configureSAMDClock();
+
+ // configure the TCC(s)
+ configureTCC(pinAh, pwm_frequency, false, (pinAh.tcc.chaninfo==pinAl.tcc.chaninfo)?dead_zone:-1);
+ if ((pinAh.tcc.chaninfo!=pinAl.tcc.chaninfo))
+ configureTCC(pinAl, pwm_frequency, true, -1.0);
+ configureTCC(pinBh, pwm_frequency, false, (pinBh.tcc.chaninfo==pinBl.tcc.chaninfo)?dead_zone:-1);
+ if ((pinBh.tcc.chaninfo!=pinBl.tcc.chaninfo))
+ configureTCC(pinBl, pwm_frequency, true, -1.0);
+ configureTCC(pinCh, pwm_frequency, false, (pinCh.tcc.chaninfo==pinCl.tcc.chaninfo)?dead_zone:-1);
+ if ((pinCh.tcc.chaninfo!=pinCl.tcc.chaninfo))
+ configureTCC(pinCl, pwm_frequency, true, -1.0);
+#ifdef SIMPLEFOC_SAMD_DEBUG
+ Serial.println("Configured TCCs...");
+#endif
+
+ return 0;
+}
+
+
+/**
+ * Function setting the duty cycle to the pwm pin (ex. analogWrite())
+ * - Stepper driver - 2PWM setting
+ * - hardware specific
+ *
+ * @param dc_a duty cycle phase A [0, 1]
+ * @param dc_b duty cycle phase B [0, 1]
+ * @param pinA phase A hardware pin number
+ * @param pinB phase B hardware pin number
+ */
+void _writeDutyCycle2PWM(float dc_a, float dc_b, int pinA, int pinB) {
+ tccConfiguration* tccI = getTccPinConfiguration(pinA);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_a);
+ tccI = getTccPinConfiguration(pinB);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_b);
+ return;
+}
+
+/**
+ * Function setting the duty cycle to the pwm pin (ex. analogWrite())
+ * - BLDC driver - 3PWM setting
+ * - hardware specific
+ *
+ * @param dc_a duty cycle phase A [0, 1]
+ * @param dc_b duty cycle phase B [0, 1]
+ * @param dc_c duty cycle phase C [0, 1]
+ * @param pinA phase A hardware pin number
+ * @param pinB phase B hardware pin number
+ * @param pinC phase C hardware pin number
+ */
+void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, int pinA, int pinB, int pinC) {
+ tccConfiguration* tccI = getTccPinConfiguration(pinA);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_a);
+ tccI = getTccPinConfiguration(pinB);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_b);
+ tccI = getTccPinConfiguration(pinC);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_c);
+ return;
+}
+
+
+/**
+ * Function setting the duty cycle to the pwm pin (ex. analogWrite())
+ * - Stepper driver - 4PWM setting
+ * - hardware specific
+ *
+ * @param dc_1a duty cycle phase 1A [0, 1]
+ * @param dc_1b duty cycle phase 1B [0, 1]
+ * @param dc_2a duty cycle phase 2A [0, 1]
+ * @param dc_2b duty cycle phase 2B [0, 1]
+ * @param pin1A phase 1A hardware pin number
+ * @param pin1B phase 1B hardware pin number
+ * @param pin2A phase 2A hardware pin number
+ * @param pin2B phase 2B hardware pin number
+ */
+void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, int pin1A, int pin1B, int pin2A, int pin2B){
+ tccConfiguration* tccI = getTccPinConfiguration(pin1A);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_1a);
+ tccI = getTccPinConfiguration(pin2A);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_2a);
+ tccI = getTccPinConfiguration(pin1B);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_1b);
+ tccI = getTccPinConfiguration(pin2B);
+ writeSAMDDutyCycle(tccI->tcc.chaninfo, dc_2b);
+ return;
+}
+
+/**
+ * Function setting the duty cycle to the pwm pin (ex. analogWrite())
+ * - BLDC driver - 6PWM setting
+ * - hardware specific
+ *
+ * Note: dead-time must be setup in advance, so parameter "dead_zone" is ignored
+ * the low side pins are automatically driven by the SAMD DTI module, so it is enough to set the high-side
+ * duty cycle.
+ * No sanity checks are perfomed to ensure the pinA, pinB, pinC are the same pins you used in configure method...
+ * so use appropriately.
+ *
+ * @param dc_a duty cycle phase A [0, 1]
+ * @param dc_b duty cycle phase B [0, 1]
+ * @param dc_c duty cycle phase C [0, 1]
+ * @param dead_zone duty cycle protection zone [0, 1] - both low and high side low
+ * @param pinA_h phase A high-side hardware pin number
+ * @param pinA_l phase A low-side hardware pin number
+ * @param pinB_h phase B high-side hardware pin number
+ * @param pinB_l phase B low-side hardware pin number
+ * @param pinC_h phase C high-side hardware pin number
+ * @param pinC_l phase C low-side hardware pin number
+ *
+ */
+void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, float dead_zone, int pinA_h, int pinA_l, int pinB_h, int pinB_l, int pinC_h, int pinC_l){
+ tccConfiguration* tcc1 = getTccPinConfiguration(pinA_h);
+ tccConfiguration* tcc2 = getTccPinConfiguration(pinA_l);
+ if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
+ // low-side on a different pin of same TCC - do dead-time in software...
+ float ls = dc_a+(dead_zone*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1));
+ if (ls>1.0) ls = 1.0; // no off-time is better than too-short dead-time
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_a);
+ writeSAMDDutyCycle(tcc2->tcc.chaninfo, ls);
+ }
+ else
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_a); // dead-time is done is hardware, no need to set low side pin explicitly
+
+ tcc1 = getTccPinConfiguration(pinB_h);
+ tcc2 = getTccPinConfiguration(pinB_l);
+ if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
+ float ls = dc_b+(dead_zone*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1));
+ if (ls>1.0) ls = 1.0; // no off-time is better than too-short dead-time
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_b);
+ writeSAMDDutyCycle(tcc2->tcc.chaninfo, ls);
+ }
+ else
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_b);
+
+ tcc1 = getTccPinConfiguration(pinC_h);
+ tcc2 = getTccPinConfiguration(pinC_l);
+ if (tcc1->tcc.chaninfo!=tcc2->tcc.chaninfo) {
+ float ls = dc_c+(dead_zone*(SIMPLEFOC_SAMD_PWM_RESOLUTION-1));
+ if (ls>1.0) ls = 1.0; // no off-time is better than too-short dead-time
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_c);
+ writeSAMDDutyCycle(tcc2->tcc.chaninfo, ls);
+ }
+ else
+ writeSAMDDutyCycle(tcc1->tcc.chaninfo, dc_c);
+ return;
+}
+
+
+
+
+
+
+
+#endif
diff --git a/src/drivers/hardware_specific/samd21_wo_associations.h b/src/drivers/hardware_specific/samd21_wo_associations.h
new file mode 100644
index 00000000..3ab087e0
--- /dev/null
+++ b/src/drivers/hardware_specific/samd21_wo_associations.h
@@ -0,0 +1,131 @@
+
+
+struct wo_association {
+ EPortType port;
+ uint32_t pin;
+ ETCChannel tccE;
+ uint8_t woE;
+ ETCChannel tccF;
+ uint8_t woF;
+};
+
+
+
+#ifdef _SAMD21_
+
+
+
+#ifndef TCC3_CH0
+#define TCC3_CH0 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH1
+#define TCC3_CH1 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH2
+#define TCC3_CH2 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH3
+#define TCC3_CH3 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH4
+#define TCC3_CH4 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH5
+#define TCC3_CH5 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH6
+#define TCC3_CH6 NOT_ON_TIMER
+#endif
+#ifndef TCC3_CH7
+#define TCC3_CH7 NOT_ON_TIMER
+#endif
+#ifndef TC6_CH0
+#define TC6_CH0 NOT_ON_TIMER
+#endif
+#ifndef TC6_CH1
+#define TC6_CH1 NOT_ON_TIMER
+#endif
+#ifndef TC7_CH0
+#define TC7_CH0 NOT_ON_TIMER
+#endif
+#ifndef TC7_CH1
+#define TC7_CH1 NOT_ON_TIMER
+#endif
+
+
+/*
+ * For SAM D21 A/B/C/D Variant Devices and SAM DA1 A/B Variant Devices
+ * Good for SAMD2xE, SAMD2xG and SAMD2xJ devices. Other SAMD21s currently not supported in arduino anyway?
+ *
+ * Note: only the pins which have timers associated are listed in this table.
+ * You can use the values from g_APinDescription.ulPort and g_APinDescription.ulPin to find the correct row in the table.
+ *
+ * See Microchip Technology datasheet DS40001882F-page 30
+ */
+struct wo_association WO_associations[] = {
+
+ { PORTA, 0, TCC2_CH0, 0, NOT_ON_TIMER, 0},
+ { PORTA, 1, TCC2_CH1, 1, NOT_ON_TIMER, 0},
+ { PORTA, 2, NOT_ON_TIMER, 0, TCC3_CH0, 0},
+ { PORTA, 3, NOT_ON_TIMER, 0, TCC3_CH1, 1},
+ // PB04, PB05, PB06, PB07 - no timers
+ { PORTB, 8, TC4_CH0, 0, TCC3_CH6, 6},
+ { PORTB, 9, TC4_CH1, 1, TCC3_CH7, 7},
+ { PORTA, 4, TCC0_CH0, 0, TCC3_CH2, 2},
+ { PORTA, 5, TCC0_CH1, 1, TCC3_CH3, 3},
+ { PORTA, 6, TCC1_CH0, 0, TCC3_CH4, 4},
+ { PORTA, 7, TCC1_CH1, 1, TCC3_CH5, 5},
+ { PORTA, 8, TCC0_CH0, 0, TCC1_CH2, 2},
+ { PORTA, 9, TCC0_CH1, 1, TCC1_CH3, 3},
+ { PORTA, 10, TCC1_CH0, 0, TCC0_CH2, 2},
+ { PORTA, 11, TCC1_CH1, 1, TCC0_CH3, 3},
+ { PORTB, 10, TC5_CH0, 0, TCC0_CH4, 4},
+ { PORTB, 11, TC5_CH1, 1, TCC0_CH5, 5},
+ { PORTB, 12, TC4_CH0, 0, TCC0_CH6, 6},
+ { PORTB, 13, TC4_CH1, 1, TCC0_CH7, 7},
+ { PORTB, 14, TC5_CH0, 0, NOT_ON_TIMER, 0},
+ { PORTB, 15, TC5_CH1, 1, NOT_ON_TIMER, 0},
+ { PORTA, 12, TCC2_CH0, 0, TCC0_CH6, 6},
+ { PORTA, 13, TCC2_CH1, 1, TCC0_CH7, 7},
+ { PORTA, 14, TC3_CH0, 0, TCC0_CH4, 4},
+ { PORTA, 15, TC3_CH1, 1, TCC0_CH5, 5},
+ { PORTA, 16, TCC2_CH0, 0, TCC0_CH6, 6},
+ { PORTA, 17, TCC2_CH1, 1, TCC0_CH7, 7},
+ { PORTA, 18, TC3_CH0, 0, TCC0_CH2, 2},
+ { PORTA, 19, TC3_CH1, 1, TCC0_CH3, 3},
+ { PORTB, 16, TC6_CH0, 0, TCC0_CH4, 4},
+ { PORTB, 17, TC6_CH1, 1, TCC0_CH5, 5},
+ { PORTA, 20, TC7_CH0, 0, TCC0_CH6, 6},
+ { PORTA, 21, TC7_CH1, 1, TCC0_CH7, 7},
+ { PORTA, 22, TC4_CH0, 0, TCC0_CH4, 4},
+ { PORTA, 23, TC4_CH1, 1, TCC0_CH5, 5},
+ { PORTA, 24, TC5_CH0, 0, TCC1_CH2, 2},
+ { PORTA, 25, TC5_CH1, 1, TCC1_CH3, 3},
+ { PORTB, 22, TC7_CH0, 0, TCC3_CH0, 0},
+ { PORTB, 23, TC7_CH1, 1, TCC3_CH1, 1},
+ { PORTA, 27, NOT_ON_TIMER, 0, TCC3_CH6, 6},
+ { PORTA, 28, NOT_ON_TIMER, 0, TCC3_CH7, 7},
+ { PORTA, 30, TCC1_CH0, 0, TCC3_CH4, 4},
+ { PORTA, 31, TCC1_CH1, 1, TCC3_CH5, 5},
+ { PORTB, 30, TCC0_CH0, 0, TCC1_CH2, 2},
+ { PORTB, 31, TCC0_CH1, 1, TCC1_CH3, 3},
+ { PORTB, 0, TC7_CH0, 0, NOT_ON_TIMER, 0},
+ { PORTB, 1, TC7_CH1, 1, NOT_ON_TIMER, 0},
+ { PORTB, 2, TC6_CH0, 0, TCC3_CH2, 2},
+ { PORTB, 3, TC6_CH1, 1, TCC3_CH3, 3}
+};
+#define NUM_WO_ASSOCIATIONS 48
+
+wo_association ASSOCIATION_NOT_FOUND = { NOT_A_PORT, 0, NOT_ON_TIMER, 0, NOT_ON_TIMER, 0};
+
+
+struct wo_association& getWOAssociation(EPortType port, uint32_t pin) {
+ for (int i=0;iCTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC;
+//while (TCC0->SYNCBUSY.bit.CTRLB); // or while (TCC0->SYNCBUSY.reg);
+//int count = TCC0->COUNT.reg;
+
+
+/**
+ * Prints a table of pin assignments for your SAMD MCU. Very useful since the
+ * board pinout descriptions and variant.cpp are usually quite wrong, and this
+ * saves you hours of cross-referencing with the datasheet.
+ */
+void printAllPinInfos() {
+ Serial.println();
+ for (uint8_t pin=0;pin=0) {
+ Serial.print(info.tcc.tccn);
+ Serial.print("-");
+ Serial.print(info.tcc.chan);
+ Serial.print("[");
+ Serial.print(info.wo);
+ Serial.println("]");
+ }
+ else
+ Serial.println(" None");
+}
+
+
+