From d7e0ec9236bb68a689ce618f4a39c147581b2e31 Mon Sep 17 00:00:00 2001 From: Walt Sorensen Date: Tue, 29 Oct 2019 10:03:08 -0600 Subject: [PATCH 1/3] Simplify and improve the writeMicroseconds() calculations Checked the calculations by hand Use Datasheet 7.3.5 Equation 1 for frequency from prescale https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf Frequency = (ClockFrequency/(PreScale+1))*(1/BitRate) and from the rest of the calculation us per period = PulseLength/Frequency us per bit = us per period/BitRate we can combine to get us per bit = (PulseLength/((ClockFrequency/(PreScale+1))*(1/BitRate)))*(1/BitRate) This can be simplified to (PulseLength (PreScale + 1))/ClockFrequency this formula was implemented in proposed code checked by setting chip frequency to 60Hz read prescale = 105 us per bit = 4.069 with 25Mhz (a PWM frequency of about 57.3hz) Choosing the FREQUENCY_CALIBRATED constant listed at 104.3% higher or 26.075MHz we get us per bit = 4.1035 or a PWM frequency of about 60.056Hz much closer to the set 60Hz Overall this change makes less calculation steps, and uses fewer variables. The change should be faster and more accurate to the intended results. --- Adafruit_PWMServoDriver.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/Adafruit_PWMServoDriver.cpp b/Adafruit_PWMServoDriver.cpp index 4c614fe..b84f141 100755 --- a/Adafruit_PWMServoDriver.cpp +++ b/Adafruit_PWMServoDriver.cpp @@ -296,24 +296,14 @@ void Adafruit_PWMServoDriver::writeMicroseconds(uint8_t num, uint16_t Microsecon double pulselength; pulselength = 1000000; // 1,000,000 us per second - // Read prescale and convert to frequency + // Read prescale double prescale = Adafruit_PWMServoDriver::readPrescale(); - prescale += 1; - uint32_t freq = 25000000; // Chip frequency is 25MHz - freq /= prescale; - freq /= 4096; // 12 bits of resolution - - #ifdef ENABLE_DEBUG_OUTPUT - Serial.print(freq); Serial.println(" Calculated PCA9685 chip PWM Frequency"); - #endif - - pulselength /= freq; // us per period from PCA9685 chip PWM Frequency using prescale reverse frequency calc + Serial.print(prescale); Serial.println(" PCA9685 chip prescale"); - #ifdef ENABLE_DEBUG_OUTPUT - Serial.print(pulselength); Serial.println(" us per period"); - #endif - - pulselength /= 4096; // 12 bits of resolution + // Calculate the pulse for PWM based on Equation 1 from the datasheet section 7.3.5 + prescale += 1; + pulselength *= prescale; + pulselength /= FREQUENCY_CALIBRATED; #ifdef ENABLE_DEBUG_OUTPUT Serial.print(pulselength); Serial.println(" us per bit"); From 942f457cb43ed847824e56bbe8ed97246d60f866 Mon Sep 17 00:00:00 2001 From: Walt Sorensen Date: Tue, 29 Oct 2019 10:13:10 -0600 Subject: [PATCH 2/3] Wrap print statement in #ifdef ENABLE_DEBUG_OUTPUT debug statements should be Wrapped in `#ifdef ENABLE_DEBUG_OUTPUT` --- Adafruit_PWMServoDriver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Adafruit_PWMServoDriver.cpp b/Adafruit_PWMServoDriver.cpp index b84f141..bb31329 100755 --- a/Adafruit_PWMServoDriver.cpp +++ b/Adafruit_PWMServoDriver.cpp @@ -298,7 +298,10 @@ void Adafruit_PWMServoDriver::writeMicroseconds(uint8_t num, uint16_t Microsecon // Read prescale double prescale = Adafruit_PWMServoDriver::readPrescale(); + + #ifdef ENABLE_DEBUG_OUTPUT Serial.print(prescale); Serial.println(" PCA9685 chip prescale"); + #endif // Calculate the pulse for PWM based on Equation 1 from the datasheet section 7.3.5 prescale += 1; From 33ec0da54b4bbee46b9f428497502a877e79de07 Mon Sep 17 00:00:00 2001 From: Walt Sorensen Date: Tue, 29 Oct 2019 12:29:38 -0600 Subject: [PATCH 3/3] Change prescale from double to uint16_t Prescale values are integers 3 to 255 with a maximum of 255+1 for the calculations. --- Adafruit_PWMServoDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adafruit_PWMServoDriver.cpp b/Adafruit_PWMServoDriver.cpp index bb31329..f3170ac 100755 --- a/Adafruit_PWMServoDriver.cpp +++ b/Adafruit_PWMServoDriver.cpp @@ -297,7 +297,7 @@ void Adafruit_PWMServoDriver::writeMicroseconds(uint8_t num, uint16_t Microsecon pulselength = 1000000; // 1,000,000 us per second // Read prescale - double prescale = Adafruit_PWMServoDriver::readPrescale(); + uint16_t prescale = Adafruit_PWMServoDriver::readPrescale(); #ifdef ENABLE_DEBUG_OUTPUT Serial.print(prescale); Serial.println(" PCA9685 chip prescale");