NUC123 PWM module
This commit is contained in:
parent
5f800992c3
commit
c1a3e2e93d
|
@ -1,4 +1,10 @@
|
|||
PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1/hal_st_lld.c
|
||||
ifeq ($(USE_SMART_BUILD),yes)
|
||||
ifneq ($(findstring HAL_USE_PWM TRUE,$(HALCONF)),)
|
||||
PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1/hal_pwm_lld.c
|
||||
#PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1/timer.c
|
||||
endif
|
||||
else
|
||||
PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1/hal_pwm_lld.c
|
||||
endif
|
||||
|
||||
PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1/hal_st_lld.c
|
||||
PLATFORMINC += $(CHIBIOS_CONTRIB)/os/hal/ports/NUMICRO/LLD/TIMv1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2019 /u/KeepItUnder
|
||||
Copyright (C) 2020 Alex Lewontin
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -30,22 +30,124 @@
|
|||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define NUC123_PWM_CLKSRC_HSE 0x0UL
|
||||
#define NUC123_PWM_CLKSRC_HCLK 0x2UL
|
||||
#define NUC123_PWM_CLKSRC_HSI 0x3UL
|
||||
#define NUC123_PWM_CLKSRC_LSI 0x7UL
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PWMD1 driver identifier.
|
||||
*/
|
||||
#if (NUC123_PWM_USE_PWM1 == TRUE) || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD1;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
typedef struct {
|
||||
__IO uint32_t CNR; /* Offset: 0x0C PWM Counter Register 0 */
|
||||
__IO uint32_t CMR; /* Offset: 0x10 PWM Comparator Register 0 */
|
||||
__I uint32_t PDR; /* Offset: 0x14 PWM Data Register 0 */
|
||||
} PWM_CHANNEL_T;
|
||||
|
||||
#define PWMA_CHANNELS_BASE (PWMA_BASE + 0xC)
|
||||
#define PWMA_CHANNELS ((PWM_CHANNEL_T *) PWMA_CHANNELS_BASE)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Shared IRQ handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @param[in] pwmd pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void pwm_lld_serve_interrupt(PWMDriver *pwmp) {
|
||||
|
||||
uint32_t piir;
|
||||
uint32_t pier_clr_msk;
|
||||
uint32_t poe_clr_msk;
|
||||
|
||||
pier_clr_msk = 0;
|
||||
poe_clr_msk = 0;
|
||||
piir = PWMA->PIIR;
|
||||
PWMA->PIIR = piir;
|
||||
|
||||
if (piir & PWM_PIIR_PWMDIF0_Msk) {
|
||||
pwmp->config->channels[0].callback(pwmp);
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMDIF1_Msk) {
|
||||
pwmp->config->channels[1].callback(pwmp);
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMDIF2_Msk) {
|
||||
pwmp->config->channels[2].callback(pwmp);
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMDIF3_Msk) {
|
||||
pwmp->config->channels[3].callback(pwmp);
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMIF0_Msk) {
|
||||
if (pwmp->periodic_callback_enabled) {
|
||||
pwmp->config->callback(pwmp);
|
||||
}
|
||||
if (!pwmIsChannelEnabledI(pwmp, 0)) {
|
||||
poe_clr_msk |= PWM_POE_PWM1_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMDIE0_Msk;
|
||||
}
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMIF1_Msk) {
|
||||
poe_clr_msk |= PWM_POE_PWM1_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMDIE1_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMIE1_Msk;
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMIF2_Msk) {
|
||||
poe_clr_msk |= PWM_POE_PWM2_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMDIE2_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMIE2_Msk;
|
||||
}
|
||||
|
||||
if (piir & PWM_PIIR_PWMIF3_Msk) {
|
||||
poe_clr_msk |= PWM_POE_PWM3_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMDIE3_Msk;
|
||||
pier_clr_msk |= PWM_PIER_PWMIE3_Msk;
|
||||
}
|
||||
|
||||
PWMA->POE &= ~poe_clr_msk;
|
||||
PWMA->PIER &= ~pier_clr_msk;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PWM interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
OSAL_IRQ_HANDLER(NUC123_PWMA_HANDLER)
|
||||
{
|
||||
OSAL_IRQ_PROLOGUE();
|
||||
pwm_lld_serve_interrupt(&PWMD1);
|
||||
OSAL_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
@ -55,175 +157,277 @@
|
|||
*
|
||||
* @notapi
|
||||
*/
|
||||
// void pwm_lld_init(void) {
|
||||
|
||||
// }
|
||||
|
||||
void pwm_lld_start(PWM_T *pwm, uint32_t mask) {
|
||||
uint32_t pwmStart = 0;
|
||||
|
||||
for(uint32_t i = 0; i < PWM_CHANNELS; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
pwmStart |= (PWM_PCR_CH0EN_Msk << (i * 8));
|
||||
}
|
||||
}
|
||||
|
||||
(pwm)->PCR |= pwmStart;
|
||||
}
|
||||
|
||||
|
||||
void pwm_lld_enable_period_int(PWM_T *pwm, uint32_t pwmChannel, uint32_t periodType) {
|
||||
(pwm)->PIER = ((pwm)->PIER & ~(0x01ul << (pwmChannel >> 1))) | (0x01ul << pwmChannel) | (periodType << (pwmChannel >> 1));
|
||||
}
|
||||
|
||||
|
||||
void pwm_lld_disable_period_int(PWM_T *pwm, uint32_t pwmChannel) {
|
||||
(pwm)->PIER &= ~(PWM_PIER_PWMIE0_Msk << pwmChannel);
|
||||
}
|
||||
|
||||
|
||||
uint32_t pwm_lld_get_period_int(PWM_T *pwm, uint32_t pwmChannel) {
|
||||
if ((pwm)->PIIR & (0x01ul << (pwmChannel))) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pwm_lld_clear_period_int(PWM_T *pwm, uint32_t pwmChannel) {
|
||||
(pwm)->PIIR = (0x01ul << pwmChannel);
|
||||
}
|
||||
|
||||
|
||||
uint32_t pwm_lld_config_output_channel(PWM_T *pwm, uint32_t channel, uint32_t freq, uint32_t duty)
|
||||
void pwm_lld_init(void)
|
||||
{
|
||||
uint32_t regSource;
|
||||
uint32_t clkSource;
|
||||
uint32_t pwmClk;
|
||||
uint8_t divider = 1;
|
||||
uint8_t prescale = 0xFF;
|
||||
uint16_t CNR = 0xFFFF;
|
||||
|
||||
if (channel < 2) {
|
||||
regSource = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM01_S_Msk)) >> (CLK_CLKSEL2_PWM01_S_Pos - 2)) | (CLK->CLKSEL1 & (CLK_CLKSEL1_PWM01_S_Msk)) >> (CLK_CLKSEL1_PWM01_S_Pos);
|
||||
} else {
|
||||
regSource = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM23_S_Msk)) >> (CLK_CLKSEL2_PWM23_S_Pos - 2)) | (CLK->CLKSEL1 & (CLK_CLKSEL1_PWM23_S_Msk)) >> (CLK_CLKSEL1_PWM23_S_Pos);
|
||||
}
|
||||
#if (NUC123_PWM_USE_PWM1 == TRUE)
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD1);
|
||||
PWMD1.channels = PWM_CHANNELS;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (regSource == 2) {
|
||||
SystemCoreClockUpdate();
|
||||
clkSource = SystemCoreClock;
|
||||
} else {
|
||||
switch (regSource) {
|
||||
case 0:
|
||||
clkSource = __HXT;
|
||||
break;
|
||||
case 1:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
clkSource = 0;
|
||||
break;
|
||||
case 2:
|
||||
SystemCoreClockUpdate();
|
||||
clkSource = SystemCoreClock;
|
||||
break;
|
||||
case 3:
|
||||
clkSource = __HIRC;
|
||||
break;
|
||||
case 7:
|
||||
clkSource = __LIRC;
|
||||
break;
|
||||
/**
|
||||
* @brief Configures and activates the PWM peripheral.
|
||||
* @note Starting a driver that is already in the @p PWM_READY state
|
||||
* disables all the active channels.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_start(PWMDriver *pwmp)
|
||||
{
|
||||
uint32_t clksel1;
|
||||
uint32_t clksel2;
|
||||
uint32_t prescale;
|
||||
uint32_t pcr;
|
||||
if (pwmp->state == PWM_STOP) {
|
||||
clksel1 = CLK->CLKSEL1;
|
||||
clksel2 = CLK->CLKSEL2;
|
||||
clksel1 = (clksel1 & ~(CLK_CLKSEL1_PWM01_S_Msk)) |
|
||||
((NUC123_PWM_CLKSRC_HCLK << CLK_CLKSEL1_PWM01_S_Pos) &
|
||||
CLK_CLKSEL1_PWM01_S_Msk);
|
||||
clksel1 = (clksel1 & ~(CLK_CLKSEL1_PWM23_S_Msk)) |
|
||||
((NUC123_PWM_CLKSRC_HCLK << CLK_CLKSEL1_PWM23_S_Pos) &
|
||||
CLK_CLKSEL1_PWM23_S_Msk);
|
||||
clksel2 = (clksel2 & ~(CLK_CLKSEL2_PWM01_S_E_Msk)) |
|
||||
((NUC123_PWM_CLKSRC_HCLK >> 2) << CLK_CLKSEL2_PWM01_S_E_Pos);
|
||||
clksel2 = (clksel2 & ~(CLK_CLKSEL2_PWM23_S_E_Msk)) |
|
||||
((NUC123_PWM_CLKSRC_HCLK >> 2) << CLK_CLKSEL2_PWM23_S_E_Pos);
|
||||
CLK->CLKSEL1 = clksel1;
|
||||
CLK->CLKSEL2 = clksel2;
|
||||
|
||||
CLK->APBCLK |= (CLK_APBCLK_PWM01_EN_Msk | CLK_APBCLK_PWM23_EN_Msk);
|
||||
nvicEnableVector(NUC123_PWMA_NUMBER, NUC123_PWM_IRQ_PRIORITY);
|
||||
SYS->IPRSTC2 |= SYS_IPRSTC2_PWM03_RST_Msk;
|
||||
SYS->IPRSTC2 &= ~(SYS_IPRSTC2_PWM03_RST_Msk);
|
||||
|
||||
/* Set clock scaling to 1 */
|
||||
PWMA->CSR = (4 << PWM_CSR_CSR0_Pos) | (4 << PWM_CSR_CSR1_Pos) |
|
||||
(4 << PWM_CSR_CSR2_Pos) | (4 << PWM_CSR_CSR3_Pos);
|
||||
|
||||
/* Set prescale to set frequency */
|
||||
prescale = NUC123_HCLK / pwmp->config->frequency;
|
||||
PWMA->PPR = (PWMA->PPR & ~(PWM_PPR_CP01_Msk | PWM_PPR_CP23_Msk)) |
|
||||
((prescale << PWM_PPR_CP01_Pos) & PWM_PPR_CP01_Msk) |
|
||||
((prescale << PWM_PPR_CP23_Pos) & PWM_PPR_CP23_Msk);
|
||||
|
||||
/*
|
||||
* PINV == 0 -> active high, PINV == 1 -> active low.
|
||||
* CHnINV vs CHnPINV:
|
||||
* - CHnINV inverts before deadzone generation
|
||||
* - CHnPINV inverts after deadzone generation
|
||||
* If no deadzone generation, INV(signal) == PINV(signal) and signal == INV(PINV(signal))
|
||||
* If there is deadzone generation, things get more complicated.
|
||||
*/
|
||||
|
||||
pcr = PWMA->PCR;
|
||||
|
||||
pcr &= ~(PWM_PCR_CH0PINV_Msk | PWM_PCR_CH0INV_Msk);
|
||||
pcr &= ~(PWM_PCR_CH1PINV_Msk | PWM_PCR_CH1INV_Msk);
|
||||
pcr &= ~(PWM_PCR_CH2PINV_Msk | PWM_PCR_CH2INV_Msk);
|
||||
pcr &= ~(PWM_PCR_CH3PINV_Msk | PWM_PCR_CH3INV_Msk);
|
||||
|
||||
/* Conditionally (and branchlessly) set the bits for the inverted channels */
|
||||
pcr |= ((pwmp->config->channels[0].mode == PWM_OUTPUT_ACTIVE_LOW) * PWM_PCR_CH0PINV_Msk);
|
||||
pcr |= ((pwmp->config->channels[1].mode == PWM_OUTPUT_ACTIVE_LOW) * PWM_PCR_CH1PINV_Msk);
|
||||
pcr |= ((pwmp->config->channels[2].mode == PWM_OUTPUT_ACTIVE_LOW) * PWM_PCR_CH2PINV_Msk);
|
||||
pcr |= ((pwmp->config->channels[3].mode == PWM_OUTPUT_ACTIVE_LOW) * PWM_PCR_CH3PINV_Msk);
|
||||
|
||||
/* Set to auto-reload (this will also reset CMR & CNR) */
|
||||
pcr |= (PWM_PCR_CH0MOD_Msk | PWM_PCR_CH1MOD_Msk);
|
||||
pcr |= (PWM_PCR_CH2MOD_Msk | PWM_PCR_CH3MOD_Msk);
|
||||
|
||||
PWMA->PCR = pcr;
|
||||
|
||||
/* TODO: Deadzone generation */
|
||||
|
||||
/* Mux the pins per the channel configs */
|
||||
|
||||
if (pwmp->config->channels[0].mode != PWM_OUTPUT_DISABLED) {
|
||||
if (pwmp->config->channels[0].pinmask & NUC123_PWM_CH0_PIN_PA12) {
|
||||
SYS->GPA_MFP |= (1 << 12);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate best divider */
|
||||
while (divider < 17) {
|
||||
pwmClk = (clkSource / (freq)) / divider;
|
||||
// If pwmClk is larger than (CNR * prescale) then divider can still be bigger
|
||||
if (pwmClk > (0x10000 * 0x100)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set prescaler to CNR value lower than 0xFFFF
|
||||
prescale = (pwmClk + 0xFFFF) / 0x10000;
|
||||
|
||||
// Keep prescale bigger than 2 this loop
|
||||
if (prescale < 3) {
|
||||
prescale = 2;
|
||||
}
|
||||
|
||||
pwmClk /= prescale;
|
||||
|
||||
if (pwmClk <= 0x10000) {
|
||||
if (pwmClk == 1) {
|
||||
CNR = 1; // Too fast, and PWM cannot generate expected frequency...
|
||||
} else {
|
||||
CNR = pwmClk;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
divider = divider << 1;
|
||||
if (pwmp->config->channels[1].mode != PWM_OUTPUT_DISABLED) {
|
||||
if (pwmp->config->channels[1].pinmask & NUC123_PWM_CH1_PIN_PA13) {
|
||||
SYS->GPA_MFP |= (1 << 13);
|
||||
}
|
||||
}
|
||||
|
||||
pwmClk = clkSource / (prescale * divider * CNR);
|
||||
if (pwmp->config->channels[2].mode != PWM_OUTPUT_DISABLED) {
|
||||
if (pwmp->config->channels[2].pinmask & NUC123_PWM_CH2_PIN_PA14) {
|
||||
SYS->GPA_MFP |= (1 << 14);
|
||||
}
|
||||
|
||||
/* Calculate PWM Registers */
|
||||
prescale -= 1;
|
||||
CNR -= 1;
|
||||
|
||||
/* Set final divider for output calculation */
|
||||
switch (divider) {
|
||||
case 1:
|
||||
divider = 4;
|
||||
break;
|
||||
case 4:
|
||||
divider = 1;
|
||||
break;
|
||||
case 8:
|
||||
divider = 2;
|
||||
break;
|
||||
case 16:
|
||||
divider = 3;
|
||||
break;
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
default:
|
||||
divider = 0;
|
||||
break;
|
||||
if (pwmp->config->channels[2].pinmask & NUC123_PWM_CH2_PIN_PC12) {
|
||||
SYS->GPC_MFP |= (1 << 12);
|
||||
SYS->ALT_MFP |= SYS_ALT_MFP_PC12_MFP1_Msk;
|
||||
}
|
||||
}
|
||||
|
||||
// Prescaler is shared every two channels (01/23/etc.)
|
||||
(pwm)->PPR = ((pwm)->PPR & ~(PWM_PPR_CP01_Msk << ((channel >> 1) * 8))) | (prescale << ((channel >> 1) * 8));
|
||||
(pwm)->CSR = ((pwm)->CSR & ~(PWM_CSR_CSR0_Msk << (4 * channel))) | (divider << (4 * channel));
|
||||
if (pwmp->config->channels[3].mode != PWM_OUTPUT_DISABLED) {
|
||||
if (pwmp->config->channels[3].pinmask & NUC123_PWM_CH3_PIN_PA15) {
|
||||
SYS->GPA_MFP |= (1 << 15);
|
||||
SYS->ALT_MFP &= ~SYS_ALT_MFP_PA15_MFP1_Msk;
|
||||
}
|
||||
|
||||
// Set edge-aligned PWM
|
||||
(pwm)->PCR &= ~(PWM_PCR_PWM01TYPE_Msk << (channel >> 1));
|
||||
(pwm)->PCR |= PWM_PCR_CH0MOD_Msk << (8 * channel);
|
||||
|
||||
// Set PWM register values
|
||||
if (duty) {
|
||||
*((__IO uint32_t *)((((uint32_t) & ((pwm)->CMR0)) + channel * 12))) = duty * (CNR + 1) / 100 - 1;
|
||||
} else {
|
||||
*((__IO uint32_t *)((((uint32_t) & ((pwm)->CMR0)) + channel * 12))) = 0;
|
||||
if (pwmp->config->channels[3].pinmask & NUC123_PWM_CH3_PIN_PC13) {
|
||||
SYS->GPC_MFP |= (1 << 13);
|
||||
SYS->ALT_MFP |= SYS_ALT_MFP_PC13_MFP1_Msk;
|
||||
}
|
||||
}
|
||||
|
||||
*((__IO uint32_t *)((((uint32_t) & ((pwm)->CNR0)) + channel * 12))) = CNR;
|
||||
#if defined(NUC123xxxAEx)
|
||||
/* TODO: Implement PWM0 PC8 & PWM1 PB9 */
|
||||
#endif
|
||||
|
||||
return(pwmClk);
|
||||
PWMA->PIER &= ~(PWM_PIER_INT01TYPE_Msk | PWM_PIER_INT23TYPE_Msk);
|
||||
|
||||
pwm_lld_change_period(pwmp, pwmp->config->period);
|
||||
|
||||
PWMA->PCR |= (PWM_PCR_CH0EN_Msk | PWM_PCR_CH1EN_Msk |
|
||||
PWM_PCR_CH2EN_Msk | PWM_PCR_CH3EN_Msk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the PWM peripheral.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_stop(PWMDriver *pwmp)
|
||||
{
|
||||
/* If in ready state then disables the PWM clock.*/
|
||||
if (pwmp->state == PWM_READY) {
|
||||
#if NUC123_PWM_USE_PWM1 == TRUE
|
||||
if (&PWMD1 == pwmp) {
|
||||
CLK->APBCLK &= ~(CLK_APBCLK_PWM01_EN_Msk | CLK_APBCLK_PWM23_EN_Msk);
|
||||
nvicDisableVector(NUC123_PWMA_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables a PWM channel.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is active using the specified configuration.
|
||||
* @note The function has effect at the next cycle start.
|
||||
* @note Channel notification is not enabled.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...channels-1)
|
||||
* @param[in] width PWM pulse width as clock pulses number
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_enable_channel(PWMDriver *pwmp, pwmchannel_t channel,
|
||||
pwmcnt_t width)
|
||||
{
|
||||
(void)pwmp;
|
||||
PWMA_CHANNELS[channel].CMR = width;
|
||||
PWMA->POE |= PWM_POE_PWM0_Msk << channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables a PWM channel and its notification.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is disabled and its output line returned to the
|
||||
* idle state.
|
||||
* @note The function has effect at the next cycle start.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...channels-1)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel)
|
||||
{
|
||||
(void)pwmp;
|
||||
/* We do not disable immediately, but enable the periodic interrupt
|
||||
We will actually disable in the next ISR. */
|
||||
PWMA->PIER |= (PWM_PIER_PWMIE0_Msk << channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables the periodic activation edge notification.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @note If the notification is already enabled then the call has no effect.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_enable_periodic_notification(PWMDriver *pwmp) {
|
||||
(void)pwmp;
|
||||
PWMA->PIER |= PWM_PIER_PWMIE0_Msk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables the periodic activation edge notification.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @note If the notification is already disabled then the call has no effect.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_disable_periodic_notification(PWMDriver *pwmp) {
|
||||
(void)pwmp;
|
||||
/* TODO: Make sure this isn't in use for disabling the periodic channel */
|
||||
PWMA->PIER &= ~PWM_PIER_PWMIE0_Msk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables a channel de-activation edge notification.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @pre The channel must have been activated using @p pwmEnableChannel().
|
||||
* @note If the notification is already enabled then the call has no effect.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...channels-1)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_enable_channel_notification(PWMDriver *pwmp,
|
||||
pwmchannel_t channel)
|
||||
{
|
||||
(void)pwmp;
|
||||
PWMA->PIER |= (PWM_PIER_PWMDIE0_Msk << channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables a channel de-activation edge notification.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @pre The channel must have been activated using @p pwmEnableChannel().
|
||||
* @note If the notification is already disabled then the call has no effect.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...channels-1)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_disable_channel_notification(PWMDriver * pwmp,
|
||||
pwmchannel_t channel)
|
||||
{
|
||||
(void)pwmp;
|
||||
PWMA->PIER &= ~(PWM_PIER_PWMDIE0_Msk << channel);
|
||||
}
|
||||
|
||||
void _pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period) {
|
||||
(void)pwmp;
|
||||
osalSysLock();
|
||||
PWMA_CHANNELS[0].CNR = period - 1;
|
||||
PWMA_CHANNELS[1].CNR = period - 1;
|
||||
PWMA_CHANNELS[2].CNR = period - 1;
|
||||
PWMA_CHANNELS[3].CNR = period - 1;
|
||||
osalSysUnlock();
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_PWM */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2019 /u/KeepItUnder
|
||||
Copyright (C) 2020 Alex Lewontin
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -25,9 +25,7 @@
|
|||
#ifndef HAL_PWM_LLD_H
|
||||
#define HAL_PWM_LLD_H
|
||||
|
||||
// #if HAL_USE_PWM || defined(__DOXYGEN__)
|
||||
|
||||
// #include "stm32_tim.h"
|
||||
#if (HAL_USE_PWM == TRUE) || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
|
@ -36,84 +34,237 @@
|
|||
/**
|
||||
* @brief Number of PWM channels per PWM driver.
|
||||
*/
|
||||
#define PWM_CHANNELS (4)
|
||||
#define PWM_CHANNELS 4
|
||||
|
||||
#define PWM_CH0 0x0ul
|
||||
#define PWM_CH1 0x1ul
|
||||
#define PWM_CH2 0x2ul
|
||||
#define PWM_CH3 0x3ul
|
||||
/**
|
||||
* @brief Possible per-channel output pin mask values.
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Channel 0 output pin mask values.
|
||||
* @note Pin PA.12 has not yet been implemented
|
||||
*/
|
||||
#define NUC123_PWM_CH0_PIN_NONE 0x0
|
||||
#define NUC123_PWM_CH0_PIN_PA12 0x1
|
||||
#if defined(NUC123xxxAEx)
|
||||
#define NUC123_PWM_CH0_PIN_PC8 0x2
|
||||
#endif
|
||||
|
||||
#define PWM_EDGE_ALIGNED (0x0ul) /*!< Edge aligned */
|
||||
#define PWM_CENTER_ALIGNED (0x01ul) /*!< Center aligned */
|
||||
/**
|
||||
* @brief Channel 1 output pin mask values.
|
||||
* @note Pin PB.9 has not yet been implemented
|
||||
*/
|
||||
#define NUC123_PWM_CH1_PIN_NONE 0x0
|
||||
#define NUC123_PWM_CH1_PIN_PA13 0x1
|
||||
#if defined(NUC123xxxAEx)
|
||||
#define NUC123_PWM_CH1_PIN_PB9 0x2
|
||||
#endif
|
||||
|
||||
#define PWM_CLK_DIV_1 (0x04ul) /*!< Divide by 1 */
|
||||
#define PWM_CLK_DIV_2 (0x0ul) /*!< Divide by 2 */
|
||||
#define PWM_CLK_DIV_4 (0x01ul) /*!< Divide by 4 */
|
||||
#define PWM_CLK_DIV_8 (0x02ul) /*!< Divide by 8 */
|
||||
#define PWM_CLK_DIV_16 (0x03ul) /*!< Divide by 16 */
|
||||
|
||||
#define PWM_PERIOD_INT_UNDERFLOW (0) /*!< Period interrupt - counter underflow */
|
||||
#define PWM_PERIOD_INT_MATCH_CNR (PWM_PIER_INT01TYPE_Msk) /*!< Period interrupt - counter matches CNR */
|
||||
#define PWM_CAPTURE_INT_RISING_LATCH (PWM_CCR0_CRL_IE0_Msk) /*!< Capture interrupt - rising latch */
|
||||
#define PWM_CAPTURE_INT_FALLING_LATCH (PWM_CCR0_CFL_IE0_Msk) /*!< Capture interrupt - falling latch */
|
||||
/**
|
||||
* @brief Channel 2 output pin mask values.
|
||||
*/
|
||||
#define NUC123_PWM_CH2_PIN_NONE 0x0
|
||||
#define NUC123_PWM_CH2_PIN_PA14 0x1
|
||||
#define NUC123_PWM_CH2_PIN_PC12 0x2
|
||||
|
||||
/**
|
||||
* @brief Channel 3 output pin mask values.
|
||||
*/
|
||||
#define NUC123_PWM_CH3_PIN_NONE 0x0
|
||||
#define NUC123_PWM_CH3_PIN_PA15 0x1
|
||||
#define NUC123_PWM_CH3_PIN_PC13 0x2
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name NUC123 configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief PWMD1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWM1 is included.
|
||||
* @note The default is @p TRUE
|
||||
*/
|
||||
#if !defined(NUC123_PWM_USE_PWM1) || defined(__DOXYGEN__)
|
||||
#define NUC123_PWM_USE_PWM1 TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD1 driver IRQ priority.
|
||||
*/
|
||||
#if !defined(NUC123_PWM_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define NUC123_PWM_IRQ_PRIORITY 3
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Configuration checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a PWM mode.
|
||||
*/
|
||||
typedef uint32_t pwmmode_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a PWM channel.
|
||||
*/
|
||||
typedef uint8_t pwmchannel_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a channels mask.
|
||||
*/
|
||||
typedef uint8_t pwmchnmsk_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a PWM counter.
|
||||
*/
|
||||
typedef uint32_t pwmcnt_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a PWM driver channel configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Channel active logic level.
|
||||
*/
|
||||
pwmmode_t mode;
|
||||
/**
|
||||
* @brief Channel callback pointer.
|
||||
* @note This callback is invoked on the channel compare event. If set to
|
||||
* @p NULL then the callback is disabled.
|
||||
*/
|
||||
pwmcallback_t callback;
|
||||
/* End of the mandatory fields.*/
|
||||
|
||||
/**
|
||||
* @brief A mask of pins the PWM signal will be output on.
|
||||
* @note See driver constants for possible values.
|
||||
*/
|
||||
uint8_t pinmask;
|
||||
|
||||
} PWMChannelConfig;
|
||||
|
||||
/**
|
||||
* @brief Type of a PWM driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Timer clock in Hz.
|
||||
* @note The low level can use assertions in order to catch invalid
|
||||
* frequency specifications.
|
||||
*/
|
||||
uint32_t frequency;
|
||||
/**
|
||||
* @brief PWM period in ticks.
|
||||
* @note The low level can use assertions in order to catch invalid
|
||||
* period specifications.
|
||||
*/
|
||||
pwmcnt_t period;
|
||||
/**
|
||||
* @brief Periodic callback pointer.
|
||||
* @note This callback is invoked on PWM counter reset. If set to
|
||||
* @p NULL then the callback is disabled.
|
||||
*/
|
||||
pwmcallback_t callback;
|
||||
/**
|
||||
* @brief Channels configurations.
|
||||
*/
|
||||
PWMChannelConfig channels[PWM_CHANNELS];
|
||||
/* End of the mandatory fields.*/
|
||||
} PWMConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a PWM driver.
|
||||
*/
|
||||
struct PWMDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
pwmstate_t state;
|
||||
/**
|
||||
* @brief Current driver configuration data.
|
||||
*/
|
||||
const PWMConfig *config;
|
||||
/**
|
||||
* @brief Current PWM period in ticks.
|
||||
*/
|
||||
pwmcnt_t period;
|
||||
/**
|
||||
* @brief Mask of the enabled channels.
|
||||
*/
|
||||
pwmchnmsk_t enabled;
|
||||
/**
|
||||
* @brief Number of channels in this instance.
|
||||
*/
|
||||
pwmchannel_t channels;
|
||||
#if defined(PWM_DRIVER_EXT_FIELDS)
|
||||
PWM_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
_Bool periodic_callback_enabled;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Changes the period the PWM peripheral.
|
||||
* @details This function changes the period of a PWM unit that has already
|
||||
* been activated using @p pwmStart().
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The PWM unit period is changed to the new value.
|
||||
* @note The function has effect at the next cycle start.
|
||||
* @note If a period is specified that is shorter than the pulse width
|
||||
* programmed in one of the channels then the behavior is not
|
||||
* guaranteed.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] period new cycle time in ticks
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pwm_lld_change_period(pwmp, period) _pwm_lld_change_period(pwmp, period)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if (NUC123_PWM_USE_PWM1 == TRUE) && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// void pwm_lld_init(void);
|
||||
// void pwm_lld_start(PWMDriver *pwmp);
|
||||
// void pwm_lld_stop(PWMDriver *pwmp);
|
||||
// void pwm_lld_enable_channel(PWMDriver *pwmp,
|
||||
// pwmchannel_t channel,
|
||||
// pwmcnt_t width);
|
||||
// void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
|
||||
// void pwm_lld_enable_periodic_notification(PWMDriver *pwmp);
|
||||
// void pwm_lld_disable_periodic_notification(PWMDriver *pwmp);
|
||||
// void pwm_lld_enable_channel_notification(PWMDriver *pwmp,
|
||||
// pwmchannel_t channel);
|
||||
// void pwm_lld_disable_channel_notification(PWMDriver *pwmp,
|
||||
// pwmchannel_t channel);
|
||||
// void pwm_lld_serve_interrupt(PWMDriver *pwmp);
|
||||
|
||||
void pwm_lld_enable_period_int(PWM_T *pwm, uint32_t pwmChannel, uint32_t periodType);
|
||||
void pwm_lld_disable_period_int(PWM_T *pwm, uint32_t pwmChannel);
|
||||
void pwm_lld_start(PWM_T *pwm, uint32_t mask);
|
||||
void pwm_lld_clear_period_int(PWM_T *pwm, uint32_t pwmChannel);
|
||||
uint32_t pwm_lld_get_period_int(PWM_T *pwm, uint32_t pwmChannel);
|
||||
uint32_t pwm_lld_config_output_channel(PWM_T *pwm, uint32_t channel, uint32_t freq, uint32_t duty);
|
||||
void pwm_lld_init(void);
|
||||
void pwm_lld_start(PWMDriver *pwmp);
|
||||
void pwm_lld_stop(PWMDriver *pwmp);
|
||||
void pwm_lld_enable_channel(PWMDriver *pwmp,
|
||||
pwmchannel_t channel,
|
||||
pwmcnt_t width);
|
||||
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
|
||||
void pwm_lld_enable_periodic_notification(PWMDriver *pwmp);
|
||||
void pwm_lld_disable_periodic_notification(PWMDriver *pwmp);
|
||||
void pwm_lld_enable_channel_notification(PWMDriver *pwmp,
|
||||
pwmchannel_t channel);
|
||||
void pwm_lld_disable_channel_notification(PWMDriver *pwmp,
|
||||
pwmchannel_t channel);
|
||||
void _pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// #endif /* HAL_USE_PWM */
|
||||
#endif /* HAL_USE_PWM */
|
||||
|
||||
#endif /* HAL_PWM_LLD_H */
|
||||
|
||||
|
|
Loading…
Reference in New Issue