Forum: Mikrocontroller und Digitale Elektronik STM32F103C8T6 - TIM2 PWM


von Christoph K. (chriskuku)


Angehängte Dateien:

Lesenswert?

Habe eine kleine (STM32CubeIDE) Anwendung auf einem STM32F103C8T6 und 
möchte auf pin PA15 ein PWM-Signal ausgeben. Es kommt aber leider nichts 
raus. Habe das Problem auf ein kleineres Projekt heruntergebrochen, was 
auch nicht funktioniert. Wo liegt der Fehler?

von Werner A. (gnuopfer)


Lesenswert?

Zuerst mal die Standardfrage für alle Probleme mit einem STM:
 Sind alle Clocks aktiviert?...
 Ja?...
 Wirklich alle?...
 Ganz sicher ?...

von Christoph K. (chriskuku)


Lesenswert?

Werner A. schrieb:
> Zuerst mal die Standardfrage für alle Probleme mit einem STM:
>  Sind alle Clocks aktiviert?...
>  Ja?...
>  Wirklich alle?...
>  Ganz sicher ?...

Wenn ich mir im .ioc File die Clockkonfiguration ansehe, so haben die 
Timer 72MHz Clock. Im Tim2 Setup ist Internal Clock enabled.
Des weiteren verlasse ich mich auf die generierte MX_TIM2_Init().
1
static void MX_TIM2_Init(void)
2
{
3
4
  /* USER CODE BEGIN TIM2_Init 0 */
5
6
  /* USER CODE END TIM2_Init 0 */
7
8
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
9
  TIM_MasterConfigTypeDef sMasterConfig = {0};
10
  TIM_OC_InitTypeDef sConfigOC = {0};
11
12
  /* USER CODE BEGIN TIM2_Init 1 */
13
14
  /* USER CODE END TIM2_Init 1 */
15
  htim2.Instance = TIM2;
16
  htim2.Init.Prescaler = 0;
17
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
18
  htim2.Init.Period = 1023;
19
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
20
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
21
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
22
  {
23
    Error_Handler();
24
  }
25
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
26
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
27
  {
28
    Error_Handler();
29
  }
30
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
31
  {
32
    Error_Handler();
33
  }
34
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
35
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
36
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
37
  {
38
    Error_Handler();
39
  }
40
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
41
  sConfigOC.Pulse = 0;
42
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
43
  sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
44
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
45
  {
46
    Error_Handler();
47
  }
48
  /* USER CODE BEGIN TIM2_Init 2 */
49
50
  /* USER CODE END TIM2_Init 2 */
51
  HAL_TIM_MspPostInit(&htim2);
52
53
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

PA15 muss "remapped" werden, weil dieser normalerweise für JTAG 
reserviert ist.

Siehe auch:

https://community.st.com/t5/stm32-mcu-products/what-am-i-supposed-to-do-to-use-pa14-pa15-in-stm32f103xx/td-p/400555

https://www.stm32duino.com/viewtopic.php?t=713

P.S.

Ich mach das immer so:
1
#if defined (STM32F103)                                       // disable JTAG to get back PB3, PB4, PA13, PA14, PA15
2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);      // turn on clock for the alternate function register
3
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);  // disable the JTAG, enable the SWJ interface
4
#endif

: Bearbeitet durch Moderator
von Harry L. (mysth)


Lesenswert?

Ich kann da nichts auffälliges entdecken, allerdings ist der Takt, den 
du erzeugst ca 70kHz, aber funktionieren sollte das.

Du brauchst also schon nen Oszi oder LA um das sinnvoll auswerten zu 
können.

von Christoph K. (chriskuku)


Lesenswert?

Frank M. schrieb:
> PA15 muss "remapped" werden, weil dieser normalerweise für JTAG
> reserviert ist.
>
Ach ja, jetzt fällt's mir wieder ein. Bin ich schon mal drauf 
reingefallen. Danke.

von Harry L. (mysth)


Lesenswert?

Christoph K. schrieb:
> Frank M. schrieb:
>> PA15 muss "remapped" werden, weil dieser normalerweise für JTAG
>> reserviert ist.
>>
> Ach ja, jetzt fällt's mir wieder ein. Bin ich schon mal drauf
> reingefallen. Danke.

Musst du nicht!
Das hat CubeMX bereits für dich erledigt.

von Christoph K. (chriskuku)


Lesenswert?

Harry L. schrieb:
> Ich kann da nichts auffälliges entdecken, allerdings ist der Takt, den
> du erzeugst ca 70kHz, aber funktionieren sollte das.
>
> Du brauchst also schon nen Oszi oder LA um das sinnvoll auswerten zu
> können.

Das mit den 70KHz ist schon OK. Habe Pulseview.
Oder sagen wir so, mit den 70 KHz ist so eine Geschichte :) Aber da 
müßte ich weiter ausholen. Vielleicht später (aber das fällt vielleicht 
unter die Rubrik: mein größter Bock).

von Harry L. (mysth)


Angehängte Dateien:

Lesenswert?

Und du solltest auch das Debugging einschalten! (siehe Screenshot!)

von Christoph K. (chriskuku)


Lesenswert?

Harry L. schrieb:
> Und du solltest auch das Debugging einschalten! (siehe Screenshot!)

Debugging funktioniert ja im CubeIDE momentan.

