Forum: Mikrocontroller und Digitale Elektronik Fragen zur Erstellung von 8xPWM mit einstellbarer Anzahl an Perioden beim STM3F103


von David (Gast)


Lesenswert?

Hallo,

ich suche nach Hilfe bei meinem kleinen Projekt, bei dem ich seit fast 
einem Jahr nach etlichen versuchen einfach nicht weiter komme.

Ich versuche PWMs mit vorgegebenen Anzahl an Perioden zur Erzeugen, um 8 
Stepper Motor an bestimmte Positionen zu fahren.
Wie viele Perioden der Timer für jeden Motor erzeugen soll kriegt der 
Controller über eine CAN Botschaft mitgeteilt. Die Botschaft enthält 8 
Positionen, die mit den aktuellen Positionen verglichen werden. Sollte 
die übersendete Position von der aktuellen Position abweichen, ergibt 
sich ein Verfahrweg, der umgerechnet werden kann an die jeweilige Anzahl 
an Perioden.

Folgende Timer werden dafür genutzt:
Tim2 mit CH1-CH4, Tim3 mit CH1-CH2 und Tim4 mit CH1-CH2.
Alle PWM sind mit einer festen Frequenz spezifiziert.
Als Mikrocontroller verwende ich einen STM32F103 Blue Pill.

Ich habe die Befürchtung, dass sich die Interrupts gegenseitig im Code 
Blockieren. Zumindest stimmen die Positionen nicht 100% nach etwa 24 
Stunden.
Da ich kein Experte auf dem Gebiet bin frage ich mich, ob ich das ganze 
überhaupt richtig umgesetzt habe.

Callback für die CAN Nachricht:
1
 void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
2
{
3
   flag=true; //flag zur Berechnung des Verfahrwegs in der while Schleife
4
   recieve_positions(); //Positionen werden in neue_position abgelegt
5
}

Ich Polle in meiner Hauptschleife
1
while(1)
2
{
3
    if(flag==true)
4
    {
5
      motion_calculations(); //// Berechnet den zu fahrenden Weg und Startet PWM Bewegung
6
    }
7
}

