Forum: Mikrocontroller und Digitale Elektronik STM32F1xxx Problem mit einer 50Hz PWM


von M. G. (ixil96)


Angehängte Dateien:

Lesenswert?

Hallo,

ich möchte eine PWM für die Ansteuerung eines Servos erzeugen.
Soweit funktioniert das auch, nur bekomme ich immer wieder low-Pulse 
(siehe Oszi Bild) in mein Signal rein und dabei fängt das Servo an 
ordentlich zu ruckeln.

FCPU (STM32F103C8T) = 32 MHz
PSC = 31
Timer Frequenz = FCPU / (PSC+1) = 1MHz
Counter Period = 20000
PWM Frequency = 50Hz

Es scheint, als würde hier eine Art "Timer Überlauf" dazwischen funken.
Vielleicht kann wer helfen?

Hier der Programmcode:
1
/* USER CODE BEGIN Header */
2
/**
3
  ******************************************************************************
4
  * @file           : main.c
5
  * @brief          : Main program body
6
  ******************************************************************************
7
  * @attention
8
  *
9
  * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
10
  * All rights reserved.</center></h2>
11
  *
12
  * This software component is licensed by ST under BSD 3-Clause license,
13
  * the "License"; You may not use this file except in compliance with the
14
  * License. You may obtain a copy of the License at:
15
  *                        opensource.org/licenses/BSD-3-Clause
16
  *
17
  ******************************************************************************
18
  */
19
/* USER CODE END Header */
20
/* Includes ------------------------------------------------------------------*/
21
#include "main.h"
22
23
/* Private includes ----------------------------------------------------------*/
24
/* USER CODE BEGIN Includes */
25
26
/* USER CODE END Includes */
27
28
/* Private typedef -----------------------------------------------------------*/
29
/* USER CODE BEGIN PTD */
30
31
/* USER CODE END PTD */
32
33
/* Private define ------------------------------------------------------------*/
34
/* USER CODE BEGIN PD */
35
/* USER CODE END PD */
36
37
/* Private macro -------------------------------------------------------------*/
38
/* USER CODE BEGIN PM */
39
40
/* USER CODE END PM */
41
42
/* Private variables ---------------------------------------------------------*/
43
TIM_HandleTypeDef htim4;
44
45
/* USER CODE BEGIN PV */
46
uint16_t highTime_ms = 1500;  // Positive pulse time in ms
47
uint8_t flag = 1;
48
/* USER CODE END PV */
49
50
/* Private function prototypes -----------------------------------------------*/
51
void SystemClock_Config(void);
52
static void MX_GPIO_Init(void);
53
static void MX_TIM4_Init(void);
54
/* USER CODE BEGIN PFP */
55
56
/* USER CODE END PFP */
57
58
/* Private user code ---------------------------------------------------------*/
59
/* USER CODE BEGIN 0 */
60
void set_pwm_duty_cycle(uint16_t highTime_ms)
61
{
62
  TIM_OC_InitTypeDef sConfigOC = {0};
63
64
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
65
  sConfigOC.Pulse = highTime_ms;
66
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
67
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
68
  HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
69
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
70
}
71
/* USER CODE END 0 */
72
73
/**
74
  * @brief  The application entry point.
75
  * @retval int
76
  */
77
int main(void)
78
{
79
  /* USER CODE BEGIN 1 */
80
81
  /* USER CODE END 1 */
82
83
  /* MCU Configuration--------------------------------------------------------*/
84
85
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
86
  HAL_Init();
87
88
  /* USER CODE BEGIN Init */
89
90
  /* USER CODE END Init */
91
92
  /* Configure the system clock */
93
  SystemClock_Config();
94
95
  /* USER CODE BEGIN SysInit */
96
97
  /* USER CODE END SysInit */
98
99
  /* Initialize all configured peripherals */
100
  MX_GPIO_Init();
101
  MX_TIM4_Init();
102
  /* USER CODE BEGIN 2 */
103
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);    // Start Timer
104
  /* USER CODE END 2 */
105
106
  /* Infinite loop */
107
  /* USER CODE BEGIN WHILE */
108
  while (1)
