@@ -155,26 +155,26 @@ public double publishServoEvent(ServoControl servo, Integer eventType, Double cu
155
155
// want these to be as small/large as possible without hitting the hard stop
156
156
// for max range. You'll have to tweak them as necessary to match the servos
157
157
// you have!
158
- //
159
- public final static int SERVOMIN = 150 ; // this
160
- // is
161
- // the
162
- // 'minimum'
163
- // pulse
164
- // length count (out of 4096)
165
- public final static int SERVOMAX = 600 ; // this
166
- // is
167
- // the
168
- // 'maximum'
169
- // pulse
170
- // length count (out of 4096)
158
+ public int SERVOMIN = 150 ; // this is the 'minimum' pulse length count
159
+ // (out of 4096)
160
+ // @60Hz this equates to 610uS
161
+ public int SERVOMAX = 600 ; // this is the 'maximum' pulse length count
162
+ // (out of 4096)
163
+ // @60Hz this equates to 2441uS
164
+
165
+ // Servo min and max Pulse width
166
+ final static int servoMinPulseWidth = 500 ; // in micro seconds
167
+ final static int servoMaxPulseWidth = 2500 ; // in micro seconds
171
168
172
169
transient public I2CController controller ;
173
170
174
171
// Constant for default PWM freqency
175
172
private static int defaultPwmFreq = 60 ;
176
173
final static int minPwmFreq = 24 ;
177
- final static int maxPwmFreq = 1526 ;
174
+ // Servos us a PWM signal with a pulse width between 500uS and 2500uS.
175
+ // Allowing for 500uS the latest digital servos gives us a max frequence of
176
+ // 333 Hz
177
+ final static int maxPwmFreq = 333 ;
178
178
179
179
int pwmFreq ;
180
180
boolean pwmFreqSet = false ;
@@ -216,16 +216,15 @@ public double publishServoEvent(ServoControl servo, Integer eventType, Double cu
216
216
public static final int PCA9685_LED0_OFF_H = 0x09 ; // First LED addressHigh
217
217
218
218
public static final int PCA9685_ALL_LED_OFF_H = 0xFD ; // All call i2c address
219
- // ( Used for shutdown
220
- // of all pwm )
219
+ // ( Used for shutdown of all pwm )
221
220
public static final int PCA9685_TURN_ALL_LED_OFF = 0x10 ; // Command to turn
222
- // all LED off stop
223
- // pwm )
221
+ // all LED off stop pwm )
224
222
225
223
// public static final int PWM_FREQ = 60; // default frequency for servos
226
224
public static final float osc_clock = 25000000 ; // clock frequency of the
227
225
// internal clock
228
226
public static final float precision = 4096 ; // pwm_precision
227
+ float freqAdjust = (float ) 0.9 ;
229
228
230
229
/**
231
230
* i2c controller
@@ -360,11 +359,37 @@ public void setPWM(String pinLabel, Integer pulseWidthOn, Integer pulseWidthOff)
360
359
byte [] buffer = { (byte ) (PCA9685_LED0_ON_L + (pin * 4 )), (byte ) (pulseWidthOn & 0xff ), (byte ) (pulseWidthOn >> 8 ), (byte ) (pulseWidthOff & 0xff ),
361
360
(byte ) (pulseWidthOff >> 8 ) };
362
361
log .debug ("Writing pin {}, pulesWidthOn {}, pulseWidthOff {}" , pin , pulseWidthOn , pulseWidthOff );
363
- if (controller != null ) {
364
- controller .i2cWrite (this , Integer .parseInt (deviceBus ), Integer .decode (deviceAddress ), buffer , buffer .length );
362
+ controller .i2cWrite (this , Integer .parseInt (deviceBus ), Integer .decode (deviceAddress ), buffer , buffer .length );
363
+ }
364
+
365
+ /**
366
+ * adjust the inbuilt oscilator frequency reference. The PCA9685 inbuilt
367
+ * oscilator is listed as 25MHz in the datasheets However, it has found to be
368
+ * a little different and is different between batches This allows us to
369
+ * adjust the base frequency we use for out timings so the output pulses and
370
+ * PWM frequencies are more accurate. This will affect the positioning of
371
+ * servos, center and even the upper and lower limits.
372
+ *
373
+ * @param adjustment
374
+ */
375
+ public void setFreqAdjust (float adjustment ) {
376
+ if (adjustment < 0.5 ) {
377
+ freqAdjust = (float ) 0.5 ;
378
+ } else if (adjustment > 1.5 ) {
379
+ freqAdjust = (float ) 1.5 ;
365
380
} else {
366
- error ( "controller not set!" ) ;
381
+ freqAdjust = adjustment ;
367
382
}
383
+ log .info ("pwm frequency adjust %s, resulting a frequency of %s MHz." , freqAdjust , (freqAdjust * osc_clock ));
384
+ }
385
+
386
+ /**
387
+ * returns the current Frequency Adjustment value.
388
+ *
389
+ * @return
390
+ */
391
+ public float getFreqAdjust () {
392
+ return freqAdjust ;
368
393
}
369
394
370
395
/**
@@ -407,7 +432,19 @@ public void setPWMFreq(String pin, Integer hz) { // Analog servos run at ~60
407
432
// Multiplying with factor 0.9 to correct the frequency
408
433
// See
409
434
// https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/issues/11
410
- prescale_value = Math .round (0.9 * osc_clock / precision / hz ) - 1 ;
435
+ // The adjustment required varies from batch to batch so is now a variable
436
+ // that can be adjusted.
437
+ prescale_value = Math .round (freqAdjust * osc_clock / precision / hz ) - 1 ;
438
+
439
+ // When the PWMfreq is changed, the SERVOMIN and SERVOMAX need to be
440
+ // updated as well,
441
+ // otherwise if the PWMfreq is increased, the pulse width will be
442
+ // decreased and could
443
+ // damage the servos by sending pulses that are too short.
444
+ // If the PWMfreq is decrease, then the pulse width would be increased,
445
+ // again potentially damaging servos.
446
+ SERVOMIN = (servoMinPulseWidth * (int ) precision * (int ) hz ) / 1000000 ;
447
+ SERVOMAX = (servoMaxPulseWidth * (int ) precision * (int ) hz ) / 1000000 ;
411
448
}
412
449
413
450
log .info ("pwm frequency {} hz, prescale_value calculated to %s" , hz , prescale_value );
@@ -507,7 +544,8 @@ public void onServoWriteMicroseconds(ServoControl servo, int uS) {
507
544
508
545
int pin = getAddress (servo .getPin ());
509
546
// 1000 ms => 150, 2000 ms => 600
510
- int pulseWidthOff = (int ) (uS * 0.45 ) - 300 ;
547
+ // This value is calculated based on the pwmFreq setting.
548
+ int pulseWidthOff = (uS * (int ) precision * pwmFreq ) / 1000000 ;
511
549
// since pulseWidthOff can be larger than > 256 it needs to be
512
550
// sent as 2 bytes
513
551
log .debug ("servoWriteMicroseconds {} deviceAddress {} pin {} pulse {}" , servo .getName (), deviceAddress , pin , pulseWidthOff );
@@ -1025,18 +1063,18 @@ public String publishServoStarted(String name) {
1025
1063
public String publishServoStopped (String name ) {
1026
1064
return name ;
1027
1065
}
1028
-
1066
+
1029
1067
@ Override
1030
1068
public Adafruit16CServoDriverConfig getConfig () {
1031
- super . getConfig ();
1032
- Adafruit16CServoDriverConfig config = (Adafruit16CServoDriverConfig )super .getConfig ();
1069
+
1070
+ Adafruit16CServoDriverConfig config = (Adafruit16CServoDriverConfig ) super .getConfig ();
1033
1071
// FIXME remove member vars use config directly
1034
1072
config .controller = controllerName ;
1035
1073
config .deviceBus = deviceBus ;
1036
1074
config .deviceAddress = deviceAddress ;
1037
1075
return config ;
1038
1076
}
1039
-
1077
+
1040
1078
@ Override
1041
1079
public Adafruit16CServoDriverConfig apply (Adafruit16CServoDriverConfig c ) {
1042
1080
super .apply (c );
@@ -1047,16 +1085,16 @@ public Adafruit16CServoDriverConfig apply(Adafruit16CServoDriverConfig c) {
1047
1085
log .error ("attaching controller failed" , e );
1048
1086
}
1049
1087
}
1050
-
1051
- // lame - this shouldn't be "copied" over - everything should just simply use config.deviceAddress
1088
+ // lame - this shouldn't be "copied" over - everything should just simply
1089
+ // use config.deviceAddress
1052
1090
if (config .deviceAddress != null ) {
1053
1091
deviceAddress = config .deviceAddress ;
1054
1092
}
1055
1093
1056
1094
if (config .deviceBus != null ) {
1057
1095
deviceBus = config .deviceBus ;
1058
1096
}
1059
-
1097
+
1060
1098
return c ;
1061
1099
}
1062
1100
0 commit comments