Forum: Mikrocontroller und Digitale Elektronik STM32 - UART Ausgabe - Wie richtig?


von Viktor S. (viktor_s)


Lesenswert?

Sehr geehrte Forum Mitglieder,

ich setze mich gerade mit STM32 und UART auseinander.
Dazu habe ich mir eine kleine Schrittkette gebaut.

Die Schrittkette scheint viel zu schnell abzulaufen, sodass die
UART-Ausgaben nicht gesendet werden. Man müsste jeder Ausgabe Zeit geben
aber das Programm  nicht anhalten.

Gibt es eine Abfrage ob die UART-Ausgabe abgeschlossen ist?

Frage: Wie macht Ihr die UART-Ausgaben?
1
/* USER CODE BEGIN Header */
2
/**
3
  ******************************************************************************
4
  * @file           : main.c
5
  * @brief          : Main program body
6
  ******************************************************************************
7
  * @attention
8
  *
9
  * Copyright (c) 2023 STMicroelectronics.
10
  * All rights reserved.
11
  *
12
  * This software is licensed under terms that can be found in the LICENSE file
13
  * in the root directory of this software component.
14
  * If no LICENSE file comes with this software, it is provided AS-IS.
15
  *
16
  ******************************************************************************
17
  */
18
/* USER CODE END Header */
19
/* Includes ------------------------------------------------------------------*/
20
#include "main.h"
21
22
/* Private includes ----------------------------------------------------------*/
23
/* USER CODE BEGIN Includes */
24
#include "string.h"
25
/* USER CODE END Includes */
26
27
/* Private typedef -----------------------------------------------------------*/
28
/* USER CODE BEGIN PTD */
29
30
/* USER CODE END PTD */
31
32
/* Private define ------------------------------------------------------------*/
33
/* USER CODE BEGIN PD */
34
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
UART_HandleTypeDef huart2;
44
DMA_HandleTypeDef hdma_usart2_rx;
45
DMA_HandleTypeDef hdma_usart2_tx;
46
47
/* USER CODE BEGIN PV */
48
49
/* USER CODE END PV */
50
51
/* Private function prototypes -----------------------------------------------*/
52
void SystemClock_Config(void);
53
static void MX_GPIO_Init(void);
54
static void MX_DMA_Init(void);
55
static void MX_USART2_UART_Init(void);
56
/* USER CODE BEGIN PFP */
57
58
/* USER CODE END PFP */
59
60
/* Private user code ---------------------------------------------------------*/
61
/* USER CODE BEGIN 0 */
62
63
64
#define RxBuf_Size 10
65
#define MainBuf_Size 20
66
67
uint8_t RxBuf[RxBuf_Size];
68
uint8_t MainBuf[MainBuf_Size];
69
70
void HAL_UARTEx_RxEventCallback( UART_HandleTypeDef *huart, uint16_t Size ){
71
72
  //if(huart -> Instance == USART2 ){
73
    for(int i=0;i < sizeof(MainBuf) ; i++){
74
        MainBuf[i] = '0';
75
    }
76
77
    memcpy ( MainBuf, RxBuf, Size );
78
79
    for(int i=0;i < Size ; i++){
80
      RxBuf[i] = '0';
81
    }
82
      HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_Size);
83
      __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
84
85
  //}
86
87
}
88
89
90
/* USER CODE END 0 */
91
92
/**
93
  * @brief  The application entry point.
94
  * @retval int
95
  */
96
int main(void)
97
{
98
  int flag =0;
99
  int flag2 =0;
100
  int iState =0;
101
102
103
  uint32_t t1;
104
  uint32_t t2;
105
  uint32_t dt;
106
107
108
  /* USER CODE BEGIN 1 */
109
110
  /* USER CODE END 1 */
111
112
  /* MCU Configuration--------------------------------------------------------*/
113
114
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
115
  HAL_Init();
116
117
  /* USER CODE BEGIN Init */
118
119
  /* USER CODE END Init */
120
121
  /* Configure the system clock */
122
  SystemClock_Config();
123
124
  /* USER CODE BEGIN SysInit */
125
126
  /* USER CODE END SysInit */
127
128
  /* Initialize all configured peripherals */
129
  MX_GPIO_Init();
130
  MX_DMA_Init();
131
  MX_USART2_UART_Init();
132
  /* USER CODE BEGIN 2 */
133
134
135
  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_Size);
