STM32F4 bringt den Timer zum Arbeiten >2 MHz

Ich habe ein Nucleo-401RE (STM32F401) Board. Ich versuche, einen Timer (TIM4) so ​​einzurichten, dass er über 2 MHz (bei Bare-Metal-Code) funktioniert.

Gemäß den folgenden Timer-Einstellungen soll ich 10,5 MHz erhalten. Am Oszilloskop erhalte ich nur 2 MHz.

Habe ich die richtige Konfiguration vorgenommen? Gibt es einen Weg, um es schneller zu machen (ich habe die gleiche Hz, wenn ich das Timer-Perload-Register auf 3 setze)

#include "stm32f4xx.h"
#include "dbg_pin.h"

#define DBG_PIN_HIGH(p,x)   (p->BSRR = (uint16_t)x)
#define DBG_PIN_LOW(p,x)    (p->BSRR = (uint32_t) x << 16)
#define DBG_PIN_TOGL(p,x)   ((p->ODR & x)? DBG_PIN_LOW(p,x) : DBG_PIN_HIGH(p,x))
        
TIM_HandleTypeDef   hTimSlice;

void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_ClkInitTypeDef RCC_ClkInitStruct;

    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 336;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        return ;
    }

    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
        return ;
    }
}

uint8_t timer_init()
{
    __HAL_RCC_TIM4_CLK_ENABLE();

    hTimSlice.Instance              = TIM4;
    hTimSlice.Init.Prescaler        = 0;
    hTimSlice.Init.Period           = 7;
    hTimSlice.Init.ClockDivision    = 0;
    hTimSlice.Init.CounterMode      = TIM_COUNTERMODE_UP;
    hTimSlice.Init.AutoReloadPreload= TIM_AUTORELOAD_PRELOAD_ENABLE;

    if (HAL_TIM_Base_Init(&hTimSlice) != HAL_OK){
        return 1;
    }
    HAL_NVIC_SetPriority(TIM4_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(TIM4_IRQn);
    return 0;
}

void TIM4_IRQHandler()
{
    if(__HAL_TIM_GET_FLAG(&hTimSlice, TIM_FLAG_UPDATE) != RESET) {
        if(__HAL_TIM_GET_IT_SOURCE(&hTimSlice, TIM_IT_UPDATE) !=RESET) {
            __HAL_TIM_CLEAR_IT(&hTimSlice, TIM_IT_UPDATE);
        }
    }
    DBG_PIN_TOGL(GPIOC, GPIO_PIN_8);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();

    setupDbgPin();
    timer_init();
    HAL_TIM_Base_Start_IT(&hTimSlice);

    for(;;);
}

Danke.

Was ist bare-metal code?
Entschuldigung, ich meinte kein RTOS und keinen anderen Code. (es gibt HAL)
Setzen Sie GPIOC, Pin 8 in der setupDbgPin-Funktion auf Highspeed?
Ja. Ich habe GPIOC-8 auf hohe Geschwindigkeit eingestellt.

Antworten (1)

Die ISR-Montageliste (unten). Gezählte Befehlsausführungszeit basierend auf Arm-M4-Befehlssatz und ungefähr (37) Zyklen gefunden. Beachten Sie, dass es sich nur um das Umschalten eines Pins handelt.

Bei 84 MHz sind (37) Befehle * 12 ns = 444 ns.

Und es ist wahr, Sie können keine Timer-Rate von >2 MHz (oder schlechter) erreichen. Ich denke, die Idee, einen Timer mit höherer Rate zu verwenden, besteht darin, ihn mit anderen Diensten wie DMA zu verwenden.

70          if(__HAL_TIM_GET_FLAG(&hTimSlice, TIM_FLAG_UPDATE) != RESET) {
      TIM4_IRQHandler:
08000f80:   ldr     r3, [pc, #40]   ; (0x8000fac <TIM4_IRQHandler+44>)
08000f82:   ldr     r3, [r3, #0]
08000f84:   ldr     r2, [r3, #16]
08000f86:   lsls    r0, r2, #31
08000f88:   bpl.n   0x8000f96 <TIM4_IRQHandler+22>
71              if(__HAL_TIM_GET_IT_SOURCE(&hTimSlice, TIM_IT_UPDATE) !=RESET) {
08000f8a:   ldr     r2, [r3, #12]
08000f8c:   lsls    r1, r2, #31
72                  __HAL_TIM_CLEAR_IT(&hTimSlice, TIM_IT_UPDATE);
08000f8e:   itt     mi
08000f90:   mvnmi.w r2, #1
08000f94:   strmi   r2, [r3, #16]
75          DBG_TOGL(GPIO_PIN_8);
08000f96:   ldr     r3, [pc, #24]   ; (0x8000fb0 <TIM4_IRQHandler+48>)
08000f98:   ldr     r2, [r3, #20]
08000f9a:   lsls    r2, r2, #23
08000f9c:   ite     mi
08000f9e:   movmi.w r2, #16777216   ; 0x1000000
08000fa2:   movpl.w r2, #256        ; 0x100
08000fa6:   str     r2, [r3, #24]
76        }
08000fa8:   bx      lr
08000faa:   nop     
08000fac:   lsls    r4, r3, #17
08000fae:   movs    r0, #0
08000fb0:   lsrs    r0, r0, #32
08000fb2:   ands    r2, r0
Nur weil du es ineffizient machst. Wenn Sie wirklich eine schnelle Ausgabe wünschen, würden Sie sie über die Timer-Hardware und nicht über eine ISR steuern. Und es gibt effizientere Möglichkeiten, die ISR zu schreiben, um die minimal notwendigen Dinge zu tun.
Ja, ich stimme zu. Ich gebe nur ein Beispiel dafür, "etwas" zu tun. Jetzt habe ich gelernt, dass ich nicht viele Dinge in Timer-ISR > 2 MHz tun kann. UND es ist nicht nur ein Einstellungsproblem.