Skip to content

Commit 1d2f969

Browse files
committed
Improve edge case handling for analogWrite and servoWrite
1 parent fc35ebc commit 1d2f969

File tree

3 files changed

+70
-31
lines changed

3 files changed

+70
-31
lines changed

cores/arduino/ard_sup/analog/ap3_analog.cpp

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -353,11 +353,45 @@ ap3_err_t ap3_change_channel(uint8_t padNumber)
353353
}
354354
}
355355

356+
357+
//**********************************************
358+
// ap3_pwm_output
359+
// - This function allows you to specify an arbitrary pwm output signal with a given frame width (fw) and time high (th).
360+
// - Due to contraints of the hardware th must be lesser than fw by at least 2.
361+
// - Furthermore fw must be at least 3 to see any high pulses
362+
//
363+
// This causes the most significant deviations for small values of fw. For example:
364+
//
365+
// th = 0, fw = 2 --> 0% duty cycle as expected
366+
// th = 1, fw = 2 --> 100% duty cycle --- expected 50%, so 50% error ---
367+
// th = 2, fw = 2 --> 100% duty cycle as expected
368+
//
369+
// th = 0, fw = 3 --> 0% duty cycle as expected
370+
// th = 1, fw = 3 --> 33% duty cycle as expected
371+
// th = 2, fw = 3 --> 100% duty cycle --- expected 66%, so 33% error ---
372+
// th = 3, fw = 3 --> 100% duty cycle as expected
373+
//
374+
// th = 0, fw = 4 --> 0% duty cycle as expected
375+
// th = 1, fw = 4 --> 25% duty cycle as expected
376+
// th = 2, fw = 4 --> 50% duty cycle as expected
377+
// th = 3, fw = 4 --> 100% duty cycle --- expected 75%, so 25% error ---
378+
// th = 4, fw = 4 --> 100% duty cycle as expected
379+
//
380+
// ...
381+
//
382+
// Then we conclude that for the case th == (fw - 1) the duty cycle will be 100% and
383+
// the percent error from the expected duty cycle will be 100/fw
384+
//**********************************************
385+
356386
ap3_err_t ap3_pwm_output(uint8_t pin, uint32_t th, uint32_t fw, uint32_t clk)
357387
{
358388
// handle configuration, if necessary
359389
ap3_err_t retval = AP3_OK;
360390

391+
if( fw > 0 ){ // reduce fw so that the user's desired value is the period
392+
fw--;
393+
}
394+
361395
ap3_gpio_pad_t pad = ap3_gpio_pin2pad(pin);
362396
if ((pad == AP3_GPIO_PAD_UNUSED) || (pad >= AP3_GPIO_MAX_PADS))
363397
{
@@ -402,9 +436,7 @@ ap3_err_t ap3_pwm_output(uint8_t pin, uint32_t th, uint32_t fw, uint32_t clk)
402436
}
403437
}
404438
else
405-
{
406-
const uint8_t n = 0; // use the zeroeth index into the options for any pd except 37 and 39
407-
439+
{ // Use the 0th index of the outcfg_tbl to select the functions
408440
timer = OUTCTIMN(ctx, 0);
409441
if (OUTCTIMB(ctx, 0))
410442
{
@@ -416,6 +448,21 @@ ap3_err_t ap3_pwm_output(uint8_t pin, uint32_t th, uint32_t fw, uint32_t clk)
416448
}
417449
}
418450

451+
// Ensure that th is not greater than the fw
452+
if(th > fw){
453+
th = fw;
454+
}
455+
456+
// Test for AM_HAL_CTIMER_OUTPUT_FORCE0 or AM_HAL_CTIMER_OUTPUT_FORCE1
457+
if(( th == 0 ) || ( fw == 0 ))
458+
{
459+
output = AM_HAL_CTIMER_OUTPUT_FORCE0;
460+
}
461+
else if( th == fw )
462+
{
463+
output = AM_HAL_CTIMER_OUTPUT_FORCE1;
464+
}
465+
419466
// Configure the pin
420467
am_hal_ctimer_output_config(timer,
421468
segment,
@@ -455,17 +502,14 @@ ap3_err_t ap3_pwm_output(uint8_t pin, uint32_t th, uint32_t fw, uint32_t clk)
455502

456503
am_hal_ctimer_start(timer, segment);
457504

458-
// todo: check fw and th -- if they are the same then change the mode to "force output high" to avoid noise
459-
// todo: handle the case where th==0 in a more elegant way (i.e. set mode to "force output low")
460-
461505
return AP3_OK;
462506
}
463507

464508
ap3_err_t analogWriteResolution(uint8_t res)
465509
{
466-
if (res > 15)
510+
if (res > 16)
467511
{
468-
_analogWriteBits = 15; // max out the resolution when this happens
512+
_analogWriteBits = 16; // max out the resolution when this happens
469513
return AP3_ERR;
470514
}
471515
_analogWriteBits = res;
@@ -475,28 +519,22 @@ ap3_err_t analogWriteResolution(uint8_t res)
475519
ap3_err_t analogWrite(uint8_t pin, uint32_t val)
476520
{
477521
// Determine the high time based on input value and the current resolution setting
478-
uint32_t fsv = (0x01 << _analogWriteBits); // full scale value for the current resolution setting
479-
val = val % fsv; // prevent excess
480-
uint32_t clk = AM_HAL_CTIMER_HFRC_12MHZ; // Use an Ambiq HAL provided value to select which clock
481-
//uint32_t fw = 32768; // Choose the frame width in clock periods (32768 -> ~ 350 Hz)
482-
// uint32_t th = (uint32_t)( (fw * val) / fsv );
483-
484-
if (val == 0)
485-
{
486-
val = 1; // todo: change this so that when val==0 we set the mode to "force output low"
487-
}
488-
if (val == fsv)
489-
{
490-
val -= 1; // todo: change this so that when val==fsv we just set the mode to "force output high"
522+
uint32_t fw = 0xFFFF; // Choose the frame width in clock periods (32767 -> ~ 180 Hz)
523+
if( val == ((0x01 << _analogWriteBits ) - 1) ){
524+
val = fw; // Enable FORCE1
525+
}else{
526+
val <<= (16 - _analogWriteBits); // Shift over the value to fill available resolution
491527
}
492-
return ap3_pwm_output(pin, val, fsv, clk);
528+
uint32_t clk = AM_HAL_CTIMER_HFRC_12MHZ; // Use an Ambiq HAL provided value to select which clock
529+
530+
return ap3_pwm_output(pin, val, fw, clk);
493531
}
494532

495533
ap3_err_t servoWriteResolution(uint8_t res)
496534
{
497-
if (res > 15)
535+
if (res > 16)
498536
{
499-
_servoWriteBits = 15; // max out the resolution when this happens
537+
_servoWriteBits = 16; // max out the resolution when this happens
500538
return AP3_ERR;
501539
}
502540
_servoWriteBits = res;

cores/arduino/ard_sup/ap3_analog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ bool power_adc_disable();
4545
uint16_t analogRead(uint8_t pinNumber);
4646
void analogReadResolution(uint8_t bits);
4747

48+
ap3_err_t ap3_pwm_output(uint8_t pin, uint32_t th, uint32_t fw, uint32_t clk);
4849
ap3_err_t analogWriteResolution(uint8_t res);
4950
ap3_err_t analogWrite(uint8_t pin, uint32_t val);
5051
ap3_err_t servoWriteResolution(uint8_t res);

libraries/Examples/examples/Example9_analogWrite/Example9_analogWrite.ino

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ void setup()
3232
Serial.println("SparkFun Arduino Apollo3 analogWrite() Example");
3333
Serial.printf("Compiled on %s, %s\n\n", __DATE__, __TIME__); // Look! You can use printf, if you so choose
3434

35-
analogWrite(46, 127); // analogWrite uses a high frequency counter/timer module to generate a PWM value with f ~= 350 Hz
36-
analogWriteResolution(15); // you can increase the resolution of the write from the default (8-bit, 0-255) to up to 15 bits (0-32767)
37-
analogWrite(37, 16383); // subsequent cals to analogWrite use the new resolution, so both this call and the original have a 50% duty cycle, or 3.3/2 V
35+
analogWrite(46, 127); // analogWrite uses a high frequency counter/timer module to generate a PWM value with f ~= 180 Hz
36+
analogWriteResolution(16); // you can increase the resolution of the write from the default (8-bit, 0-255) to up to 16 bits (0-65535)
37+
analogWrite(37, 32767); // subsequent cals to analogWrite use the new resolution, so both this call and the original have a 50% duty cycle, or 3.3/2 V
3838

3939
servoWrite(44, 127); // The servoWrite function produces a PWM signal with a pulse width between 1 and 2 ms at 500 Hz
40-
servoWriteResolution(15); // You can also increase the servoWrite() resolution
41-
servoWrite(47, 16383); // This and the above write should both produce 1.5 ms wide pulses, though using different resolutions
40+
servoWriteResolution(16); // You can also increase the servoWrite() resolution
41+
servoWrite(47, 32767); // This and the above write should both produce 1.5 ms wide pulses, though using different resolutions
4242

4343
delay(5000);
4444
}
@@ -56,8 +56,8 @@ void loop()
5656
servoWrite(44, val);
5757
servoWrite(47, val);
5858

59-
val += 5 * sign;
60-
if (val > 32767)
59+
val += 10 * sign;
60+
if (val > 65535)
6161
{
6262
sign = -1;
6363
}

0 commit comments

Comments
 (0)