STM32 Timer muss skaliert werden für richtige Messung

#5646822
Lesenswert?

Hi,

Ich brauche einen Timer für die Geschwindigkeitsregelung eines Motors 
auf einem STM32F4 und das ganze klappt auch wunderbar über die ganze 
Bandbreite des Motors (60rpm bis 5000rpm) auf +- 0.1 rpm genau, aber nur 
wenn ich den Timer mit dem Faktor 1.07 multipliziere. Es sind im übrigen 
alle Timer betroffen.

Da der Faktor ziemlich neutral ist (nicht so 1.7348922, sondern nur 
1.07) dachte ich an einen falschen prescaler oder so, aber dem ist nicht 
so:

-uC läuft mit 100Mhz mit einem externen 16Mhz Quarz.
-Timer:
1
/* TIM9 init function */
2
static void MX_TIM9_Init(void)
3
{
4

5
  TIM_OC_InitTypeDef sConfigOC;
6

7
  htim9.Instance = TIM9;
8
  htim9.Init.Prescaler = 9;
9
  htim9.Init.CounterMode = TIM_COUNTERMODE_UP;
10
  htim9.Init.Period = 65535;
11
  htim9.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
12
  if (HAL_TIM_OC_Init(&htim9) != HAL_OK)
13
  {
14
    _Error_Handler(__FILE__, __LINE__);
15
  }
16

17
  sConfigOC.OCMode = TIM_OCMODE_TIMING;
18
  sConfigOC.Pulse = 0;
19
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
20
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
21
  if (HAL_TIM_OC_ConfigChannel(&htim9, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
22
  {
23
    _Error_Handler(__FILE__, __LINE__);
24
  }
25

26
}

-Messung Cyclic Zeit für Geschwindigkeitsmessung:
1
float t;
2
stopTimeMeasureing(&t,HTIM9);
3
startTimeMeasureing(HTIM9);
4

5
void startTimeMeasureing(TIMER_INTERFACES interface) {
6
  switch(interface) {
7
    case HTIM1:
8
    {
9
      break;
10
    }
11
    case HTIM5:
12
    {
13
      break;
14
    }
15
    case HTIM6:
16
    {
17
      tim6->Instance->CNT=0; 
18
      break;
19
    }
20
    case HTIM9:
21
    {
22
      tim9->Instance->CNT=0; 
23
      break;
24
    }
25
    case HTIM11:
26
    {
27
      break;
28
    }
29

30
    default: break;
31
  }
32
}
33

34
void stopTimeMeasureing(float *t,TIMER_INTERFACES interface) {
35
  switch(interface) {
36
    case HTIM1:
37
    {
38
      break;
39
    }
40
    case HTIM5:
41
    {
42
      break;
43
    }
44
    case HTIM6:
45
    {
46
      *t=tim6->Instance->CNT;
47
      *t=*t*100; //us
48
      break;
49
    }
50
    case HTIM9:
51
    {
52
      *t=tim9->Instance->CNT;
53
      current_cycling_time=*t/1e7;
54

55
      break;
56
    }
57
    case HTIM11:
58
    {
59
      break;
60
    }
61

62
    default: break;
63
  }
64
}

Ich kann mir das wirklich nicht erklären und in einer älteren 
Programmversion ging es auch noch ohne Faktor. Jemand eine Idee? Der 
Quarz kann nicht falsch sein, da UART und CO noch Fehlerfrei laufen.
Gast #5646842
Lesenswert?

Lass doch mal die ganze Motorregelung weg und lass dir in der ISR des 
Timers einen Pin togglen und messe mit dem Oszilloskop, ob die Frequenz 
stimmt.

Bert S. schrieb:
> -Messung Cyclic Zeit für Geschwindigkeitsmessung:

Du fragst den Timer-Stand also manuell ab? Dann kann das Ergebnis dank 
variierender Programmlaufzeit doch beliebig abweichen.
#5646851
Lesenswert?

Dr. Sommer schrieb:
> Du fragst den Timer-Stand also manuell ab? Dann kann das Ergebnis dank
> variierender Programmlaufzeit doch beliebig abweichen.

Ja, ich habe das mal auch noch so probiert:
1
    float t;
2
    startTimeMeasureing(HTIM1);
3
    HAL_Delay(5);
4
    stopTimeMeasureing(&t,HTIM1);
5
    sprintf(data_buf,"t:%d\n",(int)t);
6
    usart_tx_dma(256,data_buf,USB_UART);

Das gibt mir 59832 aus, statt den 50000 die ich bräuchte, auch wenn 
alles andere deaktiviert ist (keine Interrupts). Wie würdest du denn die 
Zeit auslesen, mit dem DMA oder gleich Interrupt?

Dr. Sommer schrieb:
> Lass doch mal die ganze Motorregelung weg und lass dir in der ISR des
> Timers einen Pin togglen und messe mit dem Oszilloskop, ob die Frequenz
> stimmt.

Jo, das mache ich mal.
Gast #5646853
Lesenswert?

Bert S. schrieb:
> Wie würdest du denn die
> Zeit auslesen, mit dem DMA?

Ich würde den Input Capture / PWM Input Mode des Timers nehmen, damit 
dieser exakt in Hardware die Zeit misst und man lediglich das Ergebnis 
aus dem Register lesen muss. Dazu muss das Encoder-Signal auf einen Pin 
des Timers gelegt werden.
#5646893
Lesenswert?

Dr. Sommer schrieb:
> Ich würde den Input Capture / PWM Input Mode des Timers nehmen, damit
> dieser exakt in Hardware die Zeit misst und man lediglich das Ergebnis
> aus dem Register lesen muss. Dazu muss das Encoder-Signal auf einen Pin
> des Timers gelegt werden.

Ok, das macht natürlich schon mehr Sinn, IC wird sowiso verwendet für 
den Encoder, die Zeitmessung war aber für den Luenberg Observer zur 
Geschwindigkeitsschätzung. Aber trotzdem kann ich mir nicht erklären, 
warum die Zeitmessung so extrem daneben liegt, Interrupts treten nicht 
wirklich viele auf. Ich denke der Fehler muss irgendwo anders liegen, 
zumindest scheint der Timer mit dem Oszi genau.
Gast #5646913
Lesenswert?

Benutzt du denn die FPU, oder werden deine Floating-Point-Operationen in 
Software berechnet (langsam)? Braucht es unbedingt float?
Wenn du CNT auf 0 setzt, wird der Prescaler-Zähler nicht auf 0 gesetzt. 
Schreibe stattdessen in das EGR, dann wird beides automatisch auf 0 
gesetzt. Timer im laufenden Betrieb zu setzen und abzufragen ist ungenau 
und macht nur Ärger...
#5647185
Lesenswert?

Dr. Sommer schrieb:
> Benutzt du denn die FPU, oder werden deine Floating-Point-Operationen in
> Software berechnet (langsam)?

FPU müsste aktiviert sein:

1
void SystemInit(void)
2
{
3
  /* FPU settings ------------------------------------------------------------*/
4
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
5
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
6
  #endif
7
  /* Reset the RCC clock configuration to the default reset state ------------*/
8
  /* Set HSION bit */
9
  RCC->CR |= (uint32_t)0x00000001;
10

11
  /* Reset CFGR register */
12
  RCC->CFGR = 0x00000000;
13

14
  /* Reset HSEON, CSSON and PLLON bits */
15
  RCC->CR &= (uint32_t)0xFEF6FFFF;
16

17
  /* Reset PLLCFGR register */
18
  RCC->PLLCFGR = 0x24003010;
19

20
  /* Reset HSEBYP bit */
21
  RCC->CR &= (uint32_t)0xFFFBFFFF;
22

23
  /* Disable all interrupts */
24
  RCC->CIR = 0x00000000;
25

26
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
27
  SystemInit_ExtMemCtl(); 
28
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
29

30
  /* Configure the Vector Table location add offset address ------------------*/
31
#ifdef VECT_TAB_SRAM
32
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
33
#else
34
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
35
#endif
36
}

Dr. Sommer schrieb:
> Wenn du CNT auf 0 setzt, wird der Prescaler-Zähler nicht auf 0 gesetzt.
> Schreibe stattdessen in das EGR, dann wird beides automatisch auf 0
> gesetzt. Timer im laufenden Betrieb zu setzen und abzufragen ist ungenau
> und macht nur Ärger...

Ok danke, ich probiere das mal.
#5647217
Lesenswert?

Dr. Sommer schrieb:
> Erzeugt der Compiler denn auch Instruktionen für die FPU? Die FPU bringt
> nichts wenn eine Soft-Float-Library aufgerufen wird...

Ich verwende Atollic Truestudio, soweit ich das gelesen habe, 
unterstützt das der Compiler.

Die Compiler Optionen sind folgende:
1
-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -std=gnu11 -D__weak=__attribute__((weak)) -D__packed=__attribute__((__packed__)) -DUSE_HAL_DRIVER -DSTM32F410Cx -I../Inc -I../Drivers/STM32F4xx_HAL_Driver/Inc -I../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy -I../Drivers/CMSIS/Device/ST/STM32F4xx/Include -I../Drivers/CMSIS/Include -I../Firmware/1_app/inc -I../Firmware/2_hlvl/inc -I../Firmware/3_phal/inc -I../Firmware/4_llvl/inc -I../Firmware/5_global/inc -Os -ffunction-sections -fdata-sections -g -fstack-usage -Wall -specs=nano.specs

-mfloat-abi=hard müsste den FPU unterstützen.
#5647270
Lesenswert?

Bert S. schrieb:
> Ja, ich habe das mal auch noch so probiert:    float t;
>     startTimeMeasureing(HTIM1);
>     HAL_Delay(5);
>     stopTimeMeasureing(&t,HTIM1);
>     sprintf(data_buf,"t:%d\n",(int)t);
>     usart_tx_dma(256,data_buf,USB_UART);
>
> Das gibt mir 59832 aus, statt den 50000 die ich bräuchte, auch wenn
> alles andere deaktiviert ist (keine Interrupts).

Achtung: HAL_Delay() baut auf dem SysTick-IRQ auf, der nur 1x ms 
auslöst. Je nachdem wann du startest kann dein Delay also um 1ms 
abweichen. Die 59832 wären damit im normalen Rahmen.
#5647294
Lesenswert?

Hermann K. schrieb:
> Achtung: HAL_Delay() baut auf dem SysTick-IRQ auf, der nur 1x ms
> auslöst. Je nachdem wann du startest kann dein Delay also um 1ms
> abweichen. Die 59832 wären damit im normalen Rahmen.

Stimmt, da hast du recht, der ist immer um 1ms verschoben, daher passt 
der Timer also und auch das Auslesen. Der Fehler muss also woanders 
liegen.

Dr. Sommer schrieb:
> Ja, das sieht gut aus. Schau mal im disassemblierten Code, ob da Dinge
> wie "vmul" oder "vadd" auftauchen, das sind FPU-Anweisungen.

Ja, das taucht auf im .list file, dann passt das schon.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren