Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/rp2040/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ 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
# Software divide needed on rp2040 for rate calculation in i2c.c
select HAVE_SOFTWARE_DIVIDE_REQUIRED if MACH_RP2040

config BOARD_DIRECTORY
Expand Down
5 changes: 2 additions & 3 deletions src/rp2040/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,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 {
Expand Down
55 changes: 29 additions & 26 deletions src/rp2040/hard_pwm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -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))
Expand All @@ -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");

Expand All @@ -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;

Expand All @@ -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);
}
8 changes: 4 additions & 4 deletions src/rp2040/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,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;
}

Expand Down
Loading