2025-06-03 5:20 AM
Hi all.
I have a small problem understanding the timer 16 of the STM32G030K6T6 microcontroller. According to the data sheet, it should use the TIMPCLK clock, which corresponds to the PCLK clock.
My system clock is supplied by the HSI (SYSCLK = 16 MHz). I have set my AHB prescaler to 4 and the APB prescaler to 1, so HCLK = PCLK = 4 MHz. Since my APB prescaler is equal to 1, the timer clock should be TIMPCLK = PCLK = 4 MHz.
I then implemented a delay function with timer 16. So that the counter runs up in 1 us steps, I have to set the prescaler of the timer to 15 instead of 3. This means that the timer 16 still has a 16 MHz signal as an input signal.
Now my question is: Have I misunderstood something here, is the data sheet wrong or is my code not correct? I have attached the quotes from the data sheet and my code. I hope you can help me.
Datasheet RM0454 Rev 5:
"Timer clock
The timer clock TIMPCLK is derived from PCLK (used for APB) as follows:
1. If the APB prescaler is set to 1, TIMPCLK frequency is equal to PCLK frequency.
2. Otherwise, the TIMPCLK frequency is set to twice the PCLK frequency."
"The peripherals are clocked with the clocks from the bus they are attached to (HCLK for
AHB, PCLK for APB) except:
• TIMx, with these clock sources to select from:
– TIMPCLK (selectable for all timers) running at PCLK frequency if the APB
prescaler division factor is set to 1, or at twice the PCLK frequency otherwise"
My code:
/**
******************************************************************************
* @file : main.c
* @author : Johannes S.
* @brief : Main program body
* @microcontroller : STM32G030K6T6
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include <main.h>
#include <stdint.h>
#include <StartUp.h>
/* Private typedef -----------------------------------------------------------*/
// Enum for the colors of the RGB LED
typedef enum {
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_INVALID
} LED_Color;
// Enum for GPIO Output Control
typedef enum {
HIGH,
LOW,
TOGGLE,
GPIO_State_INVALID
} GPIO_State;
/* Private function ---------------------------------------------------------*/
void LED_RGB(LED_Color color, uint8_t state);
void GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t Pin, uint8_t state);
void Button_1_SetInterrupt(void);
void GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t Pin);
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
/**
* Main function
*/
int main(void)
{
// Setup Microcontroller
Flash_Init(); // Set flash wait cycles
PWR_MODULE_Init(); // Set Internal voltage regulator
SystemClock_Config(); // Configure Clocks
GPIO_Init(); // Configure GPIO
TIM16_us_Tick(); // Configure Timer 16 for delay function
// Test RGB LED
LED_RGB(COLOR_RED, HIGH);
LED_RGB(COLOR_BLUE, HIGH);
LED_RGB(COLOR_GREEN, HIGH);
// Configure Button No. 1 as interrupt input
Button_1_SetInterrupt();
// Turn red and blue led off - to better recognize flashing green led
LED_RGB(COLOR_RED, LOW);
LED_RGB(COLOR_BLUE, LOW);
// measure_timer_duration();
// While loop - main loop
while(1)
{
// Test LED blink
delay_ms(3000);
LED_RGB(COLOR_GREEN, TOGGLE);
}
}
/**
* Function for controlling the RGB LED
*/
void LED_RGB(LED_Color color, GPIO_State state)
{
uint32_t pin;
GPIO_TypeDef *LED_GPIO;
// Color assignment based on Enum
switch (color) {
case COLOR_RED:
pin = LED_RGB_Red_Pin;
LED_GPIO = LED_RGB_Red_GPIO_Port;
break;
case COLOR_GREEN:
pin = LED_RGB_Green_Pin;
LED_GPIO = LED_RGB_Green_GPIO_Port;
break;
case COLOR_BLUE:
pin = LED_RGB_Blue_Pin;
LED_GPIO = LED_RGB_Blue_GPIO_Port;
break;
default:
return; // Invalid color, do nothing
}
// Switch on or off based on the status
switch (state) {
case HIGH:
GPIO_WritePin(LED_GPIO, pin, 1); // turn LED on
break;
case LOW:
GPIO_WritePin(LED_GPIO, pin, 0); // turn LED off
break;
case TOGGLE:
GPIO_TogglePin(LED_GPIO, pin); // toggle LED
break;
default:
return; // Invalid color, do nothing
}
}
/**
* Function for controlling a GPIO output
*/
void GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t Pin, uint8_t state) {
if (state) {
GPIOx->BSRR = Pin; // set GPIO high (Bit Set Reset Register - BSRR)
} else {
GPIOx->BRR = Pin; // set GPIO low (Bit Reset Register - BRR)
}
}
/**
* Toggle GPIO Outputs
*/
void GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t Pin) {
GPIOx->ODR ^= Pin;
}
/**
* Declare button 1 (PC6) as interrupt
*/
void Button_1_SetInterrupt(void)
{
// SYSCFG (System Configuration Controller) - Activate clock
// Required for: “Flag pending interrupts from each interrupt line”
// See: SYSCFG interrupt line 7 status register (SYSCFG_ITLINE7) - page 196
RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; // Activate Clock for SYSCFG
// Connect pin PC6 to EXTI6 (SYSCFG Configuration)
EXTI->EXTICR[1] = 0x00000000; // Reset Register
EXTI->EXTICR[1] = 0x02 << 16; // Connect EXTI with PC6
EXTI->FTSR1 = 0x1UL << 6; // Interupt triggerd with falling edge
// Activate interrupt
// See en.stm32u0-system-extended-interrupt-event-controller-exti.pdf page 6
// EXTI_FTSR1 output is AND linked with EXTI_IMR1 -> therefore interrupt must be activated here
EXTI->IMR1 |= EXTI_IMR1_IM6; // Unmask interrupt (activate)
// Activate interrupt and set priority in NVIC (Nested vectored interrupt controller)
// Configuration for EXTI4_15
NVIC_SetPriority(EXTI4_15_IRQn, 1); // Set priority to 1 (low)
NVIC_EnableIRQ(EXTI4_15_IRQn); // Activate interrupt in NVIC
}
/**
* Interrupt service routine for button 1 (PC6)
*/
void EXTI4_15_IRQHandler(void)
{
LED_RGB(COLOR_BLUE, TOGGLE); // Toggle blue LED
EXTI->FPR1 |= 0x1UL << 6; // Delete interrupt pending register (otherwise interrupt is triggered again immediately)
}
void TIM16_us_Tick(void)
{
// Uses PCLK Clock = 16 MHz as source
// -> According to the data sheet and with the AHB prescaler of 2, this should actually be 8 MHz.
// However, the input frequency of the timer seems to be independent of the AHB prescaler.
// Enable TIM16 Clock
RCC->APBENR2 |= (0x1UL << 17);
// Set CR1 register
TIM16->CR1 &= ~TIM_CR1_UIFREMAP;
TIM16->CR1 |= TIM_CR1_ARPE;
// Set PSC register
TIM16->PSC = 15; // Set prescaler to 15 -> 1us task for timer (f_timer = f_pclk / (1+prescaler))
// Set ARR register
TIM16->ARR = 0xFFFF; // Maximum value (optional, depending on application)
// Activate Timer
//TIM16->CR1 |= TIM_CR1_CEN;
}
void delay_ms(uint16_t ms) {
// Timer-Setup: Prescaler and ARR are already configured,
// that the timer counts with 1 µs resolution.
TIM16->CR1 |= TIM_CR1_CEN; // start Timer
for (uint16_t i = 0; i < ms; i++) {
TIM16->CNT = 0; // Reset counter to 0
TIM16->SR &= ~TIM_SR_UIF; // Reset update flag (UIF)
//while (TIM16->CNT != 0); // Wait until the counter has been set to 0
//TIM16->CR1 |= TIM_CR1_CEN; // start Timer
while (TIM16->CNT < 1000); // wait 1000 µs = 1 ms
//TIM16->CR1 &= ~TIM_CR1_CEN; // stop Timer
}
TIM16->CR1 &= ~TIM_CR1_CEN; // stop Timer
// Edit later so that function is leaner and more accurate
}
/**
* Function to check timer
*/
void measure_timer_duration(void) {
TIM16->CNT = 0;
TIM16->CR1 |= TIM_CR1_CEN;
for (volatile int i = 0; i < 1000000; ++i); // Short known waiting time
TIM16->CR1 &= ~TIM_CR1_CEN;
uint32_t count = TIM16->CNT;
}
/**
******************************************************************************
* @author J.Stuermann
* @file StartUp.h
* @brief Header file for StartUp.c which sets Register for SMT32G030K6T6 for
* Start Up routine.
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef STARTUP_H_
#define STARTUP_H_
// Constants
// Functions
void Flash_Init(void);
void PWR_MODULE_Init(void);
void SystemClock_Config(void);
void GPIO_Init(void);
#endif /* STARTUP_H_ */
/**
******************************************************************************
* @author J.Stuermann
* @file StartUp.h
* @brief Header file for StartUp.c which sets Register for SMT32G030K6T6 for
* Start Up routine.
******************************************************************************
*/
/**
* Procedure:
* 1. Set flash wait cycles
* 2. Set internal voltage regulator
* 3. Configure System Clock
* 4. Configure GPIOs
* 5.
* 6.
*
*/
/* Includes ------------------------------------------------------------------*/
#include "StartUp.h"
/* Functions -----------------------------------------------------------------*/
/**
* Set flash wait cycles
*/
void Flash_Init(void) {
// Set flash wait cycles based on the system clock frequency.
// 0 Wait-State for <24 MHz
// 1 Wait-State für <48 MHz
// 2 Wait-State für <64 MHz
FLASH->ACR &= ~0x7UL; // Set Wait-State = 0
}
/**
* Set internal voltage regulator to “Range 1” -> 1.2 V is required for system clock up to 64 MHz
*/
void PWR_MODULE_Init(void)
{
// Reset
PWR->CR1 = 0x00000208;
// Voltage scaling range - Range 1 - for 64 Mhz System Frequency
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS_Msk) | PWR_CR1_VOS_0;
// Low power run - off
PWR->CR1 &= ~PWR_CR1_LPR;
}
/**
* System Clock Configuration
* System Clock = 16 MHz
* AHB Clock = 4 MHz
* APB Clock = 4 MHz
*/
void SystemClock_Config(void)
{
// 1. Select internal clock (HSI) with a frequency of 16 MHz
// Clock control register (RCC_CR)
RCC->CR |= RCC_CR_HSION; // turn HSI on
while (!(RCC->CR & RCC_CR_HSIRDY)); // Wait until HSI is stable
RCC->CR &= ~RCC_CR_HSIDIV_Msk; // HSIDIV = 1
RCC->CR &= ~RCC_CR_PLLON; // PLL Clock disable
// Select HSISYS as System Clock
// Set APB and AHB prescaler and disable clock output
// Clock configuration register (RCC_CFGR)
RCC->CFGR &= RCC_CFGR_SWS_HSISYS; // Select HSISYS as System Clock and disable clock output
RCC->CFGR &= ~RCC_CFGR_PPRE_Msk; // Reset APB prescaler = 1
RCC->CFGR &= ~RCC_CFGR_HPRE_Msk; // Reset AHB prescaler = 1
RCC->CFGR |= 0b1001 << RCC_CFGR_HPRE_Pos; // Set AHB prescaler = 4
// MCO clock settings:
// Output: SYSCLK
// Prescaler: 128 to improve measurement (1000 not possible)
RCC->CFGR &= ~RCC_CFGR_MCOSEL_Msk; // Reset MCOSEL (clock selector)
RCC->CFGR |= RCC_CFGR_MCOSEL_0; // choose SYSCLK
RCC->CFGR &= ~RCC_CFGR_MCOPRE_Msk; // Reset MCOPRE (Prescaler)
RCC->CFGR |= (0x7UL << RCC_CFGR_MCOPRE_Pos); // Set Prescaler to 128
}
/**
* Configure GPIOs
*/
void GPIO_Init(void)
{
// 1. activate GPIO Clocks
RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // Activate GPIOA clock (for SPI, external interrupts, etc.)
RCC->IOPENR |= RCC_IOPENR_GPIOBEN; // Activate GPIOB Clock, if required
RCC->IOPENR |= RCC_IOPENR_GPIOCEN; // Activate GPIOC Clock, if required
// Declare GPIOA pin PA10 as input - for later external interrupt - switch next to connector
GPIOA->MODER &= ~GPIO_MODER_MODE10; // Configure PA10 as input
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD10; // Neither pull-up nor pull-down for PA10
// Declare GPIOA pin PC6 as input - for later external interrupt - Additional switch
GPIOC->MODER &= ~GPIO_MODER_MODE6; // Configure PAC6 as input
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPD6; // Neither pull-up nor pull-down for PC6
// GPIO pin PB4 as output for RGB LED (red)
GPIOB->MODER &= ~GPIO_MODER_MODE4; // Reset Port
GPIOB->MODER |= GPIO_MODER_MODE4_0; // Define Pin as Output
GPIOB->OTYPER &= ~GPIO_OTYPER_OT4; // Push-Pull output
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED4; // Reset Speed Setting
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED4_1; // Set to High Speed (second fastest)
// GPIO pin PB6 as output for RGB LED (blue)
GPIOB->MODER &= ~GPIO_MODER_MODE6; // Reset Port
GPIOB->MODER |= GPIO_MODER_MODE6_0; // Define Pin as Output
GPIOB->OTYPER &= ~GPIO_OTYPER_OT6; // Push-Pull output
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED6; // Reset Speed Setting
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED6_1; // Set to High Speed (second fastest)
// GPIO pin PB5 as output for RGB LED (green)
GPIOB->MODER &= ~GPIO_MODER_MODE5; // Reset Port
GPIOB->MODER |= GPIO_MODER_MODE5_0; // Define Pin as Output
GPIOB->OTYPER &= ~GPIO_OTYPER_OT5; // Push-Pull output
GPIOB->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5; // Reset Speed Setting
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEED5_1; // Set to High Speed (second fastest)
// Define GPIO pin PA9 as MCO -> MCO - SYSCLK clock output
GPIOA->MODER &= ~GPIO_MODER_MODE9; // Reset Port
GPIOA->MODER |= GPIO_MODER_MODE9_1; // Define Pin as alternate function mode
GPIOA->OTYPER &= ~GPIO_OTYPER_OT9; // Push-Pull output
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED9; // Reset Speed Setting
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEED9_1; // Set to High Speed (second fastest)
GPIOA->AFR[2] &= GPIO_AFRH_AFSEL9; // Reset alternate function register für PA9.
// The reset value is also directly correct here to select MCO.
// In the “normal” data sheet there is a table showing which number stands for
// which alternative function, here AF0 stands for MCO
}
2025-06-03 9:38 AM
> Hope this answers your question? :)
Thanks. I know the question is strange, but in the last months we see increase in messages with highlighed-in-bold portions, and that we haven't seen that in the past, so that's why I suspected there's some tool around which does such highlight automagically.
Meantime, a couple of questions:
0. what happens if you set TIM prescaler to 3?
1. I see some code related to MCO - have you measured it? Try to set MCO to SYSCLK and to HSI16, post observations.
2. have you tried to set TIM to generate PWM and output to some of the pins to measure?
3. read out and check/post content of RCC and TIM registers
JW
2025-06-03 9:39 AM
OP has AHB prescaler /4 and APB prescaler /1.
His observation is, that TPCLK is 16MHz under these circumstances.
JW
2025-06-03 9:40 AM
Hi @MM..1 ,
thank you in general for wanting to help :)
The inputs for the timer 16 are a little bit different:
TI1SEL is set to 0x0 here, correspondingly TIM16_CH1 is selected here. My understanding is that in this case TIMPCLK is used as the input for the counter (?) .
2025-06-03 9:53 AM
OK I think I know what's the problem.
TIMx_PSC is unconditionally preloaded. It means, the value written to it is not used in the internal prescaler, until there is an Update event. Update is basically an overflow; but as you use the timer so that you always stop it before it reaches the overflow, your TIMx_PSC is never applied.
You can test this assertion by using various values for TIMx_PSC, the result should be the same.
I also believe that you compile with all optimizations off, which results in the loopdelay taking longer than just the pure 1000 timer clocks, masking the real timing of the timer.
JW
2025-06-03 9:58 AM
TI1SEL dont setup timer clk.
2025-06-03 10:55 AM
Which register is used to select the input of the counter? :)
Ah okay, no I adjusted the text manually, due to the length of the text I thought it would be easier to understand that way.
"You can test this assertion by using various values for TIMx_PSC, the result should be the same." I'll test it out and report back later :)
2025-06-04 12:50 PM
Hi all,
@waclawek.jan i added the following line to update the prescaler value:
// Create Update event to force new values into PSC
TIM16->EGR |= 0x0001;
Afterwards i also checked the register values. The prescaler value is adopted correctly. However, the delay function is still only correct with a prescaler value of 15. I have also entered a prescaler value of 3 which would actually be correct according to the documentation, but here the “blink” frequency is then too fast.
So the problem remains. Do you or anyone else have an idea what the problem could be?
2025-06-04 1:16 PM
Maybe order is required , try inspire
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, 3);
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_TIM16_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
/* HSI configuration and activation */
LL_RCC_HSI_Enable();
while(LL_RCC_HSI_IsReady() != 1)
{
}
/* Set AHB prescaler*/
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_4);
/* Sysclk activation on the HSI */
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
{
}
/* Set APB1 prescaler*/
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_Init1msTick(4000000);
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
LL_SetSystemCoreClock(4000000);
}
/**
* @brief TIM16 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM16_Init(void)
{
/* USER CODE BEGIN TIM16_Init 0 */
/* USER CODE END TIM16_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM16);
/* TIM16 interrupt Init */
NVIC_SetPriority(TIM16_IRQn, 0);
NVIC_EnableIRQ(TIM16_IRQn);
/* USER CODE BEGIN TIM16_Init 1 */
/* USER CODE END TIM16_Init 1 */
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 65535;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM16, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIM16);
/* USER CODE BEGIN TIM16_Init 2 */
/* USER CODE END TIM16_Init 2 */
}