Harry L.: es lag daran. Es stand "No Debug" drin. Ich habe "Serial Wire" 
eingetragen. Erster Gedanke war, daß htim2.Instance->CCR1 dem von mir 
verwendeten TIM2->CCR1 vorzuziehen sei. Mag bei HAL sicher auch 
intuitiver sein. Aber Ursache war letztlich die Debug Einstellung, die 
offenbar dann das Remapping besorgt.

In dem größeren Projekt, in dem TIM2_CH1 output nicht funktionierte, 
konnte ich Serial Wire nicht einstellen, da ich einen Konflikt mit PA13 
hatte, der als GPIO programmiert war. Wo sind PA13 und PA14 übrigens an 
der blue pill?
Hab den Schaltplan gerade nicht zur Hand, aber vermute, die werden für 
die SWD Schnittstelle gebraucht. Denn gerade geht's Debuggen nicht mehr.

Muß jetzt wohl doch wieder zu PA15 zurück. Das mit dem Remappen habe ich 
noch nicht hinbekommen. Ich will ja nicht alles remappen wie in der von 
@Frank M.(ukw) oben beschriebenen Methode. Die einzelnen Calls 
funktionierten nicht. Da fehlten Symbole/Macros.

: Bearbeitet durch User
Beitrag #7465050 wurde vom Autor gelöscht.
von Steve van de Grens (roehrmond)


Lesenswert?

Christoph K. schrieb:
> Wo sind PA13 und PA14 übrigens an der blue pill?

An der vierpoligen Stiftleiste
http://stefanfrings.de/stm32/stm32f1.html#bluepill

> vermute, die werden für die SWD Schnittstelle gebraucht

Ja

: Bearbeitet durch User
von Christoph K. (chriskuku)


Lesenswert?

So. Ruhe ist. Habe jetzt im Hauptprojekt die Timer noch mal umrangiert. 
Timer 1 jetzt für die PWM genommen. Weniger Konflikte.
Um aber noch die Hintergrundgeschichte zu erzählen, falls es jemand 
interessiert: meine Aufgabenstellung für dieses kleine Bastelprojekt 
ist, ein 13 töniges Baßpedal (diesmal keine 25 Töne) mit einer 
Klangerzeugung zu versorgen. Habe mir eine Wavetable mit 256 Stützwerten 
berechnet, die ins Programm geladen. Die Frequenzen gehen von 32 bis 65 
Hz. 256 Stützwerte ergibt eine Samplefrequenz von ca. 8,3 - 17,7 KHz.
Die gibst Du dann auf den DAC und fertig - dachte ich. Vielleicht noch 
einen Tiefpass als Opamp hinter den Ausgang und fertig.
DAC? Wo war der denn? Mußte lernen, daß der STM32F103C8T6 gar keinen DAC 
besitzt. Gut, das war der "Bock". Also was machen, um den Bastelaufbau 
zu retten? PWM mußte her.

Problem ist aber, daß ich jetzt nicht mehr von 12Bit Auflösung ausgehen 
kann, sondern nur so etwa 10 bit. Daher die 70KHz PWM.

Vielleicht hat jemand eine bessere Idee, aber im Moment schreibe ich (im 
Falle der höchsten Frequenz) alle 59 µs einen Wert ins Timer_CCR 
Register.

Vielleicht doch noch einen kleinen seriellen DAC dranklemmen?

: Bearbeitet durch User
von Christoph K. (chriskuku)


Lesenswert?

Brauche noch mal Hilfe zu den Timerinterrupts.
Habe jetzt die Lösung reduziert auf nur 2 Timer. Es war Quatsch, einen 
extra PWM-Timer zu nehmen. TIM3 macht jetzt den Tastatur Scan und TIM1 
gibt die 256 Samples in Form von Pulsweiten aus. Ich dachte daran, die 
als one-shot mit jedem Interrupt aus einer Tabelle zu nehmen, die den 
Amplituden (Tastverhältnissen) entspricht.

Zuvor hatte ich einen global Interrupt, den sich TIM3 und TIM2 teilten.
TIM1 hat aber
1
break
2
update
3
trigger and commutation
4
capture compare


Interrupts. Ich vermute mal, daß "update" der Interrupt ist, der 
ausgelöst wird, wenn der Timer die Periode zu Ende gezählt hat.

Ich hatte bei dem Global Interrupt eine Routine namens
1
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)

Ich weiß nicht mehr, ob ich die da selbst hingeschrieben habe ins main() 
oder ob die da automatisch eingesetzt wurde. Aber das sind ja Routinen, 
die man überschreibt. Jetzt sehe ich in stm32f1xx_it.c :
1
void TIM1_UP_IRQHandler(void)
2
{
3
  /* USER CODE BEGIN TIM1_UP_IRQn 0 */
4
5
  /* USER CODE END TIM1_UP_IRQn 0 */
6
  HAL_TIM_IRQHandler(&htim1);
7
  /* USER CODE BEGIN TIM1_UP_IRQn 1 */
8
9
  /* USER CODE END TIM1_UP_IRQn 1 */
10
}

Meines Wissens soll man da hinein keinen User Code schreiben. Wie heißt 
die Routine, mit der ich den IR im main.c dann überschreibe (abfange)?

EDIT: ich glaube, es hat sich erledigt. Ich kann offenbar den 
"PeriodElapsedCallback" nehmen. Da landet der TIM1 IR auch drin. Wollte 
eigentlich meinen Beitrag löschen, laß ihn aber stehen, falls es noch 
etwas Hilfreiches dazu zu bemerken gilt.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.