109
  {
110
    /* USER CODE END WHILE */
111
     if((flag == 1) && (highTime_ms < 1800))    // turn servo right until 1,8ms
112
     {
113
       highTime_ms ++;
114
       set_pwm_duty_cycle(highTime_ms);
115
       HAL_Delay(5);
116
       if(highTime_ms == 1800)
117
       {
118
         flag = 0;
119
         HAL_Delay(2000);
120
       }
121
     }
122
123
     else if((flag == 0) && (highTime_ms > 1200))  // turn servo left until 1,2ms
124
     {
125
       highTime_ms --;
126
       set_pwm_duty_cycle(highTime_ms);
127
       HAL_Delay(5);
128
       if(highTime_ms == 1200)
129
      {
130
        flag = 1;
131
        HAL_Delay(2000);
132
      }
133
     }
134
    /* USER CODE BEGIN 3 */
135
  }
136
  /* USER CODE END 3 */
137
}
138
139
/**
140
  * @brief System Clock Configuration
141
  * @retval None
142
  */
143
void SystemClock_Config(void)
144
{
145
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
146
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
147
148
  /** Initializes the RCC Oscillators according to the specified parameters
149
  * in the RCC_OscInitTypeDef structure.
150
  */
151
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
152
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
153
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
154
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
155
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
156
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;
157
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
158
  {
159
    Error_Handler();
160
  }
161
  /** Initializes the CPU, AHB and APB buses clocks
162
  */
163
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
164
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
165
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
166
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
167
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
168
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
169
170
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
171
  {
172
    Error_Handler();
173
  }
174
}
175
176
/**
177
  * @brief TIM4 Initialization Function
178
  * @param None
179
  * @retval None
180
  */
181
static void MX_TIM4_Init(void)
182
{
183
184
  /* USER CODE BEGIN TIM4_Init 0 */
185
186
  /* USER CODE END TIM4_Init 0 */
187
188
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
189
  TIM_MasterConfigTypeDef sMasterConfig = {0};
190
  TIM_OC_InitTypeDef sConfigOC = {0};
191
192
  /* USER CODE BEGIN TIM4_Init 1 */
193
194
  /* USER CODE END TIM4_Init 1 */
195
  htim4.Instance = TIM4;
196
  htim4.Init.Prescaler = 31;  // 32MHz (clock frequency) / (Prescaler +1) = 1MHz Timer frequency
197
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
198
  htim4.Init.Period = 20000;  // Resolution of PWM (counter period) max. 65535
199
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
200
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
201
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
202
  {
203
    Error_Handler();
204
  }
205
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
206
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
207
  {
208
    Error_Handler();
209
  }
210
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
211
  {
212
    Error_Handler();
213
  }
214
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
215
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
216
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
217
  {
218
    Error_Handler();
219
  }
220
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
221
  sConfigOC.Pulse = 1500;        // Servo Mittenstellung
222
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
223
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
224
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
225
  {
226
    Error_Handler();
227
  }
228
  /* USER CODE BEGIN TIM4_Init 2 */
229
230
  /* USER CODE END TIM4_Init 2 */
231
  HAL_TIM_MspPostInit(&htim4);
232
233
}
234
235
/**
236
  * @brief GPIO Initialization Function
237
  * @param None
238
  * @retval None
239
  */
240
static void MX_GPIO_Init(void)
241
{
242
243
  /* GPIO Ports Clock Enable */
244
  __HAL_RCC_GPIOA_CLK_ENABLE();
245
  __HAL_RCC_GPIOB_CLK_ENABLE();
246
247
}
248
249
/* USER CODE BEGIN 4 */
250
251
/* USER CODE END 4 */
252
253
/**
254
  * @brief  This function is executed in case of error occurrence.
255
  * @retval None
256
  */
257
void Error_Handler(void)
258
{
259
  /* USER CODE BEGIN Error_Handler_Debug */
260
  /* User can add his own implementation to report the HAL error return state */
261
  __disable_irq();
262
  while (1)
263
  {
264
  }
265
  /* USER CODE END Error_Handler_Debug */
266
}
267
268
#ifdef  USE_FULL_ASSERT
269
/**
270
  * @brief  Reports the name of the source file and the source line number
271
  *         where the assert_param error has occurred.
272
  * @param  file: pointer to the source file name
273
  * @param  line: assert_param error line source number
274
  * @retval None
275
  */
276
void assert_failed(uint8_t *file, uint32_t line)
277
{
278
  /* USER CODE BEGIN 6 */
279
  /* User can add his own implementation to report the file name and line number,
280
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
281
  /* USER CODE END 6 */
282
}
283
#endif /* USE_FULL_ASSERT */
284
285
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

von erklehr behr (Gast)


Angehängte Dateien:

Lesenswert?

Fuck!

Was ist an den Hinweisen zum Posten nicht zu verstehen?

