STM32F756VGH6 issues with establishing 2 timers at the same time

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
174
Hi,

I am relatively new to STM32 ecosystem. I chose the register programming approach as I find it more beneficial for deep knowledge. At the moment I am learning how setup timer interrupt, but sadly I ran into another problem. I use Timer 1 to generate a precise delay. Timer 8 is meant for for experimenting with interrupts. I have a led blink in the main while loop and Timer 8 is meant to be triggered upon button press which generates an external interrupt (code has 2 interrupts in general - one for button, another one for Timer 8).

Everything except Timer 8 part is well tested and verified to be working in the earlier stages. My main problem is that the code jams or enters an infinite loop (I am not even sure) as soon as I call the Timer8_Setup function (if I comment and omit that function call, the code works normal without the timer 8).

I have tried commenting line after line in the Timer 8 setup to see where the problem is. Sadly I had to comment completely everything in order to get it working again - effectively eliminating the Timer 8 setup.

Have been reading the datasheet and analyzing the architecture of timers, but still could not find the problem. Any observations why the Timer 8 setup completely breaks my code are appreciated. Here is my code down bellow, Timer1, Timer8, and main while are the most relevant parts:


C:
#include "main.h"

void Core_Clock_Setup (void){

    RCC->CR |= RCC_CR_HSEON;               //Set the clock source to external crystal/resonator (HSE)
    while (!(RCC->CR & RCC_CR_HSEON));       //Wait until clock gets stable

    RCC->APB1ENR |= RCC_APB1ENR_PWREN;     //Enable power interface clock
    PWR->CR1  &= ~(1U << 14);
    PWR->CR1  &= ~(1U << 15);              //Set internal voltage regulator to is reset value (scale 1)

    FLASH->ACR &= ~FLASH_ACR_ARTEN;        //Disable ART accelerator
    FLASH->ACR &= ~FLASH_ACR_ARTRST;       //Reset ART accelerator
    FLASH->ACR |= FLASH_ACR_PRFTEN;        //Enable prefetch
    FLASH->ACR |= FLASH_ACR_LATENCY_6WS;   //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)

    //@ 25 MHz crystal, 200 MHz core clock configuration down below

    RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
    RCC->CFGR &= ~(1 << 4);                //Core clock division by 1 (core clock is not devided)
    RCC->CFGR &= ~(1 << 5);
    RCC->CFGR &= ~(1 << 6);
    RCC->CFGR &= ~(1 << 7);

    RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry

    RCC->PLLCFGR &= ~(1 << 16);              //PLLP Setting corresponding PLL prescalers (division by 2)
    RCC->PLLCFGR &= ~(1 << 17);

    //RCC->PLLCFGR &= ~((1 << 6) - 1);
    //RCC->PLLCFGR |= (16 & ((1 << 6) - 1));  //PLLM Setting corresponding PLL prescalers (division by 16)
    RCC->PLLCFGR |= (16 << 0);

    RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
    RCC->PLLCFGR |= (256 << 6);            //PLLN Setting corresponding PLL prescalers ( multiplication by 256)

    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;      //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;      //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)

    RCC->CR |= RCC_CR_PLLON;               //Enable PLL
    while (!(RCC->CR & RCC_CR_PLLRDY));       //Wait until PLL gets stable

    RCC->CFGR |= RCC_CFGR_SW_PLL;          //PLL is set to be core clock


    //RCC->CFGR |= RCC_CFGR_SW_HSE;        //HSE is set to be core clock

    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}

