Guten Tag,
ich versuche in STM32CubeIDE mit Hilfe von PWM flexible Rechtecksignale
(es geht nur um TTL, also keine Stromschleife oder ähnliches) im
zweistelligen Mikrosekundenbereich an einem Port-Pin zu erzeugen. Der µC
ist ein STM32F411 @50 MHz und ich benutze Timer 11 in Verbindung mit Pin
PB9. Meine Tests funktionieren halbwegs gut, so lange die Periodendauer
konstant ist und ich nur die Pulsweite anpasse. Aber selbst dann sind
der erste Puls und die erste Periode laut Speicheroszi ein paar µS zu
kurz oder zu lang.
Dies ist der Code für Clock- und Timer-Initialisierung, der aufgrund
meiner Einstellungen in STM32CubeIDE erzeugt wurde:
1 | void SystemClock_Config(void)
|
2 | {
|
3 | RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
4 | RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
5 |
|
6 | /** Configure the main internal regulator output voltage
|
7 | */
|
8 | __HAL_RCC_PWR_CLK_ENABLE();
|
9 | __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
|
10 |
|
11 | /** Initializes the RCC Oscillators according to the specified parameters
|
12 | * in the RCC_OscInitTypeDef structure.
|
13 | */
|
14 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
|
15 | RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
16 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
17 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
18 | RCC_OscInitStruct.PLL.PLLM = 4;
|
19 | RCC_OscInitStruct.PLL.PLLN = 50;
|
20 | RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
|
21 | RCC_OscInitStruct.PLL.PLLQ = 4;
|
22 | if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
|
23 | {
|
24 | Error_Handler();
|
25 | }
|
26 |
|
27 | /** Initializes the CPU, AHB and APB buses clocks
|
28 | */
|
29 | RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
30 | |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
31 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
32 | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
33 | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
|
34 | RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
35 |
|
36 | if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
|
37 | {
|
38 | Error_Handler();
|
39 | }
|
40 | }
|
41 |
|
42 | /**
|
43 | * @brief TIM11 Initialization Function
|
44 | * @param None
|
45 | * @retval None
|
46 | */
|
47 | static void MX_TIM11_Init(void)
|
48 | {
|
49 |
|
50 | /* USER CODE BEGIN TIM11_Init 0 */
|
51 |
|
52 | /* USER CODE END TIM11_Init 0 */
|
53 |
|
54 | TIM_OC_InitTypeDef sConfigOC = {0};
|
55 |
|
56 | /* USER CODE BEGIN TIM11_Init 1 */
|
57 |
|
58 | /* USER CODE END TIM11_Init 1 */
|
59 | htim11.Instance = TIM11;
|
60 | htim11.Init.Prescaler = 0;
|
61 | htim11.Init.CounterMode = TIM_COUNTERMODE_UP;
|
62 | htim11.Init.Period = 999;
|
63 | htim11.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
64 | htim11.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
65 | if (HAL_TIM_Base_Init(&htim11) != HAL_OK)
|
66 | {
|
67 | Error_Handler();
|
68 | }
|
69 | if (HAL_TIM_PWM_Init(&htim11) != HAL_OK)
|
70 | {
|
71 | Error_Handler();
|
72 | }
|
73 | sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
74 | sConfigOC.Pulse = 500;
|
75 | sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
76 | sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
77 | if (HAL_TIM_PWM_ConfigChannel(&htim11, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
|
78 | {
|
79 | Error_Handler();
|
80 | }
|
81 | /* USER CODE BEGIN TIM11_Init 2 */
|
82 |
|
83 | /* USER CODE END TIM11_Init 2 */
|
84 | HAL_TIM_MspPostInit(&htim11);
|
85 |
|
86 | }
|
Ich benutze ein Array, um die Daten für die weiteren Pulse zu erzeugen:
1 | /* USER CODE BEGIN PV */
|
2 | int16_t pwm_tx_array [4][2] = {
|
3 | { 999, 400 },
|
4 | { 999, 300 },
|
5 | { 999, 200 },
|
6 | { 999, 100 },
|
7 | };
|
8 | int pwm_tx_finished = 0;
|
9 | int16_t pwm_tx_array_counter = 0;
|
10 | int pwm_tx_array_size = sizeof(pwm_tx_array)/sizeof(pwm_tx_array[0]);
|
11 | int16_t pwm_tx_period = 0;
|
12 | int16_t pwm_tx_pulse = 0;
|
13 | /* USER CODE END PV */
|
Im CallBack für den jeweils aktuellen Puls bereite ich den nächsten Puls
vor:
1 | /* USER CODE BEGIN 4 */
|
2 | void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim)
|
3 | {
|
4 | if (htim->Instance == TIM11){
|
5 | if (!pwm_tx_finished)
|
6 | {
|
7 | HAL_TIM_PWM_Stop_IT(&htim11, TIM_CHANNEL_1);
|
8 | if (pwm_tx_array_counter == pwm_tx_array_size)
|
9 | {
|
10 | pwm_tx_finished = 1;
|
11 | } else {
|
12 | pwm_tx_period = pwm_tx_array[pwm_tx_array_counter][0];
|
13 | pwm_tx_pulse = pwm_tx_array[pwm_tx_array_counter][1];
|
14 | TIM_OC_InitTypeDef sConfigOC;
|
15 |
|
16 | // htim11.Init.Period = pwm_tx_period;
|
17 | // HAL_TIM_PWM_Init(&htim11);
|
18 |
|
19 | sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
20 | sConfigOC.Pulse = pwm_tx_pulse;
|
21 | sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
22 | sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
23 | HAL_TIM_PWM_ConfigChannel(&htim11, &sConfigOC, TIM_CHANNEL_1);
|
24 | HAL_TIM_PWM_Start_IT(&htim11, TIM_CHANNEL_1);
|
25 | ++pwm_tx_array_counter;
|
26 | }
|
27 | }
|
28 | }
|
29 | }
|
30 | /* USER CODE END 4 */
|
Das funktioniert wie bereits gesagt bis auf die Ungenauigkeit des ersten
Puls ganz gut, aber sobald ich die 2 Zeilen für die Periode
einkommentiere und Array-Werte mit wechselnden Perioden benutze, bekomme
ich echt merkwürdige Kurven auf dem Speicheroszi angezeigt. Zum Testen
habe ich hier einfach immer kürzere Perioden mit 50:50 Tastverhältnis
gewählt, es ist aber nur ein Beispiel:
1 | /* USER CODE BEGIN PV */
|
2 | int16_t pwm_tx_array [4][2] = {
|
3 | { 799, 400 },
|
4 | { 599, 300 },
|
5 | { 399, 200 },
|
6 | { 199, 100 },
|
7 | };
|
8 | int pwm_tx_finished = 0;
|
9 | int16_t pwm_tx_array_counter = 0;
|
10 | int pwm_tx_array_size = sizeof(pwm_tx_array)/sizeof(pwm_tx_array[0]);
|
11 | int16_t pwm_tx_period = 0;
|
12 | int16_t pwm_tx_pulse = 0;
|
13 | /* USER CODE END PV */
|
Der Main Loop ist leer und der µC erzeugt momentan nur diese PWM. Ich
benötige später nicht wirklich 2 Mikrosekunden lange Pulse und
eigentlich würden mir +-2 Mikrosekunden Genauikgeit ausreichen, aber da
ist ja noch das Problem mit der zu ändernden Periode und ich wollte auch
wegen des ersten ungenauen Puls wissen, ob die nachfolgenden Pulse exakt
sind...Aber sie sind es: der 2 µS Puls zum Schluss wird auf dem
Speicheroszi exakt mit 2 µS gemessen. Wenn ich im Callback vor dem
Stoppen des Interrupts einen Breakpoint setze, sehen die verschiedenen
Pulse bei konstanter Periode bis auf den ersten PWM Durchlauf stets
präzise aus.
Kann mir jemand bei diesem Problem helfen? Oder gibt es eine andere
Möglichkeit, eine Abfolge von Rechtecksignalen bis runter in den
zweistelligen Mikrosekundenbereich zu erzeugen?
Vielen Dank und liebe Grüße
Hendrik