136
137
138
    __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
139
140
    HAL_UART_Transmit_DMA(&huart2, "Hallo", 5);
141
142
143
  /* USER CODE END 2 */
144
145
  /* Infinite loop */
146
  /* USER CODE BEGIN WHILE */
147
  while (1)
148
  {
149
150
    switch(iState){
151
152
    case 0:
153
154
      HAL_UART_Transmit_DMA(&huart2, "Welcome! \n\r", 15);
155
156
      iState = 5;
157
158
      break;
159
160
    case 5:
161
162
      HAL_UART_Transmit_DMA(&huart2, "Step:5 \n\r", 15);
163
164
      iState = 10;
165
166
      break;
167
168
    case 10:
169
170
      t1 = HAL_GetTick();
171
172
      iState = 15;
173
174
      break;
175
176
    case 15:
177
178
179
180
      HAL_UART_Transmit_DMA(&huart2, "Waiting... \n\r", 15);
181
182
183
      iState = 20;
184
185
      break;
186
187
    case 20:
188
189
190
      t2 = HAL_GetTick();
191
192
      dt = t2-t1;
193
194
      if(dt > 10000){
195
        iState = 25;
196
      }
197
198
      break;
199
200
    case 25:
201
202
203
204
      HAL_UART_Transmit_DMA(&huart2, "Choose >1< \n\r", 15);
205
      //HAL_UART_Transmit(&huart2, "Choose >1< \n\r", 15, 1000);
206
207
      iState = 30;
208
      break;
209
210
    case 30:
211
212
      //HAL_UART_Transmit_DMA(&huart2, "Step:3 \n\r", 15);
213
214
      if(MainBuf[0] == '1'){
215
        iState = 300;
216
      }
217
218
      //xUartRcvDatProcessed = 1;
219
220
      break;
221
    case 300:
222
      HAL_UART_Transmit_DMA(&huart2, "Step:300 \n\r", 15);
223
224
      //HAL_UART_Transmit(&huart2, "Works! \n\r", 15,1000);
225
226
      iState = 500;
227
228
      break;
229
230
    case 500:
231
232
      HAL_UART_Transmit_DMA(&huart2, "Step:500 \n\r", 15);
233
234
      iState = 501;
235
236
      break;
237
238
    case 501:
239
      break;
240
241
    }
242
243
244
245
    /* USER CODE END WHILE */
246
247
    /* USER CODE BEGIN 3 */
248
  }
249
  /* USER CODE END 3 */
250
}
251
252
/**
253
  * @brief System Clock Configuration
254
  * @retval None
255
  */
256
void SystemClock_Config(void)
257
{
258
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
259
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
260
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
261
262
  /** Initializes the RCC Oscillators according to the specified parameters
263
  * in the RCC_OscInitTypeDef structure.
264
  */
265
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
266
  RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
267
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
268
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
269
  {
270
    Error_Handler();
271
  }
272
273
  /** Initializes the CPU, AHB and APB buses clocks
274
  */
275
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
276
                              |RCC_CLOCKTYPE_PCLK1;
277
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48;
278
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
279
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
280
281
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
282
  {
283
    Error_Handler();
284
  }
285
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
286
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
287
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
288
  {
289
    Error_Handler();
290
  }
291
}
292
293
/**
294
  * @brief USART2 Initialization Function
295
  * @param None
296
  * @retval None
297
  */
298
static void MX_USART2_UART_Init(void)
299
{
300
301
  /* USER CODE BEGIN USART2_Init 0 */
302
303
  /* USER CODE END USART2_Init 0 */
304
305
  /* USER CODE BEGIN USART2_Init 1 */
306
307
  /* USER CODE END USART2_Init 1 */
308
  huart2.Instance = USART2;
309
  huart2.Init.BaudRate = 9600;
310
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
311
  huart2.Init.StopBits = UART_STOPBITS_1;
312
  huart2.Init.Parity = UART_PARITY_NONE;
313
  huart2.Init.Mode = UART_MODE_TX_RX;
314
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
315
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
316
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
317
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
318
  if (HAL_UART_Init(&huart2) != HAL_OK)
319
  {
320
    Error_Handler();
321
  }
322
  /* USER CODE BEGIN USART2_Init 2 */
323
324
  /* USER CODE END USART2_Init 2 */
325
326
}
327
328
/**
329
  * Enable DMA controller clock
330
  */
