cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G030K6T6 Timer 16 seems to use SYSCLK instead of PCLK

JohannesSt
Associate

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
}
17 REPLIES 17
TDK
Super User

The code is certainly not ideal, but your assumptions are correct. What makes you think it's at the wrong frequency? Start the timer and do a HAL_Delay(100) or something and calculate the CNT before/after to get the frequency. Set prescaler so it doesn't overflow.

If you feel a post has answered your question, please click "Accept as Solution".

First of all, thank you for your quick response.

 

I would like to further adapt or improve the code. In general, I don't want to use any ready-made libraries, as I am doing this for learning purposes. For this reason, I don't want to use the HAL function.

 

What makes you think it's at the wrong frequency?” -> The code works as written; the LED flashes every 3 seconds. However, to my understanding, this should not be the case, as the correct prescaler value for the timer would be 3, assuming the input frequency is 4 MHz. However, I have set the prescaler value to 15 – the code works, which surprises me a little and, to be honest, I don't understand.

 

If it helps, I could also upload screenshots of the register values in debug mode, but these also match the values in the code.

Hi @JohannesSt ,

At this point I don't have answers to your question, but I am curious: how did you write the original post? In particular, did you emphasise the text in bold manually, or did you use some program to do that for you?

Thanks,

JW

> RCC->CFGR &= RCC_CFGR_SWS_HSISYS;

This one doesn't look right.

 

> I could also upload screenshots of the register values

Probably worth doing.

 

Consider only writing to RCC registers once as HAL does, instead of doing it piece-meal. You may be passing through invalid configurations.

If you feel a post has answered your question, please click "Accept as Solution".

Sorry, but boys , that for learning omit learn HAL first and then dont understand, waste own and others time.

TIM1SEL[1:0] value Clock source

00PCLK (default)
01SYSCLK
10HSI16
11PLLQ (if configured)

@MM..1,

That would be for TIM1; and not on 'G030.

JW

MM1_0-1748965826756.png

 

Hi @waclawek.jan ,

i editet the written text manually over the toolbar, for my code i used the "insert code" function:

JohannesSt_1-1748967622851.png

 

Hope this answers your question? :)

 

First thank you for you advise, i willl do this in the future.

 

Here a sreenshot of the RCC CFGR register:

JohannesSt_2-1748967872443.png

MCOPRE = 0x7 -> sets prescaler for the MCO output to 128 -> correct
MCOSEL = 0x1 -> Set SYSCLK for MCO output -> correct
MCO2PRE and MCO2SEL these two parameters are not adjusted
PPRE = 0x0 -> APB prescale = 1 -> correct
HPRE = 0x9 -> AHB prescaler = 4 -> correct
SWS = 0x0 -> SYSCLK = HSISYS (read only - therefore correct)
SW = 0x0 -> SYSCLK = HSISYS -> correct

 

So the RCC CFGR register seems to be correct in my point of view - Nevertheless, I will follow your advice on writing the registers in the future :)

 

Here is the screenshot for timer 16 - at the time before the delay function is called.

JohannesSt_3-1748968400498.png

Does this help? :)