diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index bef33e65fc..e69c398959 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -14,8 +14,6 @@ config RPXXXX_SELECT select HAVE_GPIO_HARD_PWM select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST - # Software divide needed on rp2040 in spi rate, i2c rate, hard_pwm rate - select HAVE_SOFTWARE_DIVIDE_REQUIRED if MACH_RP2040 config BOARD_DIRECTORY string diff --git a/src/rp2040/gpio.h b/src/rp2040/gpio.h index 0dd393bfed..6a7d17678b 100644 --- a/src/rp2040/gpio.h +++ b/src/rp2040/gpio.h @@ -21,10 +21,9 @@ uint8_t gpio_in_read(struct gpio_in g); struct gpio_pwm { void *reg; - uint8_t shift; - uint32_t mask; + uint32_t hwpwm_ticks, shift, mask; }; -struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val); +struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint32_t val); void gpio_pwm_write(struct gpio_pwm g, uint32_t val); struct gpio_adc { diff --git a/src/rp2040/hard_pwm.c b/src/rp2040/hard_pwm.c index 8b7c96b098..125ea11f68 100644 --- a/src/rp2040/hard_pwm.c +++ b/src/rp2040/hard_pwm.c @@ -13,11 +13,12 @@ #include "hardware/structs/iobank0.h" // iobank0_hw #include "hardware/regs/resets.h" // RESETS_RESET_PWM_BITS -#define MAX_PWM 255 +#define MAX_PWM (1<<15) DECL_CONSTANT("PWM_MAX", MAX_PWM); struct gpio_pwm -gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) { +gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint32_t val) +{ if(pin >= 30) shutdown("invalid gpio pin"); @@ -26,33 +27,32 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) { pwm_slice_hw_t * slice = &pwm_hw->slice[(pin >> 1) & 0x7]; uint8_t channel = pin & 1; - // Map cycle_time to clock divider // The rp2040 has an 8.4 fractional divider, so we'll map the requested // cycle time into that. The cycle_time we receive from Klippy is in // relation to the crystal frequency and so we need to scale it up to match // the PWM clock. - // For better precision, we introduce a scale factor such that pclk * scale - // doesn't overflow. We then multiply by this scale factor at the beginning - // and divide by it at the end. uint32_t pclk = get_pclock_frequency(RESETS_RESET_PWM_BITS); - uint32_t scale = 1 << __builtin_clz(pclk); - uint32_t clock_mult = (scale * get_pclock_frequency(RESETS_RESET_PWM_BITS)) - / CONFIG_CLOCK_FREQ; - uint32_t cycle_clocks = clock_mult * cycle_time; - uint32_t div_int = cycle_clocks / MAX_PWM / scale; - uint32_t div_frac = (cycle_clocks - div_int * MAX_PWM * scale) * 16 - / MAX_PWM / scale; + uint32_t mult_16ticks = 16ULL * pclk / CONFIG_CLOCK_FREQ; + uint64_t cycle_16ticks_64 = (uint64_t)cycle_time * mult_16ticks; + if (cycle_16ticks_64 > 0x80000000) + // Host never sends large cycle_time, so no need for elaborate handling + cycle_16ticks_64 = 0x80000000; + uint32_t cycle_16ticks = cycle_16ticks_64; - // Clamp range of the divider - if(div_int > 255) { - div_int = 255; - div_frac = 15; - } else if(div_int < 1) { - div_int = 1; - div_frac = 0; + // Convert requested cycle time (cycle_time/CLOCK_FREQ) to actual + // cycle time (hwpwm_ticks*(pwm_div/16)/FREQ_SYS). + uint32_t hwpwm_ticks, pwm_div, shift = 4; + for (;;) { + hwpwm_ticks = (cycle_16ticks + (1 << (shift-1))) >> shift; + pwm_div = 1 << shift; + if (hwpwm_ticks <= UINT16_MAX) + break; + shift += 1; } - - uint32_t pwm_div = div_int << 4 | div_frac; + if (pwm_div > 0xfff) + pwm_div = 0xfff; + if (hwpwm_ticks < 2) + hwpwm_ticks = 2; // Enable clock 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) { // as long as their cycle times are the same. if(!(slice->csr & PWM_CH0_CSR_EN_BITS)) { slice->div = pwm_div; - slice->top = MAX_PWM - 1; + slice->top = hwpwm_ticks - 1; slice->ctr = PWM_CH0_CTR_RESET; slice->cc = PWM_CH0_CC_RESET; slice->csr = PWM_CH0_CSR_EN_BITS; } else { - if (slice->div != pwm_div) + if (slice->div != pwm_div || slice->top != hwpwm_ticks - 1) shutdown("PWM pin has different cycle time from another in " "the same slice"); @@ -86,6 +86,7 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) { struct gpio_pwm out; out.reg = (void*)&slice->cc; + out.hwpwm_ticks = hwpwm_ticks; out.shift = channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB; out.mask = channel ? PWM_CH0_CC_B_BITS : PWM_CH0_CC_A_BITS; @@ -96,6 +97,8 @@ gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val) { } void -gpio_pwm_write(struct gpio_pwm g, uint32_t val) { - hw_write_masked((uint32_t*)g.reg, val << g.shift, g.mask); +gpio_pwm_write(struct gpio_pwm g, uint32_t val) +{ + uint32_t r = DIV_ROUND_CLOSEST(val * g.hwpwm_ticks, MAX_PWM); + hw_write_masked((uint32_t*)g.reg, r << g.shift, g.mask); } diff --git a/src/rp2040/i2c.c b/src/rp2040/i2c.c index 09bb174fd1..96a1a9c6c7 100644 --- a/src/rp2040/i2c.c +++ b/src/rp2040/i2c.c @@ -101,10 +101,14 @@ i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) // See `i2c_set_baudrate` in the Pico SDK `hardware_i2c/i2c.c` file // for details on the calculations here. - if (rate > 1000000) - rate = 1000000; // Clamp the rate to 1Mbps - uint32_t period = (pclk + rate / 2) / rate; - uint32_t lcnt = period * 3 / 5; + uint32_t period; + if (rate >= 1000000) + period = DIV_ROUND_CLOSEST(pclk, 1000000); + else if (rate >= 400000) + period = DIV_ROUND_CLOSEST(pclk, 400000); + else + period = DIV_ROUND_CLOSEST(pclk, 100000); + uint32_t lcnt = DIV_ROUND_CLOSEST(period * ((3<<16) / 5), 1<<16); // 60% uint32_t hcnt = period - lcnt; uint32_t sda_tx_hold_count = ((pclk * 3) / 10000000) + 1; diff --git a/src/rp2040/spi.c b/src/rp2040/spi.c index 1baf0aa895..6f2de9182e 100644 --- a/src/rp2040/spi.c +++ b/src/rp2040/spi.c @@ -88,15 +88,15 @@ spi_setup(uint32_t bus, uint8_t mode, uint32_t rate) struct spi_config res = {spi_bus[bus].spi, 0, 0}; - uint8_t prescale; + uint32_t prescale; for (prescale = 2; prescale <= 254; prescale += 2) { - if (pclk < (prescale + 2) * 256 * rate) + if ((prescale + 2) * 256 * rate > pclk) break; } - uint8_t postdiv; + uint32_t postdiv; for (postdiv = 255; postdiv > 0; --postdiv) { - if ((pclk / (prescale * postdiv)) > rate) + if (prescale * postdiv * rate < pclk) break; }