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:
parent
3aaea1a74e
commit
ba8b3dc2b4
@ -399,6 +399,34 @@ typedef struct {
|
|||||||
uint8_t chan; /**< CPU ADC channel connected to the pin */
|
uint8_t chan; /**< CPU ADC channel connected to the pin */
|
||||||
} adc_conf_t;
|
} 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
|
* @brief Configuration details for an UART interface needed by the RPX0XX peripheral
|
||||||
*/
|
*/
|
||||||
|
149
cpu/rpx0xx/periph/pwm.c
Normal file
149
cpu/rpx0xx/periph/pwm.c
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user