From 5130e47f350b9efcaf36b00085000a5cd666fbb2 Mon Sep 17 00:00:00 2001 From: "Rafael V. Volkmer" Date: Fri, 15 Aug 2025 23:28:59 -0300 Subject: [PATCH 1/3] am62ax/px: dpl: add TimerP_setOvfMask() to program TOWR Introduce a new helper in TimerP to configure the TIMER_TOWR register. - TIMER_TOWR (Timer Overflow Wrapper Register) is 16-bit wide and defines how many overflow events are masked before an interrupt is generated. - For example, writing 3 causes the timer to raise the overflow interrupt only on the third overflow occurrence. - This allows coarser interrupt pacing and can help reduce ISR load. The new function provides a simple and safe way to program this register. Signed-off-by: Rafael V. Volkmer --- source/kernel/dpl/TimerP.h | 12 +++++++++++- source/kernel/nortos/dpl/common/TimerP.c | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/source/kernel/dpl/TimerP.h b/source/kernel/dpl/TimerP.h index 4d72dd5d8b..14c545cdde 100755 --- a/source/kernel/dpl/TimerP.h +++ b/source/kernel/dpl/TimerP.h @@ -46,7 +46,7 @@ extern "C" { * * This module define's generic APIs to configure and control a timer * Depending on the SOC there can be different timer implementation's - * + * * For more details and example usage, see \ref KERNEL_DPL_TIMER_PAGE * * Timer is used by \ref KERNEL_DPL_CLOCK_PAGE to generate system ticks. @@ -190,6 +190,16 @@ void TimerP_clearOverflowInt(uint32_t baseAddr); */ uint32_t TimerP_isOverflowed(uint32_t baseAddr); +/** + * \brief Configure masked overflows for the timer + * + * \note Ensure the base address is valid before calling this function. + * + * \param baseAddr [in] HW timer base address + * \param value [in] Value to be set in the timer overflow register + */ +void TimerP_configMaskedOverflows(volatile uint32_t baseAddr, uint16_t value); + /** @} */ #ifdef __cplusplus diff --git a/source/kernel/nortos/dpl/common/TimerP.c b/source/kernel/nortos/dpl/common/TimerP.c index e33a1e2d7e..6d7f606735 100755 --- a/source/kernel/nortos/dpl/common/TimerP.c +++ b/source/kernel/nortos/dpl/common/TimerP.c @@ -45,6 +45,7 @@ #define TIMER_TCRR (0x3cu) #define TIMER_TLDR (0x40u) #define TIMER_TWPS (0x48u) +#define TIMER_TOWR (0x6Cu) #define TIMER_TCLR_PEND_SHIFT (0U) #define TIMER_TCRR_PEND_SHIFT (1U) #define TIMER_TLDR_PEND_SHIFT (2U) @@ -253,3 +254,12 @@ uint32_t TimerP_isOverflowed(uint32_t baseAddr) return ((val >> TIMER_OVF_INT_SHIFT) & 0x1U); } + +void TimerP_configMaskedOverflows(volatile uint32_t baseAddr, uint16_t value) +{ + volatile uint32_t *towr_addr = NULL; + + towr_addr = (volatile uint32_t *)(baseAddr + TIMER_TOWR); + + *towr_addr = (uint32_t)value; +} From 90508360ef897f80165f74598e2d1607218bc0da Mon Sep 17 00:00:00 2001 From: "Rafael V. Volkmer" Date: Fri, 15 Aug 2025 23:31:06 -0300 Subject: [PATCH 2/3] am62ax/px: dpl: add TimerP_setCompare to set TMAR value The TIMER_TMAR register stores the timer compare value. When the counter matches this value, a compare event is triggered. This commit adds the TimerP_setCompare() function to allow setting the compare value through the DPL API, enabling users to configure the timer compare register without direct register access. Signed-off-by: Rafael V. Volkmer --- source/kernel/dpl/TimerP.h | 10 ++++++++++ source/kernel/nortos/dpl/common/TimerP.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/source/kernel/dpl/TimerP.h b/source/kernel/dpl/TimerP.h index 14c545cdde..3c9bfb36f0 100755 --- a/source/kernel/dpl/TimerP.h +++ b/source/kernel/dpl/TimerP.h @@ -200,6 +200,16 @@ uint32_t TimerP_isOverflowed(uint32_t baseAddr); */ void TimerP_configMaskedOverflows(volatile uint32_t baseAddr, uint16_t value); +/** + * \brief Set the compare value for the timer + * + * \note Ensure the timer is properly configured before setting the compare value. + * + * \param baseAddr [in] HW timer base address + * \param value [in] Compare value to be set + */ +void TimerP_setCompare(volatile uint32_t baseAddr, uint16_t value); + /** @} */ #ifdef __cplusplus diff --git a/source/kernel/nortos/dpl/common/TimerP.c b/source/kernel/nortos/dpl/common/TimerP.c index 6d7f606735..ad4bb8e0f4 100755 --- a/source/kernel/nortos/dpl/common/TimerP.c +++ b/source/kernel/nortos/dpl/common/TimerP.c @@ -45,6 +45,7 @@ #define TIMER_TCRR (0x3cu) #define TIMER_TLDR (0x40u) #define TIMER_TWPS (0x48u) +#define TIMER_TMAR (0x4Cu) #define TIMER_TOWR (0x6Cu) #define TIMER_TCLR_PEND_SHIFT (0U) #define TIMER_TCRR_PEND_SHIFT (1U) @@ -263,3 +264,12 @@ void TimerP_configMaskedOverflows(volatile uint32_t baseAddr, uint16_t value) *towr_addr = (uint32_t)value; } + +void TimerP_setCompare(volatile uint32_t baseAddr, uint16_t value) +{ + volatile uint32_t *tmar_addr = NULL; + + tmar_addr = (volatile uint32_t *)(baseAddr + TIMER_TMAR); + + *tmar_addr = (uint32_t)value; +} From 8aab20a1edf465599b2a9c2efe51104d7e12c223 Mon Sep 17 00:00:00 2001 From: "Rafael V. Volkmer" Date: Fri, 15 Aug 2025 23:35:46 -0300 Subject: [PATCH 3/3] am62ax/px: dpl: add PWM configuration helper with validation Introduce TimerP_configPwm() to configure the GP timer for PWM output. The helper programs TLDR/TCRR/TMAR based on frequency and duty cycle, using a nanosecond timebase and a 32-bit counter range. This provides a safer, single entry point to set PWM frequency and duty cycle while preserving the DPL timer semantics. Signed-off-by: Rafael V. Volkmer --- source/kernel/dpl/TimerP.h | 19 ++++++++ source/kernel/nortos/dpl/common/TimerP.c | 56 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/source/kernel/dpl/TimerP.h b/source/kernel/dpl/TimerP.h index 3c9bfb36f0..f2cc752a7a 100755 --- a/source/kernel/dpl/TimerP.h +++ b/source/kernel/dpl/TimerP.h @@ -210,6 +210,25 @@ void TimerP_configMaskedOverflows(volatile uint32_t baseAddr, uint16_t value); */ void TimerP_setCompare(volatile uint32_t baseAddr, uint16_t value); +/** + * \brief Configure the timer for PWM mode + * + * \note Ensure the timer is properly initialized before calling this function. + * The function calculates and sets the timer registers to generate the + * desired PWM signal based on the provided frequency and duty cycle. + * Remember to multiplex the output pins correctly for the + * configured timer. + * + * \param baseAddr [in] HW timer base address + * \param params [in] Pointer to TimerP_Params containing clock settings + * \param frequency [in] Desired PWM frequency in Hz + * \param duty_cycle [in] Duty cycle percentage (0-100) + * + * \return 0 on success, -22 if input parameters are invalid + */ +int TimerP_configPwm(volatile uint32_t baseAddr, TimerP_Params *params, + uint32_t frequency, uint8_t duty_cycle); + /** @} */ #ifdef __cplusplus diff --git a/source/kernel/nortos/dpl/common/TimerP.c b/source/kernel/nortos/dpl/common/TimerP.c index ad4bb8e0f4..a5e1ecde88 100755 --- a/source/kernel/nortos/dpl/common/TimerP.c +++ b/source/kernel/nortos/dpl/common/TimerP.c @@ -273,3 +273,59 @@ void TimerP_setCompare(volatile uint32_t baseAddr, uint16_t value) *tmar_addr = (uint32_t)value; } + +int TimerP_configPwm(volatile uint32_t baseAddr, TimerP_Params *params, + uint32_t frequency, uint8_t duty_cycle) +{ + volatile uint32_t *tldr_reg = (volatile uint32_t *)(baseAddr + TIMER_TLDR); + volatile uint32_t *tcrr_reg = (volatile uint32_t *)(baseAddr + TIMER_TCRR); + volatile uint32_t *tmar_reg = (volatile uint32_t *)(baseAddr + TIMER_TMAR); + volatile uint32_t *twps_reg = (volatile uint32_t *)(baseAddr + TIMER_TWPS); + + uint64_t abs_clock = 0U; + uint64_t period_in_ns = 0U; + uint64_t counter_ticks = 0U; + uint64_t duty_ticks = 0U; + + if (params == NULL) + return -22; + + if (params->inputPreScaler == 0U || params->inputClkHz == 0U) + return -22; + + if (duty_cycle > 100U || frequency == 0U) + return -22; + + if ((params->inputClkHz % params->inputPreScaler) != 0U) + return -22; + + abs_clock = (uint64_t)(params->inputClkHz / params->inputPreScaler); + + period_in_ns = TIME_IN_NANO_SECONDS / (uint64_t)frequency; + + counter_ticks = (abs_clock * period_in_ns) / TIME_IN_NANO_SECONDS; + if (counter_ticks == 0U) + counter_ticks = 1U; + + duty_ticks = counter_ticks - ((counter_ticks * (uint64_t)duty_cycle) / 100U); + + if (duty_ticks == 0U) + duty_ticks = 1U; + if (duty_ticks >= counter_ticks) + duty_ticks = counter_ticks - 1U; + + /* TLDR */ + while ((*twps_reg & TIMER_TLDR_PEND_MASK) == TIMER_TLDR_PEND_MASK) { } + *tldr_reg = (uint32_t)(MAX_TIMER_COUNT_VALUE - (uint32_t)(counter_ticks - 1U)); + while ((*twps_reg & TIMER_TLDR_PEND_MASK) == TIMER_TLDR_PEND_MASK) { } + + /* TCRR */ + while ((*twps_reg & TIMER_TCRR_PEND_MASK) == TIMER_TCRR_PEND_MASK) { } + *tcrr_reg = (uint32_t)(MAX_TIMER_COUNT_VALUE - (uint32_t)(counter_ticks - 1U)); + while ((*twps_reg & TIMER_TCRR_PEND_MASK) == TIMER_TCRR_PEND_MASK) { } + + /* TMAR */ + *tmar_reg = (uint32_t)(MAX_TIMER_COUNT_VALUE - (uint32_t)(duty_ticks - 1U)); + + return 0; +}