source: trunk/fw_g473rct/SES/src/modbus.c@ 69

Last change on this file since 69 was 69, checked in by f.jahn, 4 weeks ago

Started implementing Modbus on USB CDC channel.

File size: 19.8 KB
Line 
1/*
2 * modbus.c
3 *
4 * Created: 03.09.2012 08:39:20
5 * Author: Falko
6 */
7
8
9
10#if MODBUS_SUPPORT == TRUE
11
12 #include "modbus.h"
13 //#include "stm32g4xx_hal.h"
14 #include "main.h"
15 //#include "stm32g0xx_hal_tim.h"
16 //#include "stm32_hal_legacy.h"
17 #include <stdio.h>
18 // ---------------------------------------------------------
19 // -------------------- MODUL DEFINES ----------------------
20 // ---------------------------------------------------------
21
22 /* Protocol exceptions */
23 #define ILLEGAL_FUNCTION 0x01
24 #define ILLEGAL_DATA_ADDRESS 0x02
25 #define ILLEGAL_DATA_VALUE 0x03
26 #define SLAVE_DEVICE_FAILURE 0x04
27 #define SERVER_FAILURE 0x04
28 #define ACKNOWLEDGE 0x05
29 #define SLAVE_DEVICE_BUSY 0x06
30 #define SERVER_BUSY 0x06
31 #define NEGATIVE_ACKNOWLEDGE 0x07
32 #define MEMORY_PARITY_ERROR 0x08
33 #define GATEWAY_PROBLEM_PATH 0x0A
34 #define GATEWAY_PROBLEM_TARGET 0x0B
35
36 /* Local Error codes */
37 #define INVALID_CRC -1
38
39 // Position der Daten im Rx String
40 #define OFFSET_SLAVE_ADRESS 0x00
41 #define OFFSET_FUNCTION_CODE 0x01
42 #define OFFSET_START_ADRESS_HI 0x02
43 #define OFFSET_START_ADRESS_LO 0x03
44 #define OFFSET_NO_OF_REGISTERS_HI 0x04
45 #define OFFSET_NO_OF_REGISTERS_LO 0x05
46
47 #define MIN_NUMBER_OF_REGISTERS_FC3 0x01
48 #define MAX_NUMBER_OF_REGISTERS_FC3 0x7D
49 #define MIN_NUMBER_OF_REGISTERS_FC16 0x01
50 #define MAX_NUMBER_OF_REGISTERS_FC16 0x7B
51
52 #ifdef DEBUG
53 #define RESPONSE_TIMEOUT 300 // * 1ms
54 #else
55 #define RESPONSE_TIMEOUT 1000 // * 1ms
56 #endif
57
58 #define FAST_BAUDRATE_INTERFRAME_DELAY_us (1750UL)
59 // --- Externe Variablen --------------------------------------------
60 extern modbus_t modbusData;
61 extern sys_data_t sys_data;
62
63
64 // --- Private Funktions Prototypen --------------------------------------------
65
66 void mbUartInit (modbus_t * mb_data,UART_HandleTypeDef * usart, uint32_t baudrate, uint32_t parityMode, uint32_t stopBits , uint32_t nrOfBitsPerChar);
67 uint16_t mbCrc16 (uint8_t *buf, uint32_t len);
68 void mbSend (modbus_t * mb_data );
69 uint32_t mbSlaveReadHoldingRegisters (uint8_t * response_string, uint8_t *msg, uint32_t tx_position, uint8_t deviceID);
70 uint32_t mbSlaveWriteMultipleRegisters (uint8_t * response_string, uint8_t *msg, uint32_t tx_position, uint32_t deviceID);
71 uint32_t mbSlaveWriteSingleRegister (uint8_t * response_string,uint8_t *msg,uint32_t tx_position, uint32_t deviceID);
72 uint32_t mbSlaveResponseException (uint8_t* response_string, uint32_t function_code, uint32_t exception_code,uint32_t tx_position) ;
73static HAL_StatusTypeDef RS485_ModbusEx_Init (UART_HandleTypeDef *huart, uint32_t Polarity, uint32_t AssertionTime, uint32_t DeassertionTime, uint32_t charReceiveTimeout);
74static void UART_TxISR_8BIT (UART_HandleTypeDef *huart);
75
76 // --- GEMEINSAME MODBUS FUNKTIONEN --------------------------------------------
77 // Diese Funktionen werden sowohl von Modbus Master als auch Modbus Slave verwendet
78
79 /*
80 *
81 * @brief Diese Funktion Initialisert die Modbus Datenstrukturen und die Hardware
82 *
83 * Das Modbus Modul bentigt einen UART und einen Timer pro Modbus Anschluss
84 * Die Funktion erfordert eine vorhandene Callback funktion namens HAL_UART_MspInit
85 * In dieser muss:
86 * - Der UART CLK eingeschaltet werden
87 * - Die Pins initialisert werden (Alternate Port Funktion)
88 * - Der NVIC Interrupt eingeschaltet werden
89 * @param mb_data : Datenstruktur zur Aufnahme aller Daten
90 * @param baudrate : Bautrate
91 * @param parityMode : Parity, mglich ist UART_PARITY_ODD, UART_PARITY_EVEN, UART_PARITY_NONE. Default ist lt. Modbus Standart EVEN
92 * @param usart : Timer Modul, z.B. USART1
93 * @retval None
94 */
95 void mbInit(modbus_t* mb_data, uint32_t baudrate, uint32_t parityMode, uint16_t stopBits, UART_HandleTypeDef* usart)
96 {
97 uint32_t numberOfBitsPerChar;
98 //uint32_t stopBits;
99
100 if (stopBits < 1U || stopBits > 2U) stopBits = 1U;
101
102
103 // Berechne Anzahl der Bits per Char
104 numberOfBitsPerChar = NUMBER_OF_STARTBITS + NUMBER_OF_DATABITS + stopBits;
105 if ((parityMode == MODBUS_UART_PARITY_EVEN) || (parityMode == MODBUS_UART_PARITY_ODD))
106 {
107 numberOfBitsPerChar +=1;
108 }
109
110 mbUartInit(mb_data,usart, baudrate, parityMode, stopBits, numberOfBitsPerChar);
111
112 // Datenstrukturen zurcksetzen
113 mb_data->last_query_function_code = 0;
114 mb_data->last_query_tcp_id.w = 0;
115 mb_data->last_query_number_of_register.w = 0;
116 mb_data->current_query = MB_QUERY_NOTHING;
117 mb_data->last_query_slave_adress = 0;
118 mb_data->last_query_start_adress.w = 0;
119 mb_data->last_query_timeout = false;
120 }
121
122 /*
123 *
124 * @brief Diese Funktion Initialisert die Modbus UART Hardware
125 *
126 * @param mb_data : Datenstruktur zur Aufnahme aller Daten
127 * @param usart : UART Modul, z.B. USART1
128 * @param baudrate : UART BAUD
129 * @param parityMmode : Parity, mglich ist:
130 UART_PARITY_ODD, UART_PARITY_EVEN, UART_PARITY_NONE.
131 Default ist lt. Modbus Standart EVEN
132 * @param stopBits : Anzahl der Stop Bits, lt Modbus Standart
133 * 2 Stop Bits bei Parity None, ansonsten 2 Stop Bits
134 * @retval None
135 */
136 void mbUartInit(modbus_t* mb_data,UART_HandleTypeDef* usart, uint32_t baudrate, uint32_t parityMode, uint32_t stopBits, uint32_t nrOfBitsPerChar)
137 {
138 //--- Uart Init ------------------------------------------------------------
139 mb_data->uart = usart;
140
141 // Baudrate
142 mb_data->uart->Init.BaudRate = baudrate;
143
144 // Parity Mode // Word length
145 if(parityMode == MODBUS_UART_PARITY_EVEN)
146 {
147 mb_data->uart->Init.Parity = UART_PARITY_EVEN;
148 mb_data->uart->Init.WordLength = UART_WORDLENGTH_9B;
149 }
150 else if(parityMode == MODBUS_UART_PARITY_ODD)
151 {
152 mb_data->uart->Init.Parity = UART_PARITY_ODD;
153 mb_data->uart->Init.WordLength = UART_WORDLENGTH_9B;
154 }
155 else
156 {
157 mb_data->uart->Init.Parity = UART_PARITY_NONE;
158 mb_data->uart->Init.WordLength = UART_WORDLENGTH_8B;
159 }
160
161 // Stopbits
162 if (stopBits == 1)
163 {
164 mb_data->uart->Init.StopBits = UART_STOPBITS_1;
165 }
166 else
167 {
168 mb_data->uart->Init.StopBits = UART_STOPBITS_2;
169 }
170
171 if (HAL_UART_Init(mb_data->uart) != HAL_OK)
172 {
173 printf("ERROR Uart INIT\r\n");
174 Error_Handler();
175 }
176
177
178 HAL_UART_EnableReceiverTimeout( usart);
179 if (baudrate <= 19200)
180 {
181 HAL_UART_ReceiverTimeout_Config(usart, 3.5 * nrOfBitsPerChar);
182 }
183 else
184 {
185 uint32_t fixedDelayInBitDurations = (FAST_BAUDRATE_INTERFRAME_DELAY_us * baudrate) / 1000000UL + 1UL;
186 HAL_UART_ReceiverTimeout_Config(usart, fixedDelayInBitDurations);
187 }
188
189
190 SET_BIT(usart->Instance->CR1, USART_CR1_RTOIE);
191
192
193 if(HAL_UART_Receive_DMA(mb_data->uart, mb_data->rx_buffer, RXBUFFERSIZE) != HAL_OK)
194 {
195 printf("uart error \n\r");
196 while(1)
197 {
198 }
199 }
200 }
201
202
203
204void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
205{
206 modbusData.mb_rx_frame_complete = 1;
207 modbusData.setRxLed = false;
208 modbusData.rx_head = huart->RxXferSize - __HAL_DMA_GET_COUNTER(huart->hdmarx);
209
210
211 if (huart->ErrorCode == HAL_UART_ERROR_RTO)
212 {
213
214 // printf("MB RTO Event! \n\r");
215 // Kein Fehler, normale Funktion
216 }
217 if (huart->ErrorCode == HAL_UART_ERROR_FE)
218 {
219 printf("MB FE Error! \n\r");
220 }
221
222 if (huart->ErrorCode == HAL_UART_ERROR_PE)
223 {
224 printf("MB PE Error! \n\r");
225 }
226
227 if (huart->ErrorCode == HAL_UART_ERROR_NE)
228 {
229 printf("MB NE Error! \n\r");
230 }
231
232 if (huart->ErrorCode == HAL_UART_ERROR_DMA)
233 {
234 printf("MB DMA Error! \n\r");
235 }
236
237 if (huart->ErrorCode == HAL_UART_ERROR_DMA)
238 {
239 printf("MB DMA Error! \n\r");
240 }
241
242 if (huart->ErrorCode == HAL_UART_ERROR_ORE)
243 {
244 printf("MB ORE Error! \n\r");
245 }
246
247
248
249 if(HAL_UART_Receive_DMA(huart, huart->pRxBuffPtr, RXBUFFERSIZE) != HAL_OK)
250 {
251 printf("Uart Error bei neustart nach Fehler \n\r");
252 // while(1)
253 // {
254 // }
255 }
256
257}
258
259//void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
260//{
261// //printf("MB rxEvent!RX=%d \n\r",Size);
262// modbusData.setRxLed = true;
263
264// modbusData.mb_rx_frame_complete = 1;
265// modbusData.rx_head= Size +1;
266
267// if(HAL_UART_Receive_DMA(huart, huart->pRxBuffPtr, RXBUFFERSIZE) != HAL_OK)
268// {
269// printf("uart error \n\r");
270// // while(1)
271// // {
272// // }
273// }
274
275
276//}
277
278void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
279{
280 //printf("uart complete \n\r");
281 modbusData.current_query = MB_QUERY_NOTHING;
282
283}
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 void mbSend(modbus_t * mb_data )
306 {
307 mb_data->current_query = MB_QUERY_SEND_DATA;
308 HAL_UART_Transmit_DMA(mb_data->uart, mb_data->tx_buffer, mb_data->tx_head);
309 }
310
311 void mbClearTxBuffer(modbus_t * mb_data)
312 {
313 mb_data->tx_head = 0;
314 }
315
316
317
318 // Compute the MODBUS RTU CRC
319 uint16_t mbCrc16(uint8_t* buf, uint32_t len)
320 {
321 uint16_t crc = 0xFFFF;
322
323 for (uint32_t pos = 0; pos < len; pos++)
324 {
325 crc ^= (uint16_t)buf[pos]; // XOR byte into least sig. byte of crc
326
327 for (int i = 8; i != 0; i--)
328 { // Loop over each bit
329 if ((crc & 0x0001) != 0)
330 { // If the LSB is set
331 crc >>= 1; // Shift right and XOR 0xA001
332 crc ^= 0xA001;
333 }
334 else // Else LSB is not set
335 {
336 crc >>= 1; // Just shift right
337 }
338 }
339 }
340
341 // Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)
342 return crc;
343 }
344
345 /* If CRC is correct returns msg_length else returns INVALID_CRC */
346 int mbCheckCrc16( uint8_t *msg, const int msg_length)
347 {
348 int ret;
349 uint16_t crc_calc;
350 uint16_t crc_received;
351
352 crc_calc = mbCrc16(msg, msg_length - 2);
353 crc_received = (msg[msg_length - 1] << 8) | msg[msg_length - 2];
354
355 // Check CRC of msg
356 if (crc_calc == crc_received) {
357 ret = msg_length;
358 } else {
359 ret = INVALID_CRC;
360 }
361 return ret;
362 }
363
364 uint32_t mbAppendCrc16(uint8_t * buffer, uint32_t tx_position)
365 {
366 uint16_t crc = mbCrc16( buffer , tx_position);
367
368 uint8_t l_crc = (uint8_t) (crc & 0x00FF) ;
369 uint8_t h_crc = (uint8_t) (crc >> 8);
370 buffer[tx_position] = l_crc;
371 tx_position++;
372 buffer[tx_position] = h_crc;
373 tx_position++;
374 return tx_position;
375 }
376
377 /************************************************************************************************************
378 Function: mb_get_frame_complete
379 Purpose: Rckabe ob Frame komplett empfangen wurde
380 *************************************************************************************************************/
381 bool mbGetFrameComplete(modbus_t * mb_data)
382 {
383 return mb_data->mb_rx_frame_complete;
384 }
385
386 void mbClearRxFrame(modbus_t * mb_data)
387 {
388 // Wieder bei 0 im buffer anfangen
389 mb_data->rx_head = 0;
390
391 // keine Daten mehr vorhanden
392 mb_data->mb_rx_frame_complete=false;
393 }
394
395
396 // --------------------- SLAVE FUNCTIONS ---------------------------------------
397
398#define SEND_TO_SLAVES_BUFFER_COUNT 1000
399//static TASK_MODBUS_MASTER_Message_t xMessage[255];
400//static TASK_MODBUS_MASTER_Message_t *pxMessage;
401static bword_t values[SEND_TO_SLAVES_BUFFER_COUNT];
402static uint32_t y;
403static uint32_t z;
404
405 uint32_t mbSlaveCheckModbusRtuQuery(modbus_t * mb_data)
406 {
407 uint32_t message_lengh;
408 uint8_t *modbus_rx_message;
409 modbus_rx_message = mb_data->rx_buffer;
410 message_lengh= mb_data->rx_head;
411 uint32_t slave_adress;
412 slave_adress = modbus_rx_message[0];
413
414 if (message_lengh < 5) //Mindestens 5 Zeichen (Slave Adress + Function Code + 2x CRC
415 {
416 mbClearRxFrame(mb_data);
417 return 0;
418 }
419
420 // Prfe CRC
421 if (mbCheckCrc16(modbus_rx_message,message_lengh) == INVALID_CRC)
422 {
423 mbClearRxFrame(mb_data);
424 return 0;
425 }
426
427 if (slave_adress == MODBUS_BROADCAST_ADDRESS)
428 {
429
430 return RESPOND_TO_QUERY;
431 }
432 /* auf richtige Slave Adresse checken ansonsten nicht antworten*/
433 else if (slave_adress == sys_data.s.parameter.slave_address)
434 {
435 return RESPOND_TO_QUERY;
436 }
437
438 mbClearRxFrame(mb_data);
439 return 0;
440 }
441
442 void mbSlaveProcessRtuQuery(modbus_t* mb_data)
443 {
444 uint32_t tx_position=0; //die _Nächste_ Position in der Zeichen eingefügt werden müssen
445 uint8_t *modbus_rx_message;
446 modbus_rx_message = &mb_data->rx_buffer[0];
447
448 //Vorbereiten auf neues senden
449 mbClearTxBuffer(mb_data);
450
451 //mb_data->tx_buffer[0] = sys_data.s.vmGreenview.s.lb_slave_adress;
452 mb_data->tx_buffer[0] = *modbus_rx_message;
453 tx_position++;
454 tx_position = mbSlaveProcessPdu(mb_data->tx_buffer , modbus_rx_message,tx_position, *modbus_rx_message);
455
456 tx_position = mbAppendCrc16(mb_data->tx_buffer ,tx_position);
457 mb_data->tx_head=tx_position;
458 mbSend(mb_data);
459 mbClearRxFrame(mb_data);
460 }
461
462 uint32_t mbSlaveProcessPdu (uint8_t* response_string, uint8_t* msg, uint32_t tx_position, uint8_t deviceID)
463 {
464 uint32_t function_code;
465 uint32_t ret;
466
467 function_code = msg[OFFSET_FUNCTION_CODE];
468
469 switch (function_code)
470 {
471 case FC_READ_HOLDING_REGISTERS:
472 ret= mbSlaveReadHoldingRegisters(response_string, msg,tx_position, deviceID);
473 break;
474
475 case FC_WRITE_SINGLE_REGISTER:
476 ret = mbSlaveWriteSingleRegister(response_string, msg,tx_position, deviceID);
477 break;
478
479 case FC_WRITE_MULTIPLE_REGISTER:
480 ret=mbSlaveWriteMultipleRegisters(response_string, msg,tx_position, deviceID);
481 break;
482
483 default:
484 ret=mbSlaveResponseException(response_string,function_code,ILLEGAL_FUNCTION,tx_position);
485 break;
486 }
487
488 return ret;
489 }
490
491
492 uint32_t mbSlaveReadHoldingRegisters(uint8_t* response_string, uint8_t* msg, uint32_t tx_position, uint8_t deviceID)
493 {
494 uint32_t start_adress;
495 uint32_t adress;
496 uint32_t number_of_registers;
497
498 /*stimmt die device ID mit der eigenen berein*/
499 if((deviceID != sys_data.s.parameter.slave_address) && (deviceID != 0))
500 {
501 return mbSlaveResponseException(response_string, FC_WRITE_SINGLE_REGISTER, GATEWAY_PROBLEM_TARGET,tx_position);
502 }
503
504 start_adress = (msg[OFFSET_START_ADRESS_HI] << 8) + msg[OFFSET_START_ADRESS_LO];
505 number_of_registers = ( msg[OFFSET_NO_OF_REGISTERS_HI] << 8) + msg[OFFSET_NO_OF_REGISTERS_LO];
506
507 if ((number_of_registers < MIN_NUMBER_OF_REGISTERS_FC3) || (number_of_registers > MAX_NUMBER_OF_REGISTERS_FC3) )
508 {
509 return mbSlaveResponseException(response_string,FC_READ_HOLDING_REGISTERS,ILLEGAL_DATA_VALUE,tx_position);
510 }
511
512 if (start_adress+number_of_registers-1 > MAX_ADRESS)
513 {
514 return mbSlaveResponseException(response_string, FC_READ_HOLDING_REGISTERS,ILLEGAL_DATA_ADDRESS,tx_position);
515 }
516
517 response_string[tx_position] = FC_READ_HOLDING_REGISTERS; // FUNCTION CODE
518 tx_position++;
519 response_string[tx_position] = number_of_registers * 2; // Bytes
520 tx_position++;
521
522 for(adress=start_adress;adress < (start_adress + number_of_registers);adress++)
523 {
524 /*Daten aus dem Speicher senden*/
525 response_string[tx_position] = sys_data.mb[adress].b[1];
526 tx_position++;
527 response_string[tx_position] = sys_data.mb[adress].b[0];
528 tx_position++;
529 }
530
531 return tx_position;
532 }
533
534
535 uint32_t mbSlaveWriteMultipleRegisters(uint8_t * response_string, uint8_t *msg, uint32_t tx_position, uint32_t deviceID)
536 {
537
538 uint32_t start_adress;
539 uint32_t number_of_registers;
540 uint32_t adress;
541 uint32_t offset;
542
543 /*stimmt die device ID mit der eigenen berein*/
544 if((deviceID != sys_data.s.parameter.slave_address) && (deviceID != 0))
545 {
546 return mbSlaveResponseException(response_string,FC_WRITE_SINGLE_REGISTER,GATEWAY_PROBLEM_TARGET,tx_position);
547 }
548
549 start_adress = (msg[OFFSET_START_ADRESS_HI] << 8) + msg[OFFSET_START_ADRESS_LO];
550 number_of_registers = ( msg[OFFSET_NO_OF_REGISTERS_HI] << 8) + msg[OFFSET_NO_OF_REGISTERS_LO];
551 offset=7;
552
553 if ((number_of_registers < MIN_NUMBER_OF_REGISTERS_FC16) || (number_of_registers > MAX_NUMBER_OF_REGISTERS_FC16) )
554 {
555 return mbSlaveResponseException(response_string, FC_WRITE_MULTIPLE_REGISTER,ILLEGAL_DATA_VALUE,tx_position);
556 }
557
558 if (start_adress+number_of_registers-1 > MAX_ADRESS)
559 {
560 return mbSlaveResponseException(response_string, FC_WRITE_MULTIPLE_REGISTER,ILLEGAL_DATA_ADDRESS,tx_position);
561 }
562
563 /*Daten in Gertespeicher schreiben*/
564 for(adress=start_adress;adress < (start_adress + number_of_registers);adress++)
565 {
566 sys_data.mb[adress].b[1] = msg[offset];
567 sys_data.mb[adress].b[0] = msg[offset+1];
568 offset+=2;
569 }
570
571 response_string[tx_position] = FC_WRITE_MULTIPLE_REGISTER; // FUNCTION CODE - 1 byte
572 tx_position++;
573 response_string[tx_position] = start_adress >> 8;
574 tx_position++;
575 response_string[tx_position] = (uint8_t ) ( start_adress & 0x00FF); // start adresse 2 byte
576 tx_position++;
577 response_string[tx_position] = number_of_registers >> 8;
578 tx_position++;
579 response_string[tx_position] = (uint8_t ) ( number_of_registers & 0x00FF); // Anzahl Register 2 byte
580 tx_position++;
581 return tx_position;
582 }
583
584
585 uint32_t mbSlaveWriteSingleRegister(uint8_t * response_string,uint8_t *msg,uint32_t tx_position, uint32_t deviceID)
586 {
587
588 uint32_t adress;
589
590 /*stimmt die device ID mit der eigenen berein*/
591 if((deviceID != sys_data.s.parameter.slave_address) && (deviceID != 0))
592 {
593 return mbSlaveResponseException(response_string,FC_WRITE_SINGLE_REGISTER,GATEWAY_PROBLEM_TARGET,tx_position);
594 }
595
596 adress = (msg[2] << 8) + msg[3];
597
598 if (adress > MAX_ADRESS)
599 {
600 return mbSlaveResponseException(response_string,FC_WRITE_SINGLE_REGISTER,ILLEGAL_DATA_ADDRESS,tx_position);
601 }
602
603 /*schreibe Daten in eigenen Speicher*/
604 sys_data.mb[adress].b[1] = msg[4];
605 sys_data.mb[adress].b[0] = msg[5];
606
607 response_string[tx_position]= FC_WRITE_SINGLE_REGISTER; // FUNCTION CODE
608 tx_position++;
609 response_string[tx_position]= adress >> 8;
610 tx_position++;
611 response_string[tx_position]= (uint8_t ) ( adress & 0x00FF);
612
613 tx_position++;
614 response_string[tx_position]= msg[4];
615 tx_position++;
616 response_string[tx_position]= msg[5];
617 tx_position++;
618
619 return tx_position;
620 }
621
622
623 uint32_t mbSlaveResponseException(uint8_t* response_string, uint32_t function_code, uint32_t exception_code,uint32_t tx_position )
624 {
625 function_code += 0x80;
626 response_string[tx_position] = function_code; // FUNCTION CODE
627 tx_position++;
628 response_string[tx_position] = exception_code; //
629 tx_position++;
630 return tx_position;
631 }
632
633
634 //---------------------------- UNKNOWN -----------------------------------------
635 //- -
636 //------------------------------------------------------------------------------
637
638#endif
639
Note: See TracBrowser for help on using the repository browser.