1 /*
2 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
3 * Copyright (c) 2006 Christian Walter <wolti@sil.at>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $
29 */
30
31 /* ----------------------- System includes ----------------------------------*/
32 #include "stdlib.h"
33 #include "string.h"
34
35 /* ----------------------- Platform includes --------------------------------*/
36 #include "port.h"
37
38 /* ----------------------- Modbus includes ----------------------------------*/
39 #include "mb_m.h"
40 #include "mbconfig.h"
41 #include "mbascii.h"
42 #include "mbframe.h"
43
44 #include "mbcrc.h"
45 #include "mbport.h"
46
47 #if MB_MASTER_ASCII_ENABLED > 0
48
49 /* ----------------------- Defines ------------------------------------------*/
50
51 /* ----------------------- Type definitions ---------------------------------*/
52 typedef enum
53 {
54 STATE_M_RX_INIT, /*!< Receiver is in initial state. */
55 STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
56 STATE_M_RX_RCV, /*!< Frame is beeing received. */
57 STATE_M_RX_WAIT_EOF, /*!< Wait for End of Frame. */
58 STATE_M_RX_ERROR, /*!< If the frame is invalid. */
59 } eMBMasterAsciiRcvState;
60
61 typedef enum
62 {
63 STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
64 STATE_M_TX_START, /*!< Starting transmission (':' sent). */
65 STATE_M_TX_DATA, /*!< Sending of data (Address, Data, LRC). */
66 STATE_M_TX_END, /*!< End of transmission. */
67 STATE_M_TX_NOTIFY, /*!< Notify sender that the frame has been sent. */
68 STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
69 } eMBMasterAsciiSndState;
70
71 typedef enum
72 {
73 BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */
74 BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */
75 } eMBBytePos;
76
77 /* ----------------------- Shared values -----------------------------------*/
78 /* These Modbus values are shared in ASCII mode*/
79 extern volatile UCHAR ucMasterRcvBuf[];
80 extern volatile UCHAR ucMasterSndBuf[];
81 extern volatile eMBMasterTimerMode eMasterCurTimerMode;
82
83 /* ----------------------- Static functions ---------------------------------*/
84 static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter );
85
86 static UCHAR prvucMBBIN2CHAR( UCHAR ucByte );
87
88 static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen );
89
90 /* ----------------------- Static variables ---------------------------------*/
91 static volatile eMBMasterAsciiSndState eSndState;
92 static volatile eMBMasterAsciiRcvState eRcvState;
93
94 static volatile UCHAR *ucMasterASCIIRcvBuf = ucMasterRcvBuf;
95 static volatile UCHAR *ucMasterASCIISndBuf = ucMasterSndBuf;
96
97 static volatile USHORT usMasterRcvBufferPos;
98 static volatile eMBBytePos eBytePos;
99
100 static volatile UCHAR *pucMasterSndBufferCur;
101 static volatile USHORT usMasterSndBufferCount;
102
103 static volatile UCHAR ucLRC;
104 static volatile UCHAR ucMBLFCharacter;
105
106 /* ----------------------- Start implementation -----------------------------*/
107 eMBErrorCode
eMBMasterASCIIInit(UCHAR ucPort,ULONG ulBaudRate,eMBParity eParity)108 eMBMasterASCIIInit( UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
109 {
110 eMBErrorCode eStatus = MB_ENOERR;
111
112 ENTER_CRITICAL_SECTION( );
113 ucMBLFCharacter = MB_ASCII_DEFAULT_LF;
114
115 if( xMBMasterPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE )
116 {
117 eStatus = MB_EPORTERR;
118 }
119 else if( xMBMasterPortTimersInit( MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS ) != TRUE )
120 {
121 eStatus = MB_EPORTERR;
122 }
123
124 EXIT_CRITICAL_SECTION( );
125
126 return eStatus;
127 }
128
129 void
eMBMasterASCIIStart(void)130 eMBMasterASCIIStart( void )
131 {
132 ENTER_CRITICAL_SECTION( );
133 eRcvState = STATE_M_RX_IDLE;
134 vMBMasterPortSerialEnable( TRUE, FALSE );
135 vMBMasterPortTimersT35Enable( );
136 EXIT_CRITICAL_SECTION( );
137 }
138
139 void
eMBMasterASCIIStop(void)140 eMBMasterASCIIStop( void )
141 {
142 ENTER_CRITICAL_SECTION( );
143 vMBMasterPortSerialEnable( FALSE, FALSE );
144 vMBMasterPortTimersDisable( );
145 EXIT_CRITICAL_SECTION( );
146 }
147
148 eMBErrorCode
eMBMasterASCIIReceive(UCHAR * pucRcvAddress,UCHAR ** pucFrame,USHORT * pusLength)149 eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
150 {
151 eMBErrorCode eStatus = MB_ENOERR;
152
153 ENTER_CRITICAL_SECTION( );
154 assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX );
155
156 /* Length and CRC check */
157 if( ( usMasterRcvBufferPos >= MB_ASCII_SER_PDU_SIZE_MIN )
158 && ( prvucMBLRC( ( UCHAR * ) ucMasterASCIIRcvBuf, usMasterRcvBufferPos ) == 0 ) )
159 {
160 /* Save the address field. All frames are passed to the upper layed
161 * and the decision if a frame is used is done there.
162 */
163 *pucRcvAddress = ucMasterASCIIRcvBuf[MB_SER_PDU_ADDR_OFF];
164
165 /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
166 * size of address field and CRC checksum.
167 */
168 *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );
169
170 /* Return the start of the Modbus PDU to the caller. */
171 *pucFrame = ( UCHAR * ) & ucMasterASCIIRcvBuf[MB_SER_PDU_PDU_OFF];
172 }
173 else
174 {
175 eStatus = MB_EIO;
176 }
177 EXIT_CRITICAL_SECTION( );
178 return eStatus;
179 }
180
181 eMBErrorCode
eMBMasterASCIISend(UCHAR ucSlaveAddress,const UCHAR * pucFrame,USHORT usLength)182 eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
183 {
184 eMBErrorCode eStatus = MB_ENOERR;
185 UCHAR usLRC;
186
187 if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
188
189 ENTER_CRITICAL_SECTION( );
190 /* Check if the receiver is still in idle state. If not we where too
191 * slow with processing the received frame and the master sent another
192 * frame on the network. We have to abort sending the frame.
193 */
194 if(eRcvState == STATE_M_RX_IDLE)
195 {
196 /* First byte before the Modbus-PDU is the slave address. */
197 pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
198 usMasterSndBufferCount = 1;
199
200 /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
201 pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
202 usMasterSndBufferCount += usLength;
203
204 /* Calculate LRC checksum for Modbus-Serial-Line-PDU. */
205 usLRC = prvucMBLRC( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
206 ucMasterASCIISndBuf[usMasterSndBufferCount++] = usLRC;
207
208 /* Activate the transmitter. */
209 eSndState = STATE_M_TX_START;
210 vMBMasterPortSerialEnable( FALSE, TRUE );
211 }
212 else
213 {
214 eStatus = MB_EIO;
215 }
216 EXIT_CRITICAL_SECTION( );
217 return eStatus;
218 }
219
220 BOOL
xMBMasterASCIIReceiveFSM(void)221 xMBMasterASCIIReceiveFSM( void )
222 {
223 BOOL xNeedPoll = FALSE;
224 UCHAR ucByte;
225 UCHAR ucResult;
226
227 assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
228
229 /* Always read the character. */
230 xNeedPoll = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
231
232 switch ( eRcvState )
233 {
234 /* If we have received a character in the init state we have to
235 * wait until the frame is finished.
236 */
237 case STATE_M_RX_INIT:
238 vMBMasterPortTimersT35Enable( );
239 break;
240
241 /* In the error state we wait until all characters in the
242 * damaged frame are transmitted.
243 */
244 case STATE_M_RX_ERROR:
245 vMBMasterPortTimersRespondTimeoutEnable( );
246 break;
247
248 /* In the idle state we wait for a new character. If a character
249 * is received the t1.5 and t3.5 timers are started and the
250 * receiver is in the state STATE_RX_RECEIVE and disable early
251 * the timer of respond timeout .
252 */
253 case STATE_M_RX_IDLE:
254 /* Waiting for the start of frame character during respond timeout */
255 vMBMasterPortTimersRespondTimeoutEnable( );
256 if( ucByte == ':' )
257 {
258 /* Reset the input buffers to store the frame in receive state. */
259 usMasterRcvBufferPos = 0;
260 eBytePos = BYTE_HIGH_NIBBLE;
261 eRcvState = STATE_M_RX_RCV;
262 }
263 eSndState = STATE_M_TX_IDLE;
264 break;
265
266 /* A new character is received. If the character is a ':' the input
267 * buffer is cleared. A CR-character signals the end of the data
268 * block. Other characters are part of the data block and their
269 * ASCII value is converted back to a binary representation.
270 */
271 case STATE_M_RX_RCV:
272 /* Enable timer timeout. */
273 vMBMasterPortTimersT35Enable( );
274 if( ucByte == ':' )
275 {
276 /* Empty receive buffer. */
277 eBytePos = BYTE_HIGH_NIBBLE;
278 usMasterRcvBufferPos = 0;
279 }
280 else if( ucByte == MB_ASCII_DEFAULT_CR )
281 {
282 eRcvState = STATE_M_RX_WAIT_EOF;
283 }
284 else
285 {
286 ucResult = prvucMBCHAR2BIN( ucByte );
287 switch ( eBytePos )
288 {
289 /* High nibble of the byte comes first. We check for
290 * a buffer overflow here. */
291 case BYTE_HIGH_NIBBLE:
292 if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
293 {
294 ucMasterASCIIRcvBuf[usMasterRcvBufferPos] = ( UCHAR )( ucResult << 4 );
295 eBytePos = BYTE_LOW_NIBBLE;
296 break;
297 }
298 else
299 {
300 /* not handled in Modbus specification but seems
301 * a resonable implementation. */
302 eRcvState = STATE_M_RX_ERROR;
303 /* Disable previously activated timer because of error state. */
304 vMBPortTimersDisable( );
305 }
306 break;
307
308 case BYTE_LOW_NIBBLE:
309 ucMasterASCIIRcvBuf[usMasterRcvBufferPos] |= ucResult;
310 usMasterRcvBufferPos++;
311 eBytePos = BYTE_HIGH_NIBBLE;
312 break;
313 }
314 }
315 break;
316
317 case STATE_M_RX_WAIT_EOF:
318 if( ucByte == ucMBLFCharacter )
319 {
320 /* Disable character timeout timer because all characters are
321 * received. */
322 vMBMasterPortTimersDisable( );
323 /* Receiver is again in idle state. */
324 eRcvState = STATE_M_RX_IDLE;
325
326 /* Notify the caller of eMBMasterASCIIReceive that a new frame
327 * was received. */
328 (void)xMBMasterPortEventPost( EV_MASTER_FRAME_RECEIVED );
329 xNeedPoll = FALSE;
330 }
331 else if( ucByte == ':' )
332 {
333 /* Start of frame character received but last message is not completed.
334 * Empty receive buffer and back to receive state. */
335 eBytePos = BYTE_HIGH_NIBBLE;
336 usMasterRcvBufferPos = 0;
337 eRcvState = STATE_M_RX_IDLE;
338
339 /* Enable timer for respond timeout and wait for next frame. */
340 vMBMasterPortTimersRespondTimeoutEnable( );
341 }
342 else
343 {
344 /* Frame is not okay. Delete entire frame. */
345 eRcvState = STATE_M_RX_IDLE;
346 }
347 break;
348 }
349
350 return xNeedPoll;
351 }
352
353 BOOL
xMBMasterASCIITransmitFSM(void)354 xMBMasterASCIITransmitFSM( void )
355 {
356 BOOL xNeedPoll = TRUE;
357 UCHAR ucByte;
358 BOOL xFrameIsBroadcast = FALSE;
359
360 assert( eRcvState == STATE_M_RX_IDLE );
361
362 switch ( eSndState )
363 {
364 /* We should not get a transmitter event if the transmitter is in
365 * idle state. */
366 case STATE_M_TX_XFWR:
367 break;
368
369 /* We should not get a transmitter event if the transmitter is in
370 * idle state. */
371 case STATE_M_TX_IDLE:
372 break;
373
374 /* Start of transmission. The start of a frame is defined by sending
375 * the character ':'. */
376 case STATE_M_TX_START:
377 ucByte = ':';
378 xMBMasterPortSerialPutByte( ( CHAR )ucByte );
379 eSndState = STATE_M_TX_DATA;
380 eBytePos = BYTE_HIGH_NIBBLE;
381 break;
382
383 /* Send the data block. Each data byte is encoded as a character hex
384 * stream with the high nibble sent first and the low nibble sent
385 * last. If all data bytes are exhausted we send a '\r' character
386 * to end the transmission. */
387 case STATE_M_TX_DATA:
388 if( usMasterSndBufferCount > 0 )
389 {
390 switch ( eBytePos )
391 {
392 case BYTE_HIGH_NIBBLE:
393 ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur >> 4 ) );
394 xMBMasterPortSerialPutByte( ( CHAR ) ucByte );
395 eBytePos = BYTE_LOW_NIBBLE;
396 break;
397
398 case BYTE_LOW_NIBBLE:
399 ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur & 0x0F ) );
400 xMBMasterPortSerialPutByte( ( CHAR )ucByte );
401 pucMasterSndBufferCur++;
402 eBytePos = BYTE_HIGH_NIBBLE;
403 usMasterSndBufferCount--;
404 break;
405 }
406 }
407 else
408 {
409 xMBMasterPortSerialPutByte( MB_ASCII_DEFAULT_CR );
410 eSndState = STATE_M_TX_END;
411 }
412 break;
413
414 /* Finish the frame by sending a LF character. */
415 case STATE_M_TX_END:
416 xMBMasterPortSerialPutByte( ( CHAR )ucMBLFCharacter );
417 /* We need another state to make sure that the CR character has
418 * been sent. */
419 eSndState = STATE_M_TX_NOTIFY;
420 break;
421
422 /* Notify the task which called eMBMasterASCIISend that the frame has
423 * been sent. */
424 case STATE_M_TX_NOTIFY:
425 xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
426 vMBMasterRequestSetType( xFrameIsBroadcast );
427 eSndState = STATE_M_TX_XFWR;
428 /* If the frame is broadcast ,master will enable timer of convert delay,
429 * else master will enable timer of respond timeout. */
430 if ( xFrameIsBroadcast == TRUE )
431 {
432 vMBMasterPortTimersConvertDelayEnable( );
433 }
434 else
435 {
436 vMBMasterPortTimersRespondTimeoutEnable( );
437 }
438 xNeedPoll = FALSE;
439 break;
440 }
441
442 return xNeedPoll;
443 }
444
445 BOOL
xMBMasterASCIITimerT1SExpired(void)446 xMBMasterASCIITimerT1SExpired( void )
447 {
448 BOOL xNeedPoll = FALSE;
449
450 switch ( eRcvState )
451 {
452 /* Timer t35 expired. Startup phase is finished. */
453 case STATE_M_RX_INIT:
454 xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
455 ESP_EARLY_LOGI("xMBMasterASCIITimerT1SExpired", "RX_INIT_EXPIRED");
456 break;
457
458 /* Start of message is not received during respond timeout.
459 * Process error. */
460 case STATE_M_RX_IDLE:
461 eRcvState = STATE_M_RX_ERROR;
462 break;
463
464 /* A recieve timeout expired and no any new character received.
465 * Wait for respond time and go to error state to inform listener about error */
466 case STATE_M_RX_RCV:
467 eRcvState = STATE_M_RX_ERROR;
468 break;
469
470 /* An error occured while receiving the frame. */
471 case STATE_M_RX_ERROR:
472 vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
473 xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
474 break;
475
476 /* If we have a timeout we go back to the idle state and wait for
477 * the next frame.
478 */
479 case STATE_M_RX_WAIT_EOF:
480 eRcvState = STATE_M_RX_IDLE;
481 break;
482
483 default:
484 assert( 0 );
485 break;
486 }
487 eRcvState = STATE_M_RX_IDLE;
488
489 switch (eSndState)
490 {
491 /* A frame was send finish and convert delay or respond timeout expired.
492 * If the frame is broadcast,The master will idle,and if the frame is not
493 * broadcast.*/
494 case STATE_M_TX_XFWR:
495 if ( xMBMasterRequestIsBroadcast( ) == FALSE ) {
496 vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
497 xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
498 }
499 break;
500
501 /* Function called in an illegal state. */
502 default:
503 assert( ( eSndState == STATE_M_TX_START ) || ( eSndState == STATE_M_TX_IDLE )
504 || ( eSndState == STATE_M_TX_DATA ) || ( eSndState == STATE_M_TX_END )
505 || ( eSndState == STATE_M_TX_NOTIFY ) );
506 break;
507 }
508 eSndState = STATE_M_TX_IDLE;
509
510 vMBMasterPortTimersDisable( );
511 /* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
512 if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) {
513 xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE );
514 }
515
516 vMBMasterPortTimersDisable( );
517
518 /* no context switch required. */
519 return xNeedPoll;
520 }
521
522 static UCHAR
prvucMBCHAR2BIN(UCHAR ucCharacter)523 prvucMBCHAR2BIN( UCHAR ucCharacter )
524 {
525 if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
526 {
527 return ( UCHAR )( ucCharacter - '0' );
528 }
529 else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
530 {
531 return ( UCHAR )( ucCharacter - 'A' + 0x0A );
532 }
533 else
534 {
535 return 0xFF;
536 }
537 }
538
539 static UCHAR
prvucMBBIN2CHAR(UCHAR ucByte)540 prvucMBBIN2CHAR( UCHAR ucByte )
541 {
542 if( ucByte <= 0x09 )
543 {
544 return ( UCHAR )( '0' + ucByte );
545 }
546 else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
547 {
548 return ( UCHAR )( ucByte - 0x0A + 'A' );
549 }
550 else
551 {
552 /* Programming error. */
553 assert( 0 );
554 }
555 return '0';
556 }
557
558 static UCHAR
prvucMBLRC(UCHAR * pucFrame,USHORT usLen)559 prvucMBLRC( UCHAR * pucFrame, USHORT usLen )
560 {
561 UCHAR ucLRC = 0; /* LRC char initialized */
562
563 while( usLen-- )
564 {
565 ucLRC += *pucFrame++; /* Add buffer byte without carry */
566 }
567
568 /* Return twos complement */
569 ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );
570 return ucLRC;
571 }
572
573 #endif
574