From b719efc7ee8e16f772fc26adf31305175d1d43f2 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 7 Mar 2025 16:11:32 -0800 Subject: [PATCH] Added function to allow for writing full 16 bit values to analog output. The original analog write function limits you to 1000 steps of resolution which while easy to use and understand, was not enough for some applications. --- README.md | 6 +++++ src/SAMD21turboPWM.cpp | 59 +++++++++++++++++++++++++++++++----------- src/SAMD21turboPWM.h | 3 +++ 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index af5460e..ebf6523 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ MaxSteps is 0xFFFFFF for (24-bits) timers 0 and 1, and 0xFFFF for (16 bits) time ```pwm.analogWrite([pin number], [0-1000]);``` +**Start PWM on a pin with a 16 bit duty cycle:** + +```pwm.analogWrite([pin number], [0-65535]);``` + +Note that the duty cycle will be limited to the number of steps specified with the `timer` function. It is also limited to 16 bits. + **Enable/disable a timer:** ```pwm.enable([0, 1, 2], [true, false]);``` diff --git a/src/SAMD21turboPWM.cpp b/src/SAMD21turboPWM.cpp index 171f07d..094fdbf 100644 --- a/src/SAMD21turboPWM.cpp +++ b/src/SAMD21turboPWM.cpp @@ -121,30 +121,33 @@ int TurboPWM::timer(unsigned int timerNumber, unsigned int TCCDiv, unsigned long int TurboPWM::analogWrite(int pin, unsigned int dutyCycle) { // Check if an acceptable pin is used - unsigned int i; - for (i = 0; i < pinTableSize; i++) { - if (pinTable[i].arduinoPin == pin) { - break; - } - } - if (i >= pinTableSize || pin < 0) { + if (checkPin(pin) == 0) { return 0; } - // Enable a SAMD21 pin as multiplexed and connect it to a pin using the port multiplexer - PORT->Group[pinTable[pin].port].PINCFG[pinTable[pin].samd21Pin].bit.PMUXEN = 1; - PORT->Group[pinTable[pin].port].PMUX[pinTable[pin].samd21Pin >> 1].reg |= pinTable[pin].pMux; - // Clamp dutycycle to the maximum duty cycle set in the header file; duty cycle will be (dutyCycle / _maxDutyCycle) * 100% if (dutyCycle > _maxDutyCycle) { dutyCycle = _maxDutyCycle; } - // Set duty cycle - *(RwReg*)pinTable[pin].REG_TCCx_CCBy = (timerTable[pinTable[pin].timer].steps * dutyCycle) / _maxDutyCycle; - while (timerTable[pinTable[pin].timer].TCCx->SYNCBUSY.vec.CCB); + dutyCycle = (timerTable[pinTable[pin].timer].steps * dutyCycle) / _maxDutyCycle; + + return writeOutput(pin, dutyCycle); +} + + +int TurboPWM::analogWrite16Bit(int pin, uint16_t dutyCycle) { + // Check if an acceptable pin is used + if (checkPin(pin) == 0) { + return 0; + } + + // Clamp dutycycle to the maximum number of steps for the given timer: + if (dutyCycle > timerTable[pinTable[pin].timer].steps) { + dutyCycle = timerTable[pinTable[pin].timer].steps; + } - return 1; + return writeOutput(pin, dutyCycle); } int TurboPWM::enable(unsigned int timerNumber, bool enabled) { @@ -184,3 +187,29 @@ float TurboPWM::frequency(unsigned int timerNumber) { } return (static_cast(VARIANT_MCK) * PLL96M) / (fastDivider * _GCLKDiv * timerTable[timerNumber].TCCDiv * timerTable[timerNumber].steps); } + +bool TurboPWM::checkPin(int pin){ + // Check if an acceptable pin is used + unsigned int i; + for (i = 0; i < pinTableSize; i++) { + if (pinTable[i].arduinoPin == pin) { + return 1; + } + } + if (i >= pinTableSize || pin < 0) { + return 0; + } + return 0; +} + +int TurboPWM::writeOutput(int pin, uint16_t dutyCycle){ + // Enable a SAMD21 pin as multiplexed and connect it to a pin using the port multiplexer + PORT->Group[pinTable[pin].port].PINCFG[pinTable[pin].samd21Pin].bit.PMUXEN = 1; + PORT->Group[pinTable[pin].port].PMUX[pinTable[pin].samd21Pin >> 1].reg |= pinTable[pin].pMux; + + // Set duty cycle + *(RwReg*)pinTable[pin].REG_TCCx_CCBy = dutyCycle; + while (timerTable[pinTable[pin].timer].TCCx->SYNCBUSY.vec.CCB); + + return 1; +} \ No newline at end of file diff --git a/src/SAMD21turboPWM.h b/src/SAMD21turboPWM.h index 528b782..76c261e 100644 --- a/src/SAMD21turboPWM.h +++ b/src/SAMD21turboPWM.h @@ -8,12 +8,15 @@ class TurboPWM { void setClockDivider(unsigned int GCLKDiv, bool turbo); int timer(unsigned int timernumber, unsigned int TCCDiv, unsigned long long int steps, bool fastPWM); int analogWrite(int pin, unsigned int dutyCycle); + int analogWrite16Bit(int pin, uint16_t dutyCycle); int enable(unsigned int timerNumber, bool enabled); float frequency(unsigned int timerNumber); private: unsigned int _GCLKDiv = 1; // Main clock divider: 1 to 255 for both TCC0 and TCC1 bool _turbo = false; // False for 48MHz clock, true for 96MHz clock const unsigned int _maxDutyCycle = 1000; // The maximum duty cycle number; duty cycle will be (dutyCycle / _maxDutyCycle) * 100% + bool checkPin(int pin); + int writeOutput(int pin, uint16_t dutyCycle); }; //Table for looking up and storing values for TCCx