331
static void MX_DMA_Init(void)
332
{
333
334
  /* DMA controller clock enable */
335
  __HAL_RCC_DMA1_CLK_ENABLE();
336
337
  /* DMA interrupt init */
338
  /* DMA1_Channel4_5_6_7_IRQn interrupt configuration */
339
  HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0, 0);
340
  HAL_NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
341
342
}
343
344
/**
345
  * @brief GPIO Initialization Function
346
  * @param None
347
  * @retval None
348
  */
349
static void MX_GPIO_Init(void)
350
{
351
  GPIO_InitTypeDef GPIO_InitStruct = {0};
352
/* USER CODE BEGIN MX_GPIO_Init_1 */
353
/* USER CODE END MX_GPIO_Init_1 */
354
355
  /* GPIO Ports Clock Enable */
356
  __HAL_RCC_GPIOC_CLK_ENABLE();
357
  __HAL_RCC_GPIOF_CLK_ENABLE();
358
  __HAL_RCC_GPIOA_CLK_ENABLE();
359
360
  /*Configure GPIO pin Output Level */
361
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
362
363
  /*Configure GPIO pin : B1_Pin */
364
  GPIO_InitStruct.Pin = B1_Pin;
365
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
366
  GPIO_InitStruct.Pull = GPIO_NOPULL;
367
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
368
369
  /*Configure GPIO pin : LD2_Pin */
370
  GPIO_InitStruct.Pin = LD2_Pin;
371
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
372
  GPIO_InitStruct.Pull = GPIO_NOPULL;
373
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
374
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
375
376
/* USER CODE BEGIN MX_GPIO_Init_2 */
377
/* USER CODE END MX_GPIO_Init_2 */
378
}
379
380
/* USER CODE BEGIN 4 */
381
382
/* USER CODE END 4 */
383
384
/**
385
  * @brief  This function is executed in case of error occurrence.
386
  * @retval None
387
  */
388
void Error_Handler(void)
389
{
390
  /* USER CODE BEGIN Error_Handler_Debug */
391
  /* User can add his own implementation to report the HAL error return state */
392
  __disable_irq();
393
  while (1)
394
  {
395
  }
396
  /* USER CODE END Error_Handler_Debug */
397
}
398
399
#ifdef  USE_FULL_ASSERT
400
/**
401
  * @brief  Reports the name of the source file and the source line number
402
  *         where the assert_param error has occurred.
403
  * @param  file: pointer to the source file name
404
  * @param  line: assert_param error line source number
405
  * @retval None
406
  */
407
void assert_failed(uint8_t *file, uint32_t line)
408
{
409
  /* USER CODE BEGIN 6 */
410
  /* User can add his own implementation to report the file name and line number,
411
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
412
  /* USER CODE END 6 */
413
}
414
#endif /* USE_FULL_ASSERT */

von Helmut -. (dc3yc)


Lesenswert?

Lies bitte die "Wichtige Regeln - erst lesen, dann posten!", bevor du so 
einen Scheiß schreibst!

von Wastl (hartundweichware)


Angehängte Dateien:

Lesenswert?

Viktor S. schrieb:
> Sehr geehrte Forum Mitglieder,
>
> ich setze mich gerade mit STM32 und UART auseinander.

Fuck!

Setze dich erst einmal mit den Regeln hier im Forum auseinander!

von Harry L. (mysth)


Lesenswert?


von Peter (pittyj)


Lesenswert?

Ich verzichte auf DMA, und nehme die normale Interrupt Variante.
Ich nehme auch den Code, den die STM IDE automatisch erzeugt, und es 
lief auf Anhieb.
Durch deinen Code steige ich nicht durch. Man hätte die Leerzeilen und 
automatisch generierten Kommentare löschen können, damit man 'deine 
Eigenleistung' überhaupt erkennt.
Bei so einem Code hat keiner mehr Lust sich den überhaupt anzuschauen.

