NUC123 PWM module

This commit is contained in:
Alex Lewontin 2020-12-08 22:13:49 -05:00
parent 5f800992c3
commit c1a3e2e93d
No known key found for this signature in database
GPG Key ID: 52A3855FC3BB8CD7
3 changed files with 562 additions and 201 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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 */