Auschnitt aus motion_calculation():
1
void motion_calculations()
2
{
3
  flag=false;
4
  for(uint8_t i=0; i<4; i++)
5
  {
6
    weg_m[1][i]=(neue_position_m[1][i]-aktuelle_position_m[1][i]); //weg_m enthält die zu erreichende Anzahl der Perioden, um die Position zu erreichen 
7
    weg_s[1][i]=(neue_position_s[1][i]-aktuelle_position_s[1][i]);
8
  }
9
  for(uint8_t i=0; i<4; i++)
10
  {
11
    if (weg_s[1][i] != 0)
12
    {
13
      switch(i)
14
      {
15
      case 0:
16
      if (weg_s[1][0] >= 0)
17
      {
18
        HAL_GPIO_WritePin(GPIOA, CWCCW1A_Pin, GPIO_PIN_SET); //Motor dreht rechts
19
      }
20
      else
21
      {
22
        HAL_GPIO_WritePin(GPIOA, CWCCW1A_Pin, GPIO_PIN_RESET); //Motor dreht links
23
        weg_s[1][0]=abs(weg_s[1][0]); //Bewegungsvorzeichen entfernen
24
      }
25
      HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1);
26
      counter_clock_1s=0;
27
      break
28
// Das ganze wird auch noch für alle andern Timer/Motoren gestartet weg_s[1][0] weg_s[1][1] weg_s[1][2] weg_s[1][3] und weg_m[1][0] weg_m[1][1] weg_m[1][2] weg_m[1][3]

Nach jeder Periode wird der HAL_TIM_PWM_PulseFinishedCallback 
aufgerufen, um die Anzahl der Periode zu inkrementieren, falls die Ziel 
Anzahl (weg_s, weg_m) erreicht ist, wird der PWM gestoppt:
1
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
2
{
3
  if(htim->Instance == TIM2)
4
  {
5
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
6
    {
7
      if(counter_clock_1s==weg_s[0][0])
8
9
      {
10
        HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1);
11
        aktuelle_position_s[0][0] = neue_position_s[0][0];
12
      }
13
      counter_clock_1s++;
14
    }
15
  }
16
17
// in diesem Interrupt sind auch alle anderen Interrupts enthalten
18
if(htim->Instance == TIM2)
19
  {
20
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
21
........

PWM Einstellung für Tim2 (anderen Timer sind gleich eingestellt):
1
/* TIM2 init function */
2
void MX_TIM2_Init(void)
3
{
4
  TIM_MasterConfigTypeDef sMasterConfig = {0};
5
  TIM_OC_InitTypeDef sConfigOC = {0};
6
7
  htim2.Instance = TIM2;
8
  htim2.Init.Prescaler = 17;
9
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
10
  htim2.Init.Period = 4095;
11
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
12
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
13
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
14
  {
15
    Error_Handler();
16
  }
17
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
18
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
19
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
20
  {
21
    Error_Handler();
22
  }
23
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
24
  sConfigOC.Pulse = 2095;
25
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
26
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
27
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
28
  {
29
    Error_Handler();
30
  }
31
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
32
  {
33
    Error_Handler();
34
  }
35
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
36
  {
37
    Error_Handler();
38
  }
39
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
40
  {
41
    Error_Handler();
42
  }
43
  HAL_TIM_MspPostInit(&htim2);
44
45
}

Ist es überhaupt richtig wie ich hier vorgehe? Meine Hoffnung ist, dass 
ich hier ein falschen weg gegangen bin und es einen anderen weg gibt. 
Über jede Hilfe wäre ich dankbar, weil ich einfach nicht mehr weiter 
weiß. Falls doch richtig sein sollte, wäre ich über das Feedback 
natürlich auch glücklich, dann muss es wohl an etwas anderem liegen.

Gruß David

von Klaus W. (mfgkw)


Lesenswert?

Ein Stepper-Motor ist für dich ein Schrittmotor?

Es fällt mir gerade schwer vorzustellen, was den die Pulsweite eines 
Signal interessiert.

von David (Gast)


Lesenswert?

Ich nutze ein VID 6606 Stepper Motor Driver 
https://guy.carpenter.id.au/gaugette/resources/vid/2009111391612_VID6606%20manual%20060927.pdf
https://guy.carpenter.id.au/gaugette/resources/switec/X12_017.pdf

"- The rising edge of the f(scx) input signal moves the
rotor by one microstep"

Hoffe es wird jetzt klarer

von Stefan F. (Gast)


Lesenswert?

Interrupts werden sich wohl kaum gegenseitig blockieren. Was viel 
schneller mal schief geht ist, dass Zugriff auf gemeinsam genutzte 
Variablen (in Interrupts und Hauptprogramm) nicht synchronisiert wurden 
und man dadurch zu unerwarteten Ergebnissen kommt.

Sagt dir in diesem Zusammenhang "volatile" etwas?

von Axel S. (a-za-z0-9)


Lesenswert?

David schrieb:
> Ich nutze ein VID 6606 Stepper Motor Driver

> "- The rising edge of the f(scx) input signal moves the
> rotor by one microstep"
>
> Hoffe es wird jetzt klarer

Nein. Es ist noch immer noch nicht klar, warum du ein PWM-Signal 
erzeugst (PWM steht für pulse with modulation) wenn du doch nur eine 
bestimmte Anzahl Impulse erzeugen mußt.

Ferner ist unklar, ob du auch Rampen erzeugen willst oder mußt. Also die 
Impulsfrequenz variabel gestalten, damit der Motor langsam anläuft und 
langsam wieder abbremst.

von David (Gast)


Lesenswert?

Was wäre hier das richtige vorgehen? Gibt es ein Beispiel aus dem ich 
entnehmen kann, wie ich es alternativ erzeuge?

von David (Gast)


Lesenswert?

Grund für die Verwendung vom PWM war folgender Link:
https://www.programmersought.com/article/72408260455/

von David (Gast)


Lesenswert?

Axel S. schrieb:
> Ferner ist unklar, ob du auch Rampen erzeugen willst oder mußt. Also die
> Impulsfrequenz variabel gestalten, damit der Motor langsam anläuft und
> langsam wieder abbremst.

nice to have, wird aber im ersten fall nicht gebraucht

von c-hater (Gast)


Lesenswert?

David schrieb:

> Ich habe die Befürchtung, dass sich die Interrupts gegenseitig im Code
> Blockieren. Zumindest stimmen die Positionen nicht 100% nach etwa 24
> Stunden.

Das kann viele Ursachen haben. Z.B. könnte auch die Pulsfrequenz die 
physikalischen Möglichkeiten des Motors übersteigen. Besonders kritisch 
sind diesbezüglich die Beschleunigungsphasen, deshalb benutzt man hier 
oft Rampen. Das ist aber nicht zwingend. Wenn es nicht so darauf 
ankommt, wie schnell der Motor die Zielposition erreicht, kann man auch 
auf Rampen verzichten.

Sprich: verringere einfach mal die Pulsfrequenz, also: erhöhe entweder 
den PWM-Zyklus oder verringere den Timertakt. Wird das Problem dadurch 
geheilt, lag's wohl daran.

Ansonsten wird's ein Softwareproblem sein. Dem Tip von Stefan bezüglich 
fehlender "volatile"-Qualifizierung nachzugehen, könnte auf jeden Fall 
nicht schaden.

von David (Gast)



Lesenswert?

Stefan ⛄ F. schrieb:
> Interrupts werden sich wohl kaum gegenseitig blockieren. Was viel
> schneller mal schief geht ist, dass Zugriff auf gemeinsam genutzte
> Variablen (in Interrupts und Hauptprogramm) nicht synchronisiert wurden
> und man dadurch zu unerwarteten Ergebnissen kommt.
>
> Sagt dir in diesem Zusammenhang "volatile" etwas?


c-hater schrieb:
> Ansonsten wird's ein Softwareproblem sein. Dem Tip von Stefan bezüglich
> fehlender "volatile"-Qualifizierung nachzugehen, könnte auf jeden Fall
> nicht schaden.

Danke für eure Tipps ich habe meinen Code mal abgeändert, sodass alle 
Variablen, die "von außen" verändert werden können nun volatile sind:
1
volatile bool flag = false;
2
3
//wird als Array deklariert weil ich 6 Platinen a 8 Motoren habe
4
5
volatile int weg_m[3][8];
6
volatile int weg_s[3][8];
7
8
volatile int aktuelle_position_m[3][8];  
9
volatile int aktuelle_position_s[3][8];
10
11
volatile int neue_position_m[3][8];
12
volatile int neue_position_s[3][8];
13
14
volatile int counter_clock_1s=0;
15
volatile int counter_clock_1m=0;
16
volatile int counter_clock_2s=0;
17
volatile int counter_clock_2m=0;
18
volatile int counter_clock_3s=0;
19
volatile int counter_clock_3m=0;
20
volatile int counter_clock_4s=0;
21
volatile int counter_clock_4m=0;

Leider besteht das Problem immer noch. Ich werde morgen mal die Frequenz 
runterschrauben.

Ich hab mir mal den Debugger Serial Wire Viewer (Live Expression und das 
SWV Data Trace) angeschaut. Ich weiß nicht wie sehr ich den Daten 
vertrauen kann vor allem, weil mir hier unterschiedliche Dinge angezeigt 
werden. Ich habe die Variable counter_clock_1s mal in den zwei Ansichten 
angeschaut. In der Live Expression entspricht counter_clock_1s auch 
weg_s[1][0] wie es sein muss (counter_clock_1s ist Programm bedingt um 
eins höher aber der Interrupt wird schon vorher beendet) im SWV Data 
Trace erreicht counter_clock_1s nur den Wert 935. Zusätzlich sind auch 
Sprünge in den Log Daten zu entnehmen. Die Screenshots habe ich mal 
angehangen. Kann ich dem Debugger vertrauen?

von Harry L. (mysth)


Lesenswert?

Das "Timer-Cookbook" kennst du?

Das Kapitel 5 solltest du dir mal intensiver anschauen!

5 Arbitrary waveform generation using timer DMAburst feature

https://www.st.com/resource/en/application_note/dm00236305-generalpurpose-timer-cookbook-for-stm32-microcontrollers-stmicroelectronics.pdf

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.