//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void Timer1_Setup(void){ //16 bit advanced timer

    RCC->DCKCFGR1 &= ~ (1 << 24);          //TIMxCLK = 2xPCLKx
    //When TIMPRE bit of the RCC_DCKCFGR1 register is reset, if APBx prescaler is 1, then TIMxCLK = PCLKx, otherwise
    //TIMxCLK = 2x PCLKx.
    // When TIMPRE bit in the RCC_DCKCFGR1 register is set, if APBx prescaler is 1,2 or 4, then TIMxCLK = HCLK, otherwise
    //TIMxCLK = 4x PCLKx.
    //TIM1 CLK is HCLK in this case
    RCC->APB2ENR = (1 << 0);               //Enable Timer 1 clock

    TIM1->PSC = 99;                        //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ
    TIM1->ARR = 0xFFFF;                    //Auto reload at 100 ticks -> (around 100 micro seconds at 100 MHz timer clock)/2

    TIM1->CR1 = (1 << 0);                  //Enable Timer 1 counter
    while(!(TIM1->SR & (1<<0)));           //Wait until timer update bit is set
    TIM1->CR1 &= ~(1 << 0);                //Disable Timer 1 counter
    TIM1->CNT = 0;

}

void Timer8_Setup(void){ //16 bit advanced timer

    RCC->APB2ENR |= (1 << 1);              //Enable Timer 8 clock

    TIM8->PSC = 99;                        //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ
    TIM8->ARR = 0xFFFF;                       //Auto reload at 100 ticks -> (around 100 micro seconds at 100 MHz timer clock)/2

    TIM8->CR1 = (1 << 2);                  //Only counter overflow/underflow generates an update interrupt or DMA request if enabled
    TIM8->DIER = (1 << 0);                 //Update interrupt enabled
    TIM8->EGR = (1 << 0);

    NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 1);
    NVIC_EnableIRQ (TIM1_UP_TIM10_IRQn);

    TIM8->CR1 = (1 << 0);                  //Enable Timer 1 counter
    while(!(TIM8->SR & (1<<0)));           //Wait until timer update bit is set

}

void delay_ms (uint16_t ms){
    TIM1->CR1 = (1 << 0);                  //Enable Timer 1 counter
    for(uint16_t i = 0; i<ms; i++)
    {
        TIM1->CNT = 0;                      //Reset counter
        while (TIM1->CNT < 2000);           //Wait until counter reaches desired value
    }
    TIM1->CR1 &= ~(1 << 0);                 //Disable Timer 1 counter
    TIM1->CNT = 0;
}

void TIM1_UP_TIM10_IRQnHandler(void){

    //If multiple timer interrupts are enabled, have to check the status bits, to determine, which of the 1-10 timer interrupts have occurred

    if(TIM8->SR & (1<<0)){

    TIM8->CR1 &= ~(1 << 0);
    TIM8->CNT = 0;

    GPIOC->BSRR = (1 << 15);
    delay_ms(1000);
    GPIOC->BSRR = (1 << 31);

    TIM8->SR &= ~(1<<0);
    }
}

//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){

    //PC14 OUTPUT                                                                  //LEDS
    RCC->AHB1ENR |= (1 << 2);                 //Enable clock for GPIO bank C
    RCC->AHB1ENR |= (1 << 4);                //Enable clock for GPIO bank E
    delay_ms(1);
    GPIOC->MODER |= (0b01 << 28);            //PC14 General purpose output mode
    GPIOC->OTYPER &= ~ (1 << 14);            //PC14 Output push-pull (reset state)
    GPIOC->OSPEEDR |= (0b11 << 28);          //PC14 very high GPIO speed
    GPIOC->PUPDR |= (0b10 << 28);            //PC14 pull down resistors
    //PC15 OUTPUT
    GPIOC->MODER |= (0b01 << 30);            //PC15 General purpose output mode
    GPIOC->OTYPER &= ~ (1 << 15);            //PC15 Output push-pull (reset state)
    GPIOC->OSPEEDR |= (0b11 << 30);          //PC15 very high GPIO speed
    GPIOC->PUPDR |= (0b10 << 30);            //PC15 pull down resistors
    //PE4 OUTPUT
    GPIOE->MODER |= (0b01 << 8);             //PE4 General purpose output mode
    GPIOE->OTYPER &= ~ (1 << 4);             //PE4 Output push-pull (reset state)
    GPIOE->OSPEEDR |= (0b11 << 8);           //PE4 very high GPIO speed
    GPIOE->PUPDR |= (0b10 << 8);             //PE4 pull down resistors
    //PE0 OUTPUT
    GPIOE->MODER |= (0b01 << 0);             //PE0 General purpose output mode
    GPIOE->OTYPER &= ~ (1 << 0);             //PE0 Output push-pull (reset state)
    GPIOE->OSPEEDR |= (0b11 << 0);           //PE0 very high GPIO speed
    GPIOE->PUPDR |= (0b10 << 0);             //PE0 pull down resistors
    //-----------------------------------------------------------------------------------
    //PE5 INPUT                                                                        ALL BUTTONS EXTERNALLY PULLED UP
    GPIOE->MODER |= (0b00 << 10);             //PE5 General purpose input mode
    //PE6 INPUT
    GPIOE->MODER |= (0b00 << 12);             //PE6 General purpose input mode

}