von J. S. (jojos)


Lesenswert?

es ist egal ob man die Interrupt oder DMA Variante nimmt, es bleibt das 
Problem das die Funktion  HAL_UART_Transmit_xx erst wieder ausgeführt 
wird wenn der verwendete UART frei ist. D.h. sie liefert sofort den 
Fehler HAL_BUSY, blöderweise auch ohne ein Timeout abzuwarten.
Es fehlt also ein Ausgabepuffer und das würde durch den Code von Harry 
gelöst. Oder man verwendet das gepufferte stdio über printf, dazu 
braucht es nur eine Funktion _write() die den String über den 
gewünschten UART ausgibt.

von Viktor S. (viktor_s)


Lesenswert?

Sehr geehrte Forum-Mitglieder,

erstmal vorweg: an Helmut und Wastl: Den Hinweis, dass man einen langen 
Code
nicht senden soll, sieht man nicht auf Anhieb.
Ich werde den Hinweis in Zukunft beachten, so wie Ihr in Zukunft auf 
Euere Sprache achten müsst. Mit so einer Art kommt man nicht weiter! 
Geht ihr auch so mit Eueren Kollegen und anderen Mitmenschen um?

Vielen Dank an die Mitglieder, welche meine Frage ernst genommen haben.

Zurück zum Thema:
Wie J.S. schreibt, wäre die Lösung auf den UART zu warten und erst 
wieder eine Ausgabe zu liefern, wenn der UART bereit dafür ist.

Kann mir Jemand sagen, wie man den aktuellen Zustand des verwendeten 
UART abfragen kann? Vielleicht einen Hinweis auf eine Doku geben. Bis 
jetzt habe ich nichts außer "HAL_UART_TxCpltCallback" gefunden.

von J. S. (jojos)


Lesenswert?

HAL_UART_Transmit liefert busy zurück, das könnte man auswerten. Aber es 
blockiert ja den Ablauf, ich würde das nicht so machen.

von Harry L. (mysth)


Lesenswert?

Viktor S. schrieb:
> Vielleicht einen Hinweis auf eine Doku geben.

https://www.st.com/resource/en/user_manual/um1725-description-of-stm32f4-hal-and-lowlayer-drivers-stmicroelectronics.pdf

Wie man das alles benutzt, siehst du in meinem oben verlinkten Code.

von A. F. (chefdesigner)


Lesenswert?

Wastl schrieb:
> Fuck!
>
> Setze dich erst einmal mit den Regeln hier im Forum auseinander!

Das erste Wort hättest du dir sparen können :-)
Dann wäre deine Kritik auch besser verarbeitbar :-)

von Wastl (hartundweichware)


Angehängte Dateien:

Lesenswert?

Viktor S. schrieb:
> Wie J.S. schreibt, wäre die Lösung auf den UART zu warten und erst
> wieder eine Ausgabe zu liefern, wenn der UART bereit dafür ist.
>
> Kann mir Jemand sagen, wie man den aktuellen Zustand des verwendeten
> UART abfragen kann? Vielleicht einen Hinweis auf eine Doku geben. Bis
> jetzt habe ich nichts außer "HAL_UART_TxCpltCallback" gefunden.

Das "Geheimnis" ist: Low-Level Library verwenden. Man kann es gar
nicht deutlich genug sagen: das ist ein wahrer Schatz der
harwarenahen Programmierung (woanders habe ich so etwas noch
nie gesehen). Alles Zugriffsmakros die es erlauben auf Register-
Ebene zu programmieren ohne jemals ein Register oder ein Register-
Bit mit Namen zu kennen. Macht ein bisschen mehr Arbeit als das
HAL Zeugs aber man wird mit Code belohnt der so arbeitet wie
man sich es vorstellt. Natürlich gibt es auch in der LL einige
benötigte Funktionen die einem das Leben leicht machen, aber
die Zugriffsmakros sind schon das Allerwichtigste. Im Prinzip
könnte man wohl auch die gesamte Hardware-Initialisierung
damit bewerkstelligen.

von Steve van de Grens (roehrmond)


Lesenswert?

Viktor S. schrieb:
> Den Hinweis, dass man einen langen Code nicht
> senden soll, sieht man nicht auf Anhieb.

Das ist eine sehr faule Ausrede.

von Harry L. (mysth)


Lesenswert?

Wastl schrieb:
> Das "Geheimnis" ist: Low-Level Library verwenden.

Kann man machen, ist aber in den allermeisten Fällen vollkommen unnötig.
Das braucht man eigentlich nur auf sehr, sehr kleinen MCUs, wo man um 
jedes Byte kämpfen muß.

HAL ist komplexer, aber auch einfacher anzuwenden. (wenn man es einmal 
verstanden hat)
Auf die Ausführungs-Geschwindigkeit hat das nahezu keinen Einfluss und 
die Einsparungen bei der Größe des Binary sind sehr überschaubar.

von Helmut -. (dc3yc)


Lesenswert?

Viktor S. schrieb:
> Den Hinweis, dass man einen langen
> Code
> nicht senden soll, sieht man nicht auf Anhieb.

Dann als Tip, bevor du noch mehr Fehler programmierst, weil du was nicht 
lesen kannst: Gehe mal zum Optiker und lasse dir eine Brille verpassen. 
Ich jedenfalls habe keine Lust, dein Programm zu lesen, weil ich mit dem 
Scrollen nicht nachkomme.

von Klaus (feelfree)


Lesenswert?

Helmut -. schrieb:
> Ich jedenfalls habe keine Lust, dein Programm zu lesen, weil ich mit dem
> Scrollen nicht nachkomme.

Prima, jeder unfreundliche Meckerer weniger tut dem Forum gut.

von Viktor S. (viktor_s)


Lesenswert?

Wie gesagt, werde in Zukunft die Regeln mehr beachten.
Ich wollte auf keinen Fall unfreundlich wirken.

von Viktor S. (viktor_s)


Angehängte Dateien:

Lesenswert?

Ich bin nochmal ein Schritt zurück gegangen und habe
einfach ein Projekt erstellt, wo ich per UART Texte
in einer Schrittkette ausgebe.

Am Anfang mache ich eine Pause von 5sek, bevor der nächste
Text ausgegeben wird.
Zum Schluss gebe ich die Texte per UART ohne zeitlicher
Verzögerung aus.

Das Seltsame ist, dass bei  den ersten Ausgaben immer
eine "Sta"-Zeichenkette davor geschrieben wird.
Bei den letzten Ausgaben jedoch nicht.

Habe von der Ausgabe ein Video gemacht:

https://streamable.com/fhjswh

Habt Ihr sowas erlebt? Warum passiert das?
Ich versuche mich  erstmal mit der HAL irgendwie auseinander zu setzen.

: Bearbeitet durch User
von Rick (rick)


Lesenswert?

Viktor S. schrieb:
> Das Seltsame ist, dass bei  den ersten Ausgaben immer
> eine "Sta"-Zeichenkette davor geschrieben wird.
> Bei den letzten Ausgaben jedoch nicht.
Dein Programm läuft zweimal. Nach einem kurzen Moment kommt der Debugger 
und macht einen Reset. Da hat er gerade die ersten drei Zeichen 
geschafft.
Mach mal die Pause etwas länger oder schreib drei Leerzeichen und einen 
Zeilenumbruch als Erstes...

von Viktor S. (viktor_s)


Lesenswert?

Hallo Rick, ich habe deine Anweisungen befolgt und ein neues Video 
aufgezeichnet.

https://streamable.com/3jjsfy

Irgendetwas stimmt da nicht.
Die Zeichenkette "Sta" kommt ein paar Mal.

von Rick (rick)


Lesenswert?

Da ich die HAL nicht nutze (zu abstrakt), kann ich zu deren Verwendung 
nur wenig sagen, aber mir ist aufgefallen das bei dir der dritte 
Parameter (Size) bei HAL_UART_Transmit nicht an die Datenlänge angepasst 
wird...

von Viktor S. (viktor_s)


Lesenswert?

Habe für den auszugebenden String eine feste Größe
von 5 Zeichen zugeordnet.

Vor jeder Ausgabe leere ich den String.

Nun scheint es zu funktionieren.

https://streamable.com/so96wr

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.