von M. G. (ixil96)


Lesenswert?

erklehr behr schrieb:
> Fuck!
>
> Was ist an den Hinweisen zum Posten nicht zu verstehen?

Sehr nette Ausdrucksweise!
Das ist wirklich sehr hilfreich. Wo ist die Definition von "langer 
Sourcecode"?

von erklehr behr (Gast)


Lesenswert?

M. G. schrieb:
> Wo ist die Definition von "langer
> Sourcecode"?

Du könntest dich in diesem Zusammenhang auch einfach selbst
in Bescheidenheit üben. Wenn man zig-mal scrollen muss um
das Ende zu finden dann ist das langer Sourcecode.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

M. G. schrieb:
> if(highTime_ms == 1800)
>        {
>          flag = 0;
>          HAL_Delay(2000);
>        }
>      }

Wann soll das je ausgeführt werden? Bedenke, die Bedingung des äusseren 
if ist :
> if((flag == 1) && (highTime_ms < 1800))    // turn servo right until bla

Das musst du schon ausserhalb des if() schreiben.
Ansonsten wäre es schön, entweder die Leerzeilen zu entfernen, oder als 
Datei zu posten.

von Stefan F. (Gast)


Lesenswert?

Ich würde das Problem einkreisen.

Ersetze deine Hauptschleife durch eine leere und schaue nach, ob das 
Ausgangssignal dann stabil ist.

von erklehr behr (Gast)


Lesenswert?

M. G. schrieb:
> Das ist wirklich sehr hilfreich.

Als sehr lang hier angemeldeter Benutzer solltest du um die
Gepflogenheiten und Nettiquette hier im Forum Bescheid wissen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

M. G. schrieb:
> Wo ist die Definition von "langer Sourcecode"?
Da hat jeder so seine Vorstellungen. Aber wenn man Scrollen muss und 
dann noch eine Antwort auf eine Frage beantworten will, wirds 
unübersichtlich.

Also kurze Programmschnipsel direkt in den Text und alles, was länger 
als ca. 30 Zeilen ist in den Anhang.

Die Idee von Andreas war vermutlich, dass man da mitdenkt und selber 
drauf kommt.

Ansonsten wie üblich bei der Fehlersuche: das Programm so lange 
reduzieren, bis der Fehler verschwindet.

Wenn er nicht verschwindet, dann den verbleibenden "Dreizeiler" 
posten...

Es ist übrigens eine ganz schlechte Idee, einen Timer einfach zu 
beliebigen Zeitpunkten anzuhalten, an ihm herumzuschrauben und wieder 
neu zu starten, so wie du es offenbar in der set_pwm_duty_cycle() 
machst.

von Kevin M. (arduinolover)


Lesenswert?

M. G. schrieb:
> void set_pwm_duty_cycle(uint16_t highTime_ms)

Warum machst du das Ändern des Duty Cyles denn so kompliziert?
Es gibt keinen Grund das so zu tun, schreib einfach den gewünschten Duty 
Cycle direkt in das Register.

z.B. TIM4->CCR1 = X;

von M. G. (ixil96)


Lesenswert?

mit
1
void set_pwm_duty_cycle(uint16_t highTime_ms)
2
{
3
  htim4.Instance->CCR1 = highTime_ms;
4
}

funktioniert es jetzt.

von Kevin M. (arduinolover)


Lesenswert?

M. G. schrieb:
> funktioniert es jetzt.

Wer hätte das gedacht.....

von W.S. (Gast)


Lesenswert?

Kevin M. schrieb:
> Es gibt keinen Grund das so zu tun, schreib einfach den gewünschten Duty
> Cycle direkt in das Register.
>
> z.B. TIM4->CCR1 = X;

Tja, normalerweise tut man das so. Aber hier haben wir einen TO, der 
ganz offensichtlich NUR sowas wie Cube benutzt und vermutlich so denkt 
wie einer, der mich vor kurzem mal so angeblafft hat: "Ich programmiere 
nicht auf Registerebene".

Allerdings gebe ich zu, daß man beim Benutzen von PWM-Registern an den 
Timern gut tut, einen Blick in das Manual des verwendeten µC zu tun.

Eben zu dem Zweck, daß man das Verhalten dieses Peripheriecores beim 
Überlaufen oder Reset des Zähler-Registers sich genauer anschaut und die 
ON-Zeit des PWM-Ausganges geeignet wählt, also entweder 0..X oder 
X..Zählende, je nachdem, was man erreichen will.

W.S.

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.