void Interrupt_setup(void){
    //PE5
    RCC->APB2ENR |= (1 << 14);                //System configuration controller clock enabled

    SYSCFG->EXTICR[1] |= (0b0100 << 4);       //0100: PE[x] pin

    EXTI->IMR |= (1<< 5);                     //Interrupt request from line x is not masked

    EXTI->FTSR |= (1<< 5);                    //Falling trigger enabled (for Event and Interrupt) for input line
    EXTI->RTSR &= ~ (1<< 5);                  //Rising trigger disabled (for Event and Interrupt) for input line

    NVIC_SetPriority(EXTI9_5_IRQn, 0);
    NVIC_EnableIRQ (EXTI9_5_IRQn);
}

void EXTI9_5_IRQHandler(void){

    if(EXTI->PR & (1 << 5)){
        EXTI->PR |= (1 << 5);

        //TIM8->CNT = 0;
        //TIM8->CR1 = (1 << 0);
    }
}

int main (void){

    Core_Clock_Setup();
    Timer1_Setup();
    Timer8_Setup();
    GPIO_Setup();
    Interrupt_setup();

    while(1){

        GPIOC->BSRR = (1 << 15);
        delay_ms(100);
        GPIOC->BSRR = (1 << 31);
        delay_ms(100);
        GPIOC->BSRR = (1 << 15);
        delay_ms(100);
        GPIOC->BSRR = (1 << 31);
        delay_ms(100);

    }

}
 

ApacheKid

Joined Jan 12, 2015
1,658
I am using STM32CubeIDE
Try VisualGDB with Visual Studio, the experience is much slicker and your ability to debug and investigate will be enhanced, you can open a Cube project in VGDB too.

Frankly the huge reliance on explicit hex, and, or, not and shifts too, is a common source of extremely frustrating bugs. These kinds of issues should be much easier to reduce and discover, sadly the C language requires a high degree of skill, too much, in order to write even basic code sometimes.
 

ApacheKid

Joined Jan 12, 2015
1,658
Also take a look at this work in progress, something I've been playing around with for fun. It enables us to write more expressive code yet efficient code, no API just raw register manipulation but with none of the shifts, and, or etc etc. Here's what a simple program looks like:

C:
    // Setup the GPIO pins

#define MODER(X) MODER_ ## X

