1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/rpx0xx: add PWM support

Signed-off-by: Dylan Laduranty <dylan.laduranty@mesotic.com>
This commit is contained in:
Dylan Laduranty 2024-03-18 21:04:27 +01:00
parent 3aaea1a74e
commit ba8b3dc2b4
2 changed files with 177 additions and 0 deletions

View File

@ -399,6 +399,34 @@ typedef struct {
uint8_t chan; /**< CPU ADC channel connected to the pin */
} adc_conf_t;
/**
* @brief Number of slices available per PWM device
*/
#define PWM_SLICE_NUMOF (8)
/**
* @brief Number of channels available per slice
*/
#define PWM_CHANNEL_NUMOF (2)
/**
* @brief PWM channel
*/
typedef struct {
gpio_t pin; /**< GPIO pin mapped to this channel */
uint8_t cc_chan; /**< capture compare channel used */
} pwm_chan_t;
/**
* @brief PWM device configuration data structure
*/
typedef struct {
uint8_t pwm_slice; /**< PWM slice instance,
must be < to PWM_SLICE_NUMOF */
pwm_chan_t chan[PWM_CHANNEL_NUMOF]; /**< channel mapping set to
{GPIO_UNDEF, 0} if not used */
} pwm_conf_t;
/**
* @brief Configuration details for an UART interface needed by the RPX0XX peripheral
*/

149
cpu/rpx0xx/periph/pwm.c Normal file
View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2023-2024 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_pwm
* @{
*
* @file
* @brief Low-level PWM driver implementation
*
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
*
* @}
*/
#include "cpu.h"
#include "assert.h"
#include "periph/pwm.h"
#include "periph/gpio.h"
#include "periph_conf.h"
#include <stdio.h>
#define ENABLE_DEBUG 0
#include "debug.h"
/* Vendor files don't offer a convenient way to access these registers
through a dedicated struct, thus create one for this purpose */
typedef struct {
uint32_t csr;
uint32_t div;
uint32_t ctr;
uint32_t cc;
uint32_t top;
} pwm_slice_reg_t;
/* Structure holding all PWM slices registers */
struct pwm_reg {
pwm_slice_reg_t slices[PWM_SLICE_NUMOF];
};
/* Start address of PWM slices */
#define PWM_REG ((struct pwm_reg *)PWM_BASE)
/* Helper to get slice register */
static inline pwm_slice_reg_t *pwm_slice(unsigned slice_idx)
{
return &PWM_REG->slices[slice_idx];
}
/* PWM block is feed by RP2040 sysclk (CLOCK_CORECLOCK) */
uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res)
{
uint8_t div_int;
uint8_t div_frac;
uint32_t val;
uint32_t ret;
uint8_t slice = pwm_config[pwm].pwm_slice;
(void)mode;
const gpio_io_ctrl_t pwm_io_config = {
.function_select = FUNCTION_SELECT_PWM,
};
/* Initialize associated GPIO pin */
if (pwm_config[pwm].chan[0].pin != GPIO_UNDEF) {
gpio_set_io_config(pwm_config[pwm].chan[0].pin, pwm_io_config);
}
if (pwm_config[pwm].chan[1].pin != GPIO_UNDEF) {
gpio_set_io_config(pwm_config[pwm].chan[1].pin, pwm_io_config);
}
/* Compute DIV register value to get closest match for
freq and res variables */
val = (((uint32_t)CLOCK_CORECLOCK) << 4) / (freq * res);
/* If the value is above 4095, we will not be able to reach the desired
frequency so set the divisor value to maximum to get to the closest
possible value for the PWM frequency */
if (val > 4095) {
div_frac = 0x0F;
div_int = 0xFF;
} else {
div_frac = val % 16;
div_int = val / 16;
}
/* Compute the real frequency we will get */
ret = CLOCK_CORECLOCK / (res * (div_int + (div_frac / 16)));
DEBUG("[pwm]: div_int:%d, div_frac:%d\n", div_int, div_frac);
/* Set the slice divider to reach the desired frequency */
pwm_slice(slice)->div = ((div_int << PWM_CH0_DIV_INT_Pos) | div_frac);
/* Let PWM slice run in free running mode */
pwm_slice(slice)->csr = PWM_CH0_CSR_DIVMODE_div;
/* Set PWM slice TOP value */
pwm_slice(slice)->top = res-1;
/* Enable PWM slice */
io_reg_atomic_set(&PWM->EN, 1 << slice);
DEBUG("[pwm]: Init done, frequency set to %ld\n", ret);
return ret;
}
uint8_t pwm_channels(pwm_t pwm)
{
assert(pwm < PWM_NUMOF);
return PWM_CHANNEL_NUMOF;
}
void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
{
assert((pwm < PWM_NUMOF) && (channel < PWM_CHANNEL_NUMOF));
uint8_t slice = pwm_config[pwm].pwm_slice;
/* Set channel compare value */
if (channel) {
io_reg_write_dont_corrupt(&pwm_slice(slice)->cc,
(value << PWM_CH0_CC_B_Pos),
PWM_CH0_CC_B_Msk);
}
else {
io_reg_write_dont_corrupt(&pwm_slice(slice)->cc,
(value << PWM_CH0_CC_A_Pos),
PWM_CH0_CC_A_Msk);
}
}
void pwm_poweron(pwm_t pwm)
{
assert(pwm < PWM_NUMOF);
uint8_t slice = pwm_config[pwm].pwm_slice;
io_reg_atomic_set(&PWM->EN, 1 << slice);
}
void pwm_poweroff(pwm_t pwm)
{
assert(pwm < PWM_NUMOF);
uint8_t slice = pwm_config[pwm].pwm_slice;
io_reg_atomic_clear(&PWM->EN, 1 << slice);
}