Forum: Mikrocontroller und Digitale Elektronik STM32 Timer muss skaliert werden für richtige Messung


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht 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.

von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
1 lesenswert
nicht 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.

von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht 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...

von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bert S. schrieb:
> FPU müsste aktiviert sein:

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

von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bert S. schrieb:
> -mfloat-abi=hard -mfpu=fpv4-sp-d16

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

von Hermann K. (r2d2)


Bewertung
0 lesenswert
nicht 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.

von Bert S. (kautschuck)


Bewertung
0 lesenswert
nicht 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.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Toggle im Moment der Messung ("CNT"-Zugriff) mal einen Pin und 
oszilloskopiere den gleichzeitig  zum Eingangs-Pin.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.