1313#include "hardware/structs/iobank0.h" // iobank0_hw
1414#include "hardware/regs/resets.h" // RESETS_RESET_PWM_BITS
1515
16- #define MAX_PWM 255
16+ #define MAX_PWM (1<<15)
1717DECL_CONSTANT ("PWM_MAX" , MAX_PWM );
1818
1919struct gpio_pwm
20- gpio_pwm_setup (uint8_t pin , uint32_t cycle_time , uint8_t val ) {
20+ gpio_pwm_setup (uint8_t pin , uint32_t cycle_time , uint32_t val )
21+ {
2122 if (pin >= 30 )
2223 shutdown ("invalid gpio pin" );
2324
@@ -26,33 +27,32 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) {
2627 pwm_slice_hw_t * slice = & pwm_hw -> slice [(pin >> 1 ) & 0x7 ];
2728 uint8_t channel = pin & 1 ;
2829
29- // Map cycle_time to clock divider
3030 // The rp2040 has an 8.4 fractional divider, so we'll map the requested
3131 // cycle time into that. The cycle_time we receive from Klippy is in
3232 // relation to the crystal frequency and so we need to scale it up to match
3333 // the PWM clock.
34- // For better precision, we introduce a scale factor such that pclk * scale
35- // doesn't overflow. We then multiply by this scale factor at the beginning
36- // and divide by it at the end.
3734 uint32_t pclk = get_pclock_frequency (RESETS_RESET_PWM_BITS );
38- uint32_t scale = 1 << __builtin_clz (pclk );
39- uint32_t clock_mult = (scale * get_pclock_frequency (RESETS_RESET_PWM_BITS ))
40- / CONFIG_CLOCK_FREQ ;
41- uint32_t cycle_clocks = clock_mult * cycle_time ;
42- uint32_t div_int = cycle_clocks / MAX_PWM / scale ;
43- uint32_t div_frac = (cycle_clocks - div_int * MAX_PWM * scale ) * 16
44- / MAX_PWM / scale ;
35+ uint32_t mult_16ticks = 16ULL * pclk / CONFIG_CLOCK_FREQ ;
36+ uint64_t cycle_16ticks_64 = (uint64_t )cycle_time * mult_16ticks ;
37+ if (cycle_16ticks_64 > 0x80000000 )
38+ // Host never sends large cycle_time, so no need for elaborate handling
39+ cycle_16ticks_64 = 0x80000000 ;
40+ uint32_t cycle_16ticks = cycle_16ticks_64 ;
4541
46- // Clamp range of the divider
47- if (div_int > 255 ) {
48- div_int = 255 ;
49- div_frac = 15 ;
50- } else if (div_int < 1 ) {
51- div_int = 1 ;
52- div_frac = 0 ;
42+ // Convert requested cycle time (cycle_time/CLOCK_FREQ) to actual
43+ // cycle time (hwpwm_ticks*(pwm_div/16)/FREQ_SYS).
44+ uint32_t hwpwm_ticks , pwm_div , shift = 4 ;
45+ for (;;) {
46+ hwpwm_ticks = (cycle_16ticks + (1 << (shift - 1 ))) >> shift ;
47+ pwm_div = 1 << shift ;
48+ if (hwpwm_ticks <= UINT16_MAX )
49+ break ;
50+ shift += 1 ;
5351 }
54-
55- uint32_t pwm_div = div_int << 4 | div_frac ;
52+ if (pwm_div > 0xfff )
53+ pwm_div = 0xfff ;
54+ if (hwpwm_ticks < 2 )
55+ hwpwm_ticks = 2 ;
5656
5757 // Enable clock
5858 if (!is_enabled_pclock (RESETS_RESET_PWM_BITS ))
@@ -65,12 +65,12 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) {
6565 // as long as their cycle times are the same.
6666 if (!(slice -> csr & PWM_CH0_CSR_EN_BITS )) {
6767 slice -> div = pwm_div ;
68- slice -> top = MAX_PWM - 1 ;
68+ slice -> top = hwpwm_ticks - 1 ;
6969 slice -> ctr = PWM_CH0_CTR_RESET ;
7070 slice -> cc = PWM_CH0_CC_RESET ;
7171 slice -> csr = PWM_CH0_CSR_EN_BITS ;
7272 } else {
73- if (slice -> div != pwm_div )
73+ if (slice -> div != pwm_div || slice -> top != hwpwm_ticks - 1 )
7474 shutdown ("PWM pin has different cycle time from another in "
7575 "the same slice" );
7676
@@ -86,6 +86,7 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) {
8686
8787 struct gpio_pwm out ;
8888 out .reg = (void * )& slice -> cc ;
89+ out .hwpwm_ticks = hwpwm_ticks ;
8990 out .shift = channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB ;
9091 out .mask = channel ? PWM_CH0_CC_B_BITS : PWM_CH0_CC_A_BITS ;
9192
@@ -96,6 +97,8 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) {
9697}
9798
9899void
99- gpio_pwm_write (struct gpio_pwm g , uint32_t val ) {
100- hw_write_masked ((uint32_t * )g .reg , val << g .shift , g .mask );
100+ gpio_pwm_write (struct gpio_pwm g , uint32_t val )
101+ {
102+ uint32_t r = DIV_ROUND_CLOSEST (val * g .hwpwm_ticks , MAX_PWM );
103+ hw_write_masked ((uint32_t * )g .reg , r << g .shift , g .mask );
101104}
0 commit comments