From 59fe8782415718b03eb89dd00f25ed1d6cb1ac32 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Tue, 22 Jun 2021 21:10:12 -0400 Subject: [PATCH] rp2040: Add initial adc support Signed-off-by: Kevin O'Connor --- klippy/extras/temperature_mcu.py | 4 ++ src/rp2040/Kconfig | 1 + src/rp2040/Makefile | 2 +- src/rp2040/adc.c | 88 ++++++++++++++++++++++++++++++++ src/rp2040/gpio.h | 8 +++ src/rp2040/main.c | 8 ++- 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/rp2040/adc.c diff --git a/klippy/extras/temperature_mcu.py b/klippy/extras/temperature_mcu.py index 4643d54e..188ad316 100644 --- a/klippy/extras/temperature_mcu.py +++ b/klippy/extras/temperature_mcu.py @@ -62,6 +62,7 @@ class PrinterTemperatureMCU: self.mcu_type = mcu.get_constants().get("MCU", "") # Run MCU specific configuration cfg_funcs = [ + ('rp2040', self.config_rp2040), ('sam3', self.config_sam3), ('sam4', self.config_sam4), ('samd21', self.config_samd21), ('samd51', self.config_samd51), ('stm32f1', self.config_stm32f1), ('stm32f2', self.config_stm32f2), @@ -89,6 +90,9 @@ class PrinterTemperatureMCU: def config_unknown(self): raise self.printer.config_error("MCU temperature not supported on %s" % (self.mcu_type,)) + def config_rp2040(self): + self.slope = 3.3 / -0.001721 + self.base_temperature = self.calc_base(27., 0.706 / 3.3) def config_sam3(self): self.slope = 3.3 / .002650 self.base_temperature = self.calc_base(27., 0.8 / 3.3) diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index 6375a7ff..3edebc47 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -6,6 +6,7 @@ config RP2040_SELECT bool default y select HAVE_GPIO + select HAVE_GPIO_ADC select HAVE_GPIO_BITBANGING select HAVE_STRICT_TIMING diff --git a/src/rp2040/Makefile b/src/rp2040/Makefile index 1491b419..4ffecbd1 100644 --- a/src/rp2040/Makefile +++ b/src/rp2040/Makefile @@ -13,7 +13,7 @@ CFLAGS_klipper.elf += -T $(OUT)src/rp2040/rp2040_link.ld $(OUT)klipper.elf: $(OUT)stage2.o $(OUT)src/rp2040/rp2040_link.ld # Add source files -src-y += rp2040/main.c rp2040/gpio.c generic/crc16_ccitt.c +src-y += rp2040/main.c rp2040/gpio.c rp2040/adc.c generic/crc16_ccitt.c src-y += generic/armcm_boot.c generic/armcm_irq.c generic/armcm_reset.c src-y += generic/timer_irq.c rp2040/timer.c src-$(CONFIG_SERIAL) += rp2040/serial.c generic/serial_irq.c diff --git a/src/rp2040/adc.c b/src/rp2040/adc.c new file mode 100644 index 00000000..2daf380a --- /dev/null +++ b/src/rp2040/adc.c @@ -0,0 +1,88 @@ +// ADC functions on rp2040 +// +// Copyright (C) 2021 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "board/misc.h" // timer_from_us +#include "command.h" // shutdown +#include "gpio.h" // gpio_adc_setup +#include "hardware/structs/adc.h" // adc_hw +#include "hardware/structs/padsbank0.h" // padsbank0_hw +#include "hardware/structs/resets.h" // RESETS_RESET_ADC_BITS +#include "internal.h" // enable_pclock +#include "sched.h" // sched_shutdown + +DECL_CONSTANT("ADC_MAX", 4095); + +#define ADC_TEMPERATURE_PIN 0xfe +DECL_ENUMERATION("pin", "ADC_TEMPERATURE", ADC_TEMPERATURE_PIN); + +struct gpio_adc +gpio_adc_setup(uint32_t pin) +{ + if ((pin < 26 || pin > 29) && pin != ADC_TEMPERATURE_PIN) + shutdown("Not a valid ADC pin"); + + // Enable the ADC + if (!is_enabled_pclock(RESETS_RESET_ADC_BITS)) { + enable_pclock(RESETS_RESET_ADC_BITS); + adc_hw->cs = ADC_CS_EN_BITS; + } + + uint8_t chan; + if (pin == ADC_TEMPERATURE_PIN) { + chan = 4; + adc_hw->cs |= ADC_CS_TS_EN_BITS; + } else { + chan = pin - 26; + padsbank0_hw->io[pin] = PADS_BANK0_GPIO0_OD_BITS; + } + + return (struct gpio_adc){ .chan = chan }; +} + +enum { ADC_DUMMY=0xff }; +static uint8_t last_analog_read = ADC_DUMMY; + +// Try to sample a value. Returns zero if sample ready, otherwise +// returns the number of clock ticks the caller should wait before +// retrying this function. +uint32_t +gpio_adc_sample(struct gpio_adc g) +{ + uint32_t cs = adc_hw->cs; + if (!(cs & ADC_CS_READY_BITS)) + // ADC is busy + goto need_delay; + if (last_analog_read == g.chan) + // Sample now ready + return 0; + if (last_analog_read != ADC_DUMMY) + // Sample on another channel in progress + goto need_delay; + + // Begin sample + last_analog_read = g.chan; + adc_hw->cs = ((cs & ADC_CS_TS_EN_BITS) | ADC_CS_START_ONCE_BITS + | ADC_CS_EN_BITS | (g.chan << ADC_CS_AINSEL_LSB)); + +need_delay: + return timer_from_us(5); // Sample takes 2us but provide extra time +} + +// Read a value; use only after gpio_adc_sample() returns zero +uint16_t +gpio_adc_read(struct gpio_adc g) +{ + last_analog_read = ADC_DUMMY; + return adc_hw->result; +} + +// Cancel a sample that may have been started with gpio_adc_sample() +void +gpio_adc_cancel_sample(struct gpio_adc g) +{ + if (last_analog_read == g.chan) + last_analog_read = ADC_DUMMY; +} diff --git a/src/rp2040/gpio.h b/src/rp2040/gpio.h index 0f8507bd..50b6302c 100644 --- a/src/rp2040/gpio.h +++ b/src/rp2040/gpio.h @@ -19,4 +19,12 @@ struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up); void gpio_in_reset(struct gpio_in g, int8_t pull_up); uint8_t gpio_in_read(struct gpio_in g); +struct gpio_adc { + uint8_t chan; +}; +struct gpio_adc gpio_adc_setup(uint32_t pin); +uint32_t gpio_adc_sample(struct gpio_adc g); +uint16_t gpio_adc_read(struct gpio_adc g); +void gpio_adc_cancel_sample(struct gpio_adc g); + #endif // gpio.h diff --git a/src/rp2040/main.c b/src/rp2040/main.c index 35e4f55d..7b36856a 100644 --- a/src/rp2040/main.c +++ b/src/rp2040/main.c @@ -42,6 +42,7 @@ DECL_INIT(watchdog_init); #define FREQ_XOSC 12000000 #define FREQ_SYS 125000000 +#define FREQ_USB 48000000 void enable_pclock(uint32_t reset_bit) @@ -126,8 +127,13 @@ clock_setup(void) while (!(csys->selected & (1 << 1))) ; - // Setup clk_peri + // Setup pll_usb + enable_pclock(RESETS_RESET_PLL_USB_BITS); + pll_setup(pll_usb_hw, 40, 40*FREQ_XOSC/FREQ_USB); + + // Setup clk_peri and clk_adc clk_aux_setup(clk_peri, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS); + clk_aux_setup(clk_adc, CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB); // Enable watchdog tick (at 12Mhz) cref->div = 1<