mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19516
19516: cpu/rpx0xx: add initial ADC support r=dylad a=dylad ### Contribution description This PR adds initial support for RP2040 ADC peripheral. It is rather minimalist, and only use oneshot trigger to perform a single acquisition. I've tested this PR using a potentiometer connected between GND and 3V3. GP26, GP27, GP28 has been tested. ### Testing procedure Select `ADC_RES_12BIT` and flash `tests/periph_adc` `make BOARD=rpi-pico -C tests/periph_adc` ### Issues/PRs references None. Co-authored-by: Dylan Laduranty <dylan.laduranty@mesotic.com>
This commit is contained in:
commit
ffdc1df807
@ -12,6 +12,7 @@ config BOARD_RPI_PICO
|
||||
bool
|
||||
default y
|
||||
select CPU_MODEL_RP2040
|
||||
select HAS_PERIPH_ADC
|
||||
select HAS_PERIPH_UART
|
||||
|
||||
select HAVE_SAUL_GPIO
|
||||
|
@ -1,5 +1,6 @@
|
||||
CPU := rpx0xx
|
||||
|
||||
# Put defined MCU peripherals here (in alphabetical order)
|
||||
FEATURES_PROVIDED += periph_adc
|
||||
FEATURES_PROVIDED += periph_timer
|
||||
FEATURES_PROVIDED += periph_uart
|
||||
|
@ -78,6 +78,21 @@ static const timer_conf_t timer_config[] = {
|
||||
|
||||
#define TIMER_NUMOF ARRAY_SIZE(timer_config)
|
||||
|
||||
/**
|
||||
* @name ADC configuration
|
||||
*
|
||||
* The configuration consists simply of a list of channels that should be used
|
||||
* @{
|
||||
*/
|
||||
static const adc_conf_t adc_config[] = {
|
||||
{ .pin = GPIO_PIN(0, 26), .chan = 0},
|
||||
{ .pin = GPIO_PIN(0, 27), .chan = 1},
|
||||
{ .pin = GPIO_PIN(0, 28), .chan = 2},
|
||||
};
|
||||
|
||||
#define ADC_NUMOF ARRAY_SIZE(adc_config)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -177,3 +177,17 @@ void clock_gpout3_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT3_CTR
|
||||
io_reg_atomic_set(&PADS_BANK0->GPIO25, 1U << PADS_BANK0_GPIO25_IE_Pos);
|
||||
gpio_set_function_select(25, FUNCTION_SELECT_CLOCK);
|
||||
}
|
||||
|
||||
void clock_adc_configure(CLOCKS_CLK_ADC_CTRL_AUXSRC_Enum aux)
|
||||
{
|
||||
/* Stop the clock generator */
|
||||
io_reg_atomic_clear(&CLOCKS->CLK_ADC_CTRL,
|
||||
(1u << CLOCKS_CLK_ADC_CTRL_ENABLE_Pos));
|
||||
/* Selects the new auxiliary clock source */
|
||||
io_reg_write_dont_corrupt(&CLOCKS->CLK_ADC_CTRL,
|
||||
aux << CLOCKS_CLK_ADC_CTRL_AUXSRC_Pos,
|
||||
CLOCKS_CLK_ADC_CTRL_AUXSRC_Msk);
|
||||
/* Restart the clock generator */
|
||||
io_reg_atomic_set(&CLOCKS->CLK_ADC_CTRL,
|
||||
(1u << CLOCKS_CLK_ADC_CTRL_ENABLE_Pos));
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ static void _cpu_reset(void)
|
||||
| RESETS_RESET_pll_usb_Msk
|
||||
| RESETS_RESET_pll_sys_Msk
|
||||
| RESETS_RESET_pads_qspi_Msk
|
||||
| RESETS_RESET_pads_bank0_Msk
|
||||
| RESETS_RESET_io_qspi_Msk);
|
||||
periph_reset(rst);
|
||||
/* Assert that reset has completed except for those components which
|
||||
@ -78,6 +79,13 @@ static void _cpu_reset(void)
|
||||
clock_gpout0_configure(CLOCK_XOSC, CLOCK_XOSC,
|
||||
CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_clk_ref);
|
||||
}
|
||||
|
||||
/* Configure USB PLL to deliver 48MHz needed by ADC */
|
||||
if (IS_USED(MODULE_PERIPH_ADC)) {
|
||||
pll_start_usb(PLL_USB_REF_DIV, PLL_USB_VCO_FEEDBACK_SCALE,
|
||||
PLL_USB_POSTDIV1, PLL_USB_POSTDIV2);
|
||||
clock_adc_configure(CLOCKS_CLK_ADC_CTRL_AUXSRC_clksrc_pll_usb);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_init(void)
|
||||
|
@ -390,6 +390,14 @@ typedef struct {
|
||||
} gpio_io_ctrl_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief ADC channel configuration data
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< Pin connected to the channel */
|
||||
uint8_t chan; /**< CPU ADC channel connected to the pin */
|
||||
} adc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration details for an UART interface needed by the RPX0XX peripheral
|
||||
*/
|
||||
@ -613,6 +621,13 @@ void clock_gpout2_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT2_CTR
|
||||
*/
|
||||
void clock_gpout3_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_Enum aux);
|
||||
|
||||
/**
|
||||
* @brief Configure the ADC clock to run from a dedicated auxiliary
|
||||
* clock source
|
||||
*
|
||||
* @param aux Auxiliary clock source
|
||||
*/
|
||||
void clock_adc_configure(CLOCKS_CLK_ADC_CTRL_AUXSRC_Enum aux);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -636,7 +651,7 @@ void pll_start_sys(uint8_t ref_div,
|
||||
uint8_t post_div_1, uint8_t post_div_2);
|
||||
|
||||
/**
|
||||
* @brief Start the PLL for the system clock
|
||||
* @brief Start the PLL for the USB clock
|
||||
* output[MHz] = f_ref / @p ref_div * @p vco_feedback_scale / @p post_div_1 / @p post_div_2
|
||||
*
|
||||
* @note Usual setting should be (12 MHz, 1, 40, 5, 2) to get a 48 MHz USB clock signal
|
||||
|
97
cpu/rpx0xx/periph/adc.c
Normal file
97
cpu/rpx0xx/periph/adc.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Mesotic SAS
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_rpx0xx
|
||||
* @ingroup drivers_periph_adc
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "cpu.h"
|
||||
#include "mutex.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/**
|
||||
* @brief Lock to prevent concurrency issues when used from different threads
|
||||
*/
|
||||
static mutex_t lock;
|
||||
|
||||
static inline void _prep(void)
|
||||
{
|
||||
mutex_lock(&lock);
|
||||
}
|
||||
|
||||
static inline void _done(void)
|
||||
{
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
static void _disable_digital_func(gpio_t pin)
|
||||
{
|
||||
|
||||
const gpio_pad_ctrl_t disable_digital_pad = {
|
||||
.input_enable = 0,
|
||||
.output_disable = 1,
|
||||
};
|
||||
|
||||
gpio_set_pad_config(pin, disable_digital_pad);
|
||||
}
|
||||
|
||||
int adc_init(adc_t line)
|
||||
{
|
||||
if (line >= ADC_NUMOF) {
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Lock mutex for exclusive access */
|
||||
_prep();
|
||||
/* Reset ADC peripheral */
|
||||
periph_reset(RESETS_RESET_adc_Msk);
|
||||
periph_reset_done(RESETS_RESET_adc_Msk);
|
||||
|
||||
/* Enable ADC peripheral and its clock */
|
||||
io_reg_atomic_set(&ADC->CS, 1 << ADC_CS_EN_Pos);
|
||||
/* Disable associated GPIO functionality as requested by datasheet */
|
||||
_disable_digital_func(adc_config[line].pin);
|
||||
/* Unlock mutex */
|
||||
_done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t adc_sample(adc_t line, adc_res_t res)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
/* rpx0xx MCU only supports 12 bits resolution */
|
||||
if (res != ADC_RES_12BIT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Lock mutex for exclusive access */
|
||||
_prep();
|
||||
/* Select the channel */
|
||||
io_reg_write_dont_corrupt(&ADC->CS, adc_config[line].chan << ADC_CS_AINSEL_Pos,
|
||||
ADC_CS_AINSEL_Msk);
|
||||
/* Wait for ADC to be ready */
|
||||
while (!(ADC->CS & ADC_CS_READY_Msk)) {}
|
||||
/* Trigger one-shot sample */
|
||||
io_reg_atomic_set(&ADC->CS, 1 << ADC_CS_START_ONCE_Pos);
|
||||
/* Wait for completion */
|
||||
while (!(ADC->CS & ADC_CS_READY_Msk)) {}
|
||||
/* Get the result */
|
||||
val = ADC->RESULT & ADC_RESULT_RESULT_Msk;
|
||||
/* Unlock mutex */
|
||||
_done();
|
||||
return val;
|
||||
}
|
Loading…
Reference in New Issue
Block a user