#define MODER_INPUT 0
#define MODER_GENERAL 1
#define MODER_ALTERNATE 2
#define MODER_ANALOG 3
  

    ahb1_ptr->GPIO_D.MODER.MODER_12 = MODER(GENERAL);
    ahb1_ptr->GPIO_D.OTYPER.OT_12 = 0;
    ahb1_ptr->GPIO_D.SPEEDR.SPEED_12 = SPEEDR(LOW);
  
    ahb1_ptr->GPIO_D.MODER.MODER_13 = MODER(GENERAL);
    ahb1_ptr->GPIO_D.OTYPER.OT_13 = 0;
    ahb1_ptr->GPIO_D.SPEEDR.SPEED_13 = SPEEDR(LOW);
  
    ahb1_ptr->GPIO_D.MODER.MODER_14 = MODER(GENERAL);
    ahb1_ptr->GPIO_D.OTYPER.OT_14 = 0;
    ahb1_ptr->GPIO_D.SPEEDR.SPEED_14 = SPEEDR(LOW);
  
    ahb1_ptr->GPIO_D.MODER.MODER_15 = MODER(GENERAL);
    ahb1_ptr->GPIO_D.OTYPER.OT_15 = 0;
    ahb1_ptr->GPIO_D.SPEEDR.SPEED_15 = SPEEDR(LOW);
  
    // Setup TIMER2 approx 1 Hz
  
    ahb1_ptr->RCC.APB1ENR.TIM2_EN = 1;
    apb1_ptr->TIM2.PSC.PSC = 1600 - 1;
    apb1_ptr->TIM2.ARR.ARRH = 0;
    apb1_ptr->TIM2.ARR.ARRL = 10000 - 1;
    apb1_ptr->TIM2.CNT.CNT = 0;
    apb1_ptr->TIM2.CR1.CEN = 1;
Reading this code while looking at the device reference manual is a breeze.
 

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
174
I value your observations about IDE, but that is not the solution. I am after different approach to this problem. I guess I am missing something out at the architectural level of the MCU. There has to be a way to run both these timers simultaneously, but I can not find the reason any setup for the timer 8 jams my code while timer 1 is already initialized and running. I have tried disabling timer 1 clock and counter during timer 8 setup but it did not help.
 

MrChips

Joined Oct 2, 2009
30,946
I don't need to look at a register. As an example, here is what I use to setup Timer8 on APB2 bus:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);

I have many applications running STM32 MCUs with multiple timers. Some timers are running as slaves from a master timer. There is nothing wrong with the hardware. What is wrong is how you have it configured and how you are using the hardware. Your challenge is to write your code in such a way that you can systematically debug the code and find the fault. Good luck with that!
 

Thread Starter

Vilius_Zalenas

Joined Jul 24, 2022
174
I don't need to look at a register. As an example, here is what I use to setup Timer8 on APB2 bus:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);

I have many applications running STM32 MCUs with multiple timers. Some timers are running as slaves from a master timer. There is nothing wrong with the hardware. What is wrong is how you have it configured and how you are using the hardware. Your challenge is to write your code in such a way that you can systematically debug the code and find the fault. Good luck with that!
Well, thats my point exactly :D I am after the configuration issues and I can not find out whats wrong
 

MrChips

Joined Oct 2, 2009
30,946
I have written programs with over a thousand lines of code. How does one debug large complex systems?
You tackle the problem by dividing the entire project into small manageable parts. You test each part one at a time.
Consider the fact that there could be 10 faults present and any one of them could freeze the system. You create your own test procedure to check one section at a time. Program debugging is not something I can teach. This is a technique that you have to develop on your own. I can only demonstrate sound programming styles that will keep the gremlins away.
 

ApacheKid

Joined Jan 12, 2015
1,658
I value your observations about IDE, but that is not the solution. I am after different approach to this problem. I guess I am missing something out at the architectural level of the MCU. There has to be a way to run both these timers simultaneously, but I can not find the reason any setup for the timer 8 jams my code while timer 1 is already initialized and running. I have tried disabling timer 1 clock and counter during timer 8 setup but it did not help.
Vilius, here's a statement from the code you posted:
C:
    RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
Without looking at the reference manual, what is the purpose of that statement? Why do you need seven operators? What effect do you expect it to have? how can you be 100% confident it produces exactly the outcome you desire? what about fifty such statements? What if the 6 should be a 5 or the 14 a 13?

I rest my case.
 
Last edited:
Top