@@ -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+
356386ap3_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
464508ap3_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)
475519ap3_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
495533ap3_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;
0 commit comments