comparison Small_CPU/Src/uart.c @ 916:4832981f9af8 Evo_2_23

External sensor UART: Switch to DMA TX transfers: The previous version used polling tx function to transfer data. Because of the short command length of the protocols supported this was no big issue. New protocolls (like GNSS) have longer command sequence which have an impact to the program flow. That's why the implementation has been changed to DMA transmission.
author Ideenmodellierer
date Mon, 28 Oct 2024 20:34:58 +0100
parents 9e2f9b91e827
children f72613a152dd
comparison
equal deleted inserted replaced
915:ff318ae65dd0 916:4832981f9af8
35 #define CHUNK_SIZE (25u) /* the DMA will handle chunk size transfers */ 35 #define CHUNK_SIZE (25u) /* the DMA will handle chunk size transfers */
36 #define CHUNKS_PER_BUFFER (5u) 36 #define CHUNKS_PER_BUFFER (5u)
37 37
38 38
39 39
40 DMA_HandleTypeDef hdma_usart1_rx, hdma_usart6_rx, hdma_usart6_tx; 40 DMA_HandleTypeDef hdma_usart1_rx, hdma_usart1_tx, hdma_usart6_rx, hdma_usart6_tx;
41 41
42 uint8_t rxBuffer[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */ 42 uint8_t rxBuffer[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */
43 uint8_t txBuffer[CHUNK_SIZE]; /* tx uses less bytes */
44 uint8_t txBufferQue[CHUNK_SIZE]; /* In MUX mode command may be send shortly after each other => allow q 1 entry que */
45 uint8_t txBufferQueLen;
46
43 uint8_t rxBufferUart6[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */ 47 uint8_t rxBufferUart6[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */
44 uint8_t txBufferUart6[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */ 48 uint8_t txBufferUart6[CHUNK_SIZE * CHUNKS_PER_BUFFER]; /* The complete buffer has a X * chunk size to allow variations in buffer read time */
45 49
46 static uint8_t rxWriteIndex; /* Index of the data item which is analysed */ 50 static uint8_t rxWriteIndex; /* Index of the data item which is analysed */
47 static uint8_t rxReadIndex; /* Index at which new data is stared */ 51 static uint8_t rxReadIndex; /* Index at which new data is stared */
48 static uint8_t lastCmdIndex; /* Index of last command which has not been completely received */ 52 static uint8_t lastCmdIndex; /* Index of last command which has not been completely received */
49 static uint8_t dmaActive; /* Indicator if DMA reception needs to be started */ 53 static uint8_t dmaRxActive; /* Indicator if DMA reception needs to be started */
54 static uint8_t dmaTxActive; /* Indicator if DMA reception needs to be started */
50 55
51 56
52 57
53 58
54 /* Exported functions --------------------------------------------------------*/ 59 /* Exported functions --------------------------------------------------------*/
55 60
61 void clearRxBuffer(void)
62 {
63 uint16_t index = 0;
64 do
65 {
66 rxBuffer[index++] = BUFFER_NODATA_LOW;
67 rxBuffer[index++] = BUFFER_NODATA_HIGH;
68 } while (index < sizeof(rxBuffer));
69 }
56 70
57 void MX_USART1_UART_Init(void) 71 void MX_USART1_UART_Init(void)
58 { 72 {
59 /* regular init */ 73 /* regular init */
60
61 huart1.Instance = USART1; 74 huart1.Instance = USART1;
62 huart1.Init.BaudRate = 19200; 75 huart1.Init.BaudRate = 19200;
63 huart1.Init.WordLength = UART_WORDLENGTH_8B; 76 huart1.Init.WordLength = UART_WORDLENGTH_8B;
64 huart1.Init.StopBits = UART_STOPBITS_1; 77 huart1.Init.StopBits = UART_STOPBITS_1;
65 huart1.Init.Parity = UART_PARITY_NONE; 78 huart1.Init.Parity = UART_PARITY_NONE;
69 82
70 HAL_UART_Init(&huart1); 83 HAL_UART_Init(&huart1);
71 84
72 MX_USART1_DMA_Init(); 85 MX_USART1_DMA_Init();
73 86
74 memset(rxBuffer,BUFFER_NODATA,sizeof(rxBuffer)); 87 HAL_NVIC_SetPriority(USART1_IRQn, 1, 3);
88 HAL_NVIC_EnableIRQ(USART1_IRQn);
89
90 clearRxBuffer();
75 rxReadIndex = 0; 91 rxReadIndex = 0;
76 lastCmdIndex = 0; 92 lastCmdIndex = 0;
77 rxWriteIndex = 0; 93 rxWriteIndex = 0;
78 dmaActive = 0; 94 dmaRxActive = 0;
95 dmaTxActive = 0;
96 txBufferQueLen = 0;
79 } 97 }
80 98
81 99
82 100
83 void MX_USART1_UART_DeInit(void) 101 void MX_USART1_UART_DeInit(void)
84 { 102 {
85 HAL_DMA_Abort(&hdma_usart1_rx); 103 HAL_DMA_Abort(&hdma_usart1_rx);
86 HAL_DMA_DeInit(&hdma_usart1_rx); 104 HAL_DMA_DeInit(&hdma_usart1_rx);
105 HAL_DMA_Abort(&hdma_usart1_tx);
106 HAL_DMA_DeInit(&hdma_usart1_tx);
87 HAL_UART_DeInit(&huart1); 107 HAL_UART_DeInit(&huart1);
88 dmaActive = 0; 108 dmaRxActive = 0;
109 dmaTxActive = 0;
110 txBufferQueLen = 0;
89 } 111 }
90 112
91 void MX_USART1_DMA_Init() 113 void MX_USART1_DMA_Init()
92 { 114 {
93 /* DMA controller clock enable */ 115 /* DMA controller clock enable */
106 hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; 128 hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
107 HAL_DMA_Init(&hdma_usart1_rx); 129 HAL_DMA_Init(&hdma_usart1_rx);
108 130
109 __HAL_LINKDMA(&huart1,hdmarx,hdma_usart1_rx); 131 __HAL_LINKDMA(&huart1,hdmarx,hdma_usart1_rx);
110 132
133 hdma_usart1_tx.Instance = DMA2_Stream7;
134 hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;
135 hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
136 hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
137 hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
138 hdma_usart1_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
139 hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
140 hdma_usart1_tx.Init.Mode = DMA_NORMAL;
141 hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
142 hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
143 HAL_DMA_Init(&hdma_usart1_tx);
144
145 __HAL_LINKDMA(&huart1,hdmatx,hdma_usart1_tx);
146
147
111 /* DMA interrupt init */ 148 /* DMA interrupt init */
112 HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0); 149 HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 2, 2);
113 HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn); 150 HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
151 HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 2, 1);
152 HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
114 } 153 }
115 154
116 155
117 void GNSS_IO_init() { 156 void GNSS_IO_init() {
118 157
134 GPIO_InitStruct.Alternate = GPIO_AF8_USART6; 173 GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
135 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 174 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
136 175
137 /* USART6 DMA Init */ 176 /* USART6 DMA Init */
138 /* USART6_RX Init */ 177 /* USART6_RX Init */
139 hdma_usart6_rx.Instance = DMA2_Stream2; 178 hdma_usart6_rx.Instance = DMA2_Stream1;
140 hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5; 179 hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5;
141 hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; 180 hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
142 hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE; 181 hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
143 hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE; 182 hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE;
144 hdma_usart6_rx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE; 183 hdma_usart6_rx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
150 189
151 __HAL_LINKDMA(&huart6, hdmarx, hdma_usart6_rx); 190 __HAL_LINKDMA(&huart6, hdmarx, hdma_usart6_rx);
152 191
153 /* USART6_TX Init */ 192 /* USART6_TX Init */
154 hdma_usart6_tx.Instance = DMA2_Stream6; 193 hdma_usart6_tx.Instance = DMA2_Stream6;
155 hdma_usart6_tx.Init.Channel = DMA_CHANNEL_5; 194 hdma_usart6_tx.Init.Channel = DMA_CHANNEL_6;
156 hdma_usart6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; 195 hdma_usart6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
157 hdma_usart6_tx.Init.PeriphInc = DMA_PINC_DISABLE; 196 hdma_usart6_tx.Init.PeriphInc = DMA_PINC_DISABLE;
158 hdma_usart6_tx.Init.MemInc = DMA_MINC_ENABLE; 197 hdma_usart6_tx.Init.MemInc = DMA_MINC_ENABLE;
159 hdma_usart6_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE; 198 hdma_usart6_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
160 hdma_usart6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; 199 hdma_usart6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
217 { 256 {
218 indexstr[0] = '~'; 257 indexstr[0] = '~';
219 indexstr[1] = muxAddress; 258 indexstr[1] = muxAddress;
220 indexstr[2] = 0x0D; 259 indexstr[2] = 0x0D;
221 indexstr[3] = 0x0A; 260 indexstr[3] = 0x0A;
222 261 if(!dmaTxActive)
223 HAL_UART_Transmit(&huart1,indexstr,4,10); 262 {
263 memcpy(txBuffer, indexstr, 4);
264 dmaTxActive = 0;
265 if(HAL_OK == HAL_UART_Transmit_DMA(&huart1,txBuffer,4))
266 {
267 dmaTxActive = 1;
268 while(dmaTxActive)
269 {
270 HAL_Delay(1);
271 }
272 }
273 }
274 else
275 {
276 memcpy(txBufferQue, indexstr, 4);
277 txBufferQueLen = 4;
278 }
224 } 279 }
225 } 280 }
226 281
227 282
228 void UART_SendCmdString(uint8_t *cmdString) 283 void UART_SendCmdString(uint8_t *cmdString)
229 { 284 {
230 uint8_t cmdLength = strlen((char*)cmdString); 285 uint8_t cmdLength = strlen((char*)cmdString);
231 286
232 if(cmdLength < 20) /* A longer string is an indication for a missing 0 termination */ 287 if(dmaTxActive == 0)
233 { 288 {
234 if(dmaActive == 0) 289 if(cmdLength < CHUNK_SIZE) /* A longer string is an indication for a missing 0 termination */
290 {
291 if(dmaRxActive == 0)
292 {
293 UART_StartDMA_Receiption();
294 }
295 memcpy(txBuffer, cmdString, cmdLength);
296 if(HAL_OK == HAL_UART_Transmit_DMA(&huart1,txBuffer,cmdLength))
297 {
298 dmaTxActive = 1;
299 }
300 }
301 }
302 else
303 {
304 memcpy(txBufferQue, cmdString, cmdLength);
305 txBufferQueLen = cmdLength;
306 }
307 }
308
309 void UART_SendCmdUbx(uint8_t *cmd, uint8_t len)
310 {
311 if(len < CHUNK_SIZE) /* A longer string is an indication for a missing 0 termination */
312 {
313 if(dmaRxActive == 0)
235 { 314 {
236 UART_StartDMA_Receiption(); 315 UART_StartDMA_Receiption();
237 } 316 }
238 HAL_UART_Transmit(&huart1,cmdString,cmdLength,10); 317 memcpy(txBuffer, cmd, len);
318 HAL_UART_Transmit_DMA(&huart1,txBuffer,len);
239 } 319 }
240 } 320 }
241 321
242 322
243 void StringToInt(char *pstr, uint32_t *puInt32) 323 void StringToInt(char *pstr, uint32_t *puInt32)
266 *puint64 = result; 346 *puint64 = result;
267 } 347 }
268 348
269 void UART_StartDMA_Receiption() 349 void UART_StartDMA_Receiption()
270 { 350 {
271 if(dmaActive == 0) 351 if(dmaRxActive == 0)
272 { 352 {
273 if(HAL_OK == HAL_UART_Receive_DMA (&huart1, &rxBuffer[rxWriteIndex], CHUNK_SIZE)) 353 if(HAL_OK == HAL_UART_Receive_DMA (&huart1, &rxBuffer[rxWriteIndex], CHUNK_SIZE))
274 { 354 {
275 dmaActive = 1; 355 dmaRxActive = 1;
276 } 356 }
277 } 357 }
278 } 358 }
279 359
280 void UART_ChangeBaudrate(uint32_t newBaudrate) 360 void UART_ChangeBaudrate(uint32_t newBaudrate)
281 { 361 {
282 uint8_t dmaWasActive = dmaActive; 362 uint8_t dmaWasActive = dmaRxActive;
283 // HAL_DMA_Abort(&hdma_usart1_rx); 363 // HAL_DMA_Abort(&hdma_usart1_rx);
284 MX_USART1_UART_DeInit(); 364 MX_USART1_UART_DeInit();
285 //HAL_UART_Abort(&huart1); 365 //HAL_UART_Abort(&huart1);
286 //HAL_DMA_DeInit(&hdma_usart1_rx); 366 //HAL_DMA_DeInit(&hdma_usart1_rx);
287 367
288 368
289 // huart1.Instance->BRR = UART_BRR_SAMPLING8(HAL_RCC_GetPCLK2Freq()/2, newBaudrate); 369 // huart1.Instance->BRR = UART_BRR_SAMPLING8(HAL_RCC_GetPCLK2Freq()/2, newBaudrate);
290 huart1.Init.BaudRate = newBaudrate; 370 huart1.Init.BaudRate = newBaudrate;
291 HAL_UART_Init(&huart1); 371 HAL_UART_Init(&huart1);
292 MX_USART1_DMA_Init(); 372 MX_USART1_DMA_Init();
373 HAL_NVIC_SetPriority(USART1_IRQn, 1, 3);
374 HAL_NVIC_EnableIRQ(USART1_IRQn);
375
293 if(dmaWasActive) 376 if(dmaWasActive)
294 { 377 {
295 memset(rxBuffer,BUFFER_NODATA,sizeof(rxBuffer)); 378 clearRxBuffer();
296 rxReadIndex = 0; 379 rxReadIndex = 0;
297 rxWriteIndex = 0; 380 rxWriteIndex = 0;
298 dmaActive = 0; 381 dmaRxActive = 0;
382 txBufferQueLen = 0;
299 UART_StartDMA_Receiption(); 383 UART_StartDMA_Receiption();
300 } 384 }
301 } 385 }
302 386
303 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 387 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
304 { 388 {
305 if(huart == &huart1) 389 if(huart == &huart1)
306 { 390 {
307 dmaActive = 0; 391 dmaRxActive = 0;
308 rxWriteIndex+=CHUNK_SIZE; 392 rxWriteIndex+=CHUNK_SIZE;
309 if(rxWriteIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER) 393 if(rxWriteIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
310 { 394 {
311 rxWriteIndex = 0; 395 rxWriteIndex = 0;
312 } 396 }
314 { 398 {
315 UART_StartDMA_Receiption(); 399 UART_StartDMA_Receiption();
316 } 400 }
317 } 401 }
318 } 402 }
319 403 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
404 {
405 if(huart == &huart1)
406 {
407 dmaTxActive = 0;
408 UART_WriteData();
409 if(txBufferQueLen)
410 {
411 memcpy(txBuffer, txBufferQue, txBufferQueLen);
412 HAL_UART_Transmit_DMA(&huart1,txBuffer,txBufferQueLen);
413 dmaTxActive = 1;
414 txBufferQueLen = 0;
415 }
416 }
417 }
418
419 uint8_t isEndIndication(uint8_t index)
420 {
421 uint8_t ret = 0;
422 if(index % 2)
423 {
424 if(rxBuffer[index] == BUFFER_NODATA_HIGH)
425 {
426 ret = 1;
427 }
428 }
429 else
430 {
431 if(rxBuffer[index] == BUFFER_NODATA_LOW)
432 {
433 ret = 1;
434 }
435 }
436
437 return ret;
438 }
320 void UART_ReadData(uint8_t sensorType) 439 void UART_ReadData(uint8_t sensorType)
321 { 440 {
322 uint8_t localRX = rxReadIndex; 441 uint8_t localRX = rxReadIndex;
323 442 uint8_t futureIndex = rxReadIndex + 1;
324 while((rxBuffer[localRX]!=BUFFER_NODATA)) 443 uint8_t moreData = 0;
325 { 444
326 switch (sensorType) 445 if(futureIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
327 { 446 {
328 case SENSOR_MUX: 447 futureIndex = 0;
329 case SENSOR_DIGO2: uartO2_ProcessData(rxBuffer[localRX]); 448 }
330 break; 449
331 #ifdef ENABLE_CO2_SUPPORT 450 if((!isEndIndication(localRX)) || (!isEndIndication(futureIndex))) do
332 case SENSOR_CO2: uartCo2_ProcessData(rxBuffer[localRX]); 451 {
333 break; 452 while((!isEndIndication(localRX)) || (moreData))
334 #endif 453 {
335 #ifdef ENABLE_SENTINEL_MODE 454 moreData = 0;
336 case SENSOR_SENTINEL: uartSentinel_ProcessData(rxBuffer[localRX]); 455 switch (sensorType)
337 break; 456 {
338 #endif 457 case SENSOR_MUX:
339 default: 458 case SENSOR_DIGO2: uartO2_ProcessData(rxBuffer[localRX]);
340 break; 459 break;
341 } 460 #ifdef ENABLE_CO2_SUPPORT
342 461 case SENSOR_CO2: uartCo2_ProcessData(rxBuffer[localRX]);
343 rxBuffer[localRX] = BUFFER_NODATA; 462 break;
344 localRX++; 463 #endif
345 rxReadIndex++; 464 #ifdef ENABLE_GNSS_SUPPORT
465 case SENSOR_GNSS: uartGnss_ProcessData(rxBuffer[localRX]);
466 break;
467 #endif
468 #ifdef ENABLE_SENTINEL_MODE
469 case SENSOR_SENTINEL: uartSentinel_ProcessData(rxBuffer[localRX]);
470 break;
471 #endif
472 default:
473 break;
474 }
475 if(localRX % 2)
476 {
477 rxBuffer[localRX] = BUFFER_NODATA_HIGH;
478 }
479 else
480 {
481 rxBuffer[localRX] = BUFFER_NODATA_LOW;
482 }
483
484 localRX++;
485 rxReadIndex++;
486 if(rxReadIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
487 {
488 localRX = 0;
489 rxReadIndex = 0;
490 }
491 futureIndex++;
492 if(futureIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
493 {
494 futureIndex = 0;
495 }
496 }
497 if(!isEndIndication(futureIndex))
498 {
499 moreData = 1;
500 }
501 } while(moreData);
502 }
503
504 void UART_WriteData(void)
505 {
506 if(huart1.hdmatx->State == HAL_DMA_STATE_READY)
507 {
508 huart1.gState = HAL_UART_STATE_READY;
509 dmaTxActive = 0;
510 }
511 }
512
513 void UART_FlushRxBuffer(void)
514 {
515 uint8_t futureIndex = rxReadIndex + 1;
516
517 if(futureIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
518 {
519 futureIndex = 0;
520 }
521 while((rxBuffer[rxReadIndex] != BUFFER_NODATA_LOW) && (rxBuffer[futureIndex] != BUFFER_NODATA_HIGH))
522 {
523 if(rxReadIndex % 2)
524 {
525 rxBuffer[rxReadIndex++] = BUFFER_NODATA_HIGH;
526 }
527 else
528 {
529 rxBuffer[rxReadIndex++] = BUFFER_NODATA_LOW;
530 }
346 if(rxReadIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER) 531 if(rxReadIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
347 { 532 {
348 localRX = 0;
349 rxReadIndex = 0; 533 rxReadIndex = 0;
350 } 534 }
351 } 535 futureIndex++;
352 } 536 if(futureIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
353 537 {
354 void UART_FlushRxBuffer(void) 538 futureIndex = 0;
355 {
356 while(rxBuffer[rxReadIndex] != BUFFER_NODATA)
357 {
358 rxBuffer[rxReadIndex] = BUFFER_NODATA;
359 rxReadIndex++;
360 if(rxReadIndex >= CHUNK_SIZE * CHUNKS_PER_BUFFER)
361 {
362 rxReadIndex = 0;
363 } 539 }
364 } 540 }
365 } 541 }
366 542
367 uint8_t UART_isComActive(uint8_t sensorId) 543 uint8_t UART_isComActive(uint8_t sensorId)
368 { 544 {
369 uint8_t active = 1; 545 uint8_t active = 1;
370 546
371 uint8_t ComState = externalInterface_GetSensorState(sensorId + EXT_INTERFACE_MUX_OFFSET); 547 uint8_t ComState = externalInterface_GetSensorState(sensorId + EXT_INTERFACE_MUX_OFFSET);
372 548
373 if((ComState == UART_COMMON_INIT) || (ComState == UART_COMMON_IDLE) || (ComState == UART_COMMON_ERROR)) 549 if((ComState == UART_COMMON_INIT) || (ComState == UART_COMMON_IDLE) || (ComState == UART_COMMON_ERROR) || (ComState == COMMON_SENSOR_STATE_INVALID))
374 { 550 {
375 active = 0; 551 active = 0;
376 } 552 }
377 return active; 553 return active;
378 } 554 }