STM32 - UART Ausgabe - Wie richtig?

OP #7569446
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 */
#7569690
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.

#7569765
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.

OP #7569952
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.

#7570035
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.

Angehängte Dateien:
#7570058
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.

OP #7570241
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.

Angehängte Dateien:
#7570255
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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren