1 // $Header: /volume1/cvs/cvsroot/microrisc/iqrf/DPA/Examples/DP2Papp-UART.c,v 1.10 2022/02/25 09:41:25 hynek Exp $
    2 //############################################################################################
    3 // Example DP2P TR module application via UART using HDLC protocol (see DPA UART Interface documentation)
    4 //############################################################################################
    5 // Revision history:
    6 //   2022/02/24  Release for DPA 4.17
    7 //
    8 // *********************************************************************
    9 
   10 #include "../includes/IQRF.h"
   11 #include "Dpa.h"
   12 
   13 //############################################################################################
   14 // This example is derived from DP2Papp.c, which uses the SPI. Therefore many same structures are named *SPI* although UART is used.
   15 // When trying example below in IQRF IDE then connect it to the COM port using "DPA HDLC" protocol.
   16 //############################################################################################
   17 
   18 // DP2P special (fake PNUM and PCMD) DPA values to indicate that DP2P parameters are specified
   19 #define DP2P_PNUM   0xDD
   20 #define DP2P_PCMD   0xDD
   21 
   22 // UART baud rate
   23 #define BAUDRATE    115200
   24 
   25 typedef struct
   26 {
   27   // PNUM
   28   uns8  PNUM;
   29   // PCMD
   30   uns8  PCMD;
   31   // Zero
   32   uns8  _zero_;
   33   // Previous fields overlap Header part of the TDP2Prequest
   34   // Non-zero value when STD+LP network is used, otherwise STD
   35   uns8  STD_LP_network;
   36   // Channel to communicate DP2P packets
   37   uns8  RfChannel;
   38   // Power to send DP2P request
   39   uns8  ReqTxPower;
   40   // Filter to receive DP2P responses
   41   uns8  RespRxFilter;
   42   // Access Password to use for de/encryption
   43   uns8  AccessPassword[sizeof( _DpaMessage.PerOSSetSecurity_Request.Data )];
   44 } TSPI_DP2PrequestParams;
   45 
   46 // Incoming SPI DP2P packet request
   47 typedef union
   48 {
   49   // Used to send DP2P requests, then only PNUM+PCMD(+_zero_) fields from the next union field are used to specify DPA request PCMD+PNUN
   50 
   51   /* IQRF IDE Terminal example
   52    $07$03$00$3E$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$07$00$FF$FF
   53      PNUM = PNUM_LEDG = $07
   54      PCMD = CMD_LED_PULSE = $03
   55      Nodes #1-5 = $3E + 29 x $00
   56      SlotLength = 0x00 (default values)
   57      Response RF power = $07
   58      HWPID 0xFFFF = $FF$FF
   59   */
   60   TDP2Prequest DP2Prequest;
   61 
   62   // Used to specify PNUM and PCMD of the DP2P request or to setup DP2P parameters (when PNUM and PCMD equal DP2P_PNUM and DP2P_PCMD respectively)
   63 
   64   /* IQRF IDE Terminal example
   65     $DD$DD$00$01$23$07$05$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00
   66       STD+LP network = $01
   67       channel 35 = $23
   68       Request RF power 7 = $07
   69       RX filter 5 = $05
   70       AccessPassword = 16 x $00 (i.e. empty, not recommended to use!)
   71   */
   72   TSPI_DP2PrequestParams DP2PrequestParams;
   73 } TSPI_TDP2Prequest;
   74 
   75 // Outgoing SPI DP2P packet invite (fields must exactly overlap TDP2Invite)
   76 typedef struct
   77 {
   78   uns8  Header[3];  // 0x000001 (0x00 is used to detect TSPI_TDP2Pinvite, 0xFF specifies TSPI_TDP2Presponse - see below)
   79   uns8  NADR;
   80 } TSPI_TDP2Pinvite;
   81 
   82 // Outgoing SPI DP2P packet response
   83 typedef union
   84 {
   85   // Real DP2P response but PNUM and PCMD is overlapped, see the next union field
   86   TDP2Presponse DP2Presponse;
   87 
   88   struct
   89   {
   90     // PNUM
   91     uns8  PNUM;
   92     // PCMD
   93     uns8  PCMD;
   94     // 0xFF (remainder from the DP2P Response, used to detect Response)
   95     uns8 _0xFF_;
   96   } DP2PresponseParams;
   97 } TSPI_TDP2Presponse;
   98 
   99 // Packet size limits
  100 #define MinRxPacketSize    sizeof( TSPI_DP2PrequestParams )
  101 #define MaxRxPacketSize    sizeof( TSPI_TDP2Prequest )
  102 
  103 /*
  104 IQRF IDE terminal log (not HDLC encoded) example:
  105  <> Length Data (comment)
  106  Tx 23     DD.DD.00.01.23.07.05.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00. (Sets DP2P parameters, see details above)
  107  Tx 38     07.03.00.3E.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.07.00.FF.FF. (Nodes 1-5 will pulse the LEDG, see details above)
  108  Rx 4      01.00.00.01. (Invite from Node #1)
  109  Rx 4      07.83.FF.01. (Response from Node #1)
  110  Rx 4      01.00.00.02. (Invite from Node #2)
  111  Rx 4      07.83.FF.02. (Response from Node #2)
  112  Rx 4      01.00.00.03. (Invite from Node #3)
  113  Rx 4      07.83.FF.03. (Response from Node #3)
  114  Rx 4      01.00.00.04. (Invite from Node #4)
  115  Rx 4      07.83.FF.04. (Response from Node #4)
  116  Rx 4      01.00.00.06. (Invite from Node #5)
  117  Rx 4      07.83.FF.05. (Response from Node #5)
  118 
  119 The same UART level HDLC encoded communication :
  120  <> Length Data (comment)
  121  Tx 26     7E.DD.DD.00.01.23.07.05.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.D6.7E. (Sets DP2P parameters, see details above)
  122  Tx 41     7E.07.03.00.3E.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.07.00.FF.FF.D5.7E. (Nodes 1-5 will pulse the LEDG, see details above)
  123  Rx 7      7E.01.00.00.01.3A.7E. (Invite from Node #1)
  124  Rx 7      7E.07.83.FF.01.34.7E. (Response from Node #1)
  125  Rx 7      7E.01.00.00.02.D8.7E. (Invite from Node #2)
  126  Rx 7      7E.07.83.FF.02.D6.7E. (Response from Node #2)
  127  Rx 7      7E.01.00.00.03.86.7E. (Invite from Node #3)
  128  Rx 7      7E.07.83.FF.03.88.7E. (Response from Node #3)
  129  Rx 7      7E.01.00.00.04.05.7E. (Invite from Node #4)
  130  Rx 7      7E.07.83.FF.04.0B.7E. (Response from Node #4)
  131  Rx 7      7E.01.00.00.05.5B.7E. (Invite from Node #5)
  132  Rx 7      7E.07.83.FF.05.55.7E. (Response from Node #5)
  133 */
  134 
  135 //############################################################################################
  136 // UART & HDLC definitions
  137 
  138 // HDLC byte stuffing bytes
  139 // Flag Sequence
  140 #define   HDLC_FRM_FLAG_SEQUENCE    0x7e
  141 // Asynchronous Control Escape
  142 #define   HDLC_FRM_CONTROL_ESCAPE   0x7d
  143 // Asynchronous transparency modifier
  144 #define   HDLC_FRM_ESCAPE_BIT       0x20
  145 
  146 // UART TX states
  147 typedef enum
  148 {
  149   UART_TX_START = 0,
  150   UART_TX_DATA = 1,
  151   UART_TX_CHSUM = 2,
  152   UART_TX_STOP = 3
  153 } TUartTxState;
  154 
  155 // State of the UART TX machine
  156 uns8 UartTxState;
  157 // Length of the UART data to TX
  158 uns8 UartTxSize;
  159 
  160 // UART TX packet buffer
  161 #define TxUartBuffer        bufferAUX
  162 
  163 #define TxUARTbusy()        ( !TRMT )
  164 #define WaitTxUART()        while ( TxUARTbusy() )
  165 
  166 // UART RX states
  167 typedef enum
  168 {
  169   UART_RX_WAITHEAD = 0,
  170   UART_RX_STOP = 1,
  171   UART_RX_PACKET = 2,
  172   UART_RX_ESCAPE = 3,
  173 } TUartRxState;
  174 
  175 // State of the UART RX machine
  176 uns8 UartRxState; // = 0 == UART_RX_WAITHEAD
  177 // Size of the received packet from UART
  178 uns8 UartRxSize;  // = 0
  179 
  180 // UART RX packet buffer
  181 #define RxUartBuffer        bufferCOM
  182 
  183 #define RxUARTstart()       do { UartRxState = UART_RX_WAITHEAD; } while(0)
  184 #define RxUARTready()       ( UartRxState == UART_RX_STOP && UartRxSize != 0 )
  185 
  186 void OpenUART();
  187 void TxUART( uns8 dataLength );
  188 
  189 // Division macro with rounding
  190 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor)))
  191 // Macro to compute UART register value from baud rate
  192 #define UART_SPBRG_VALUE( Baud )  ( DIV( F_OSC, ( ( ( uns24 )4  ) * ( Baud ) ) ) - 1 )
  193 
  194 #define IndexedPtr(ptr,buffer,index,maxindex) \
  195 #if ( &buffer[0] >> 8 ) != ( &buffer[maxindex] >> 8 ) \
  196   ptr = (uns16)( buffer + (index) ); \
  197 #else \
  198   ptr.low8 = (uns8)(uns16)( buffer + (index) ); \
  199   ptr.high8 = (uns8)(&buffer[0] >> 8 ); \
  200 #endif \
  201 
  202 // Definition used for TRx2 having connected pins
  203 #define _OUT_A5 TRISA.5
  204 #define _OUT_B4 TRISB.4
  205 #define _OUT_C5 TRISC.5
  206 #define _OUT_C6 TRISC.6
  207 #define _OUT_C7 TRISC.7
  208 
  209 //############################################################################################
  210 void APPLICATION()
  211 //############################################################################################
  212 {
  213   // Indicate start
  214   pulseLEDR();
  215   pulseLEDG();
  216 
  217   // We use UART
  218   OpenUART();
  219 
  220   // Filter used to receive DP2P responses
  221   uns8 RespRxFilter;
  222   // RF mode to send DP2P Requests
  223   uns8 ReqRfMode;
  224 
  225   // Loop forever
  226   for ( ;; )
  227   {
  228     // Anything at UART received?
  229     if ( RxUARTready() )
  230     {
  231       // UART incoming packet variable
  232       TSPI_TDP2Prequest SPI_TDP2Prequest @ RxUartBuffer;
  233       // Check the packet a little
  234       if ( SPI_TDP2Prequest.DP2PrequestParams._zero_ == 0 )
  235       {
  236         // DP2P parameters to set?
  237         if ( SPI_TDP2Prequest.DP2PrequestParams.PNUM == DP2P_PNUM && SPI_TDP2Prequest.DP2PrequestParams.PCMD == DP2P_PCMD )
  238         {
  239           // Check the length
  240           if ( UartRxSize == sizeof( SPI_TDP2Prequest.DP2PrequestParams ) )
  241           {
  242             // Indicate reception
  243             pulseLEDR();
  244 
  245             // Set RF mode and toutRF according to the network type
  246             if ( SPI_TDP2Prequest.DP2PrequestParams.STD_LP_network != 0 )
  247               // STD+LP network
  248               ReqRfMode = _WPE | _RX_STD | _TX_LP;
  249             else
  250               // STD network
  251               ReqRfMode = _WPE | _RX_STD | _TX_STD | _STDL;
  252 
  253             // Set RF channel
  254             setRFchannel( SPI_TDP2Prequest.DP2PrequestParams.RfChannel );
  255             // Set RF power
  256             setRFpower( SPI_TDP2Prequest.DP2PrequestParams.ReqTxPower );
  257             // Remember RX filter
  258             RespRxFilter = SPI_TDP2Prequest.DP2PrequestParams.RespRxFilter;
  259             // Set Access Password
  260             copyMemoryBlock( SPI_TDP2Prequest.DP2PrequestParams.AccessPassword, bufferINFO, sizeof( SPI_TDP2Prequest.DP2PrequestParams.AccessPassword ) );
  261             setAccessPassword();
  262           }
  263         }
  264         else
  265         {
  266           // Check the length
  267           if ( UartRxSize >= offsetof( TDP2Prequest, PDATA ) )
  268           {
  269             // Send DP2P request
  270 
  271             // Prepare DPA Request
  272             // Get PNUN
  273             _PNUM = SPI_TDP2Prequest.DP2PrequestParams.PNUM;
  274             // Get PCMD
  275             _PCMD = SPI_TDP2Prequest.DP2PrequestParams.PCMD;
  276             // DP2P Request variable
  277             TDP2Prequest DP2Prequest @ bufferRF;
  278             // Make sure header is correct (there were PNUM and PMCD stored)
  279             SPI_TDP2Prequest.DP2Prequest.Header[0] = 0;
  280             SPI_TDP2Prequest.DP2Prequest.Header[1] = 0;
  281             // Set data length
  282             PPAR = UartRxSize - offsetof( TDP2Prequest, PDATA );
  283             // Set the DP2P packet length
  284             DLEN = sizeof( DP2Prequest );
  285             // Use Access Password for encryption
  286             encryptByAccessPassword = TRUE;
  287             // Encrypt the DP2P Request
  288             copyMemoryBlock( &SPI_TDP2Prequest, bufferRF, sizeof( SPI_TDP2Prequest ) );
  289             encryptBufferRF( sizeof( DP2Prequest ) / 16 );
  290             // Set RF Flags
  291             PIN = _DPAF_MASK;
  292             // And finally send DP2P Request
  293             setRFmode( ReqRfMode );
  294             RFTXpacket();
  295             // Indicate reception
  296             pulseLEDR();
  297           }
  298         }
  299       }
  300       // Start UART RX again
  301       RxUARTstart();
  302     }
  303 
  304     // Ready to receive STD packets
  305     toutRF = 1;
  306     setRFmode( _WPE | _RX_STD | _TX_STD );
  307     // Anything from RF received?
  308     if ( // There is a signal
  309          checkRF( RespRxFilter ) &&
  310          // Packet was received
  311          RFRXpacket() &&
  312          // DPA flags are used
  313          _DPAF &&
  314          // There is enough data (at least one AES block)
  315          DLEN >= 16 &&
  316          // Real packet has a correct AES-128 block length
  317          ( ( PPAR + 15 ) & ~15 ) == DLEN )
  318     {
  319       // Decrypt the packet
  320       encryptByAccessPassword = TRUE;
  321       decryptBufferRF( DLEN / 16 );
  322 
  323       // DP2P Invite variable
  324       TDP2Invite DP2Invite @ bufferRF;
  325       // Check the DP2P Invite validity
  326       if ( PPAR == sizeof( TDP2Invite ) && DP2Invite.Header[0] == 1 && ( DP2Invite.Header[1] | DP2Invite.Header[2] ) == 0 )
  327       {
  328         // Save N address
  329         uns8 addr = DP2Invite.NADR;
  330 
  331         // Prepare DP2P Confirm
  332         TDP2Confirm DP2Confirm @ bufferRF;
  333         DP2Confirm.Header[0] = 0x03;
  334         // Encrypt the DP2P Confirm
  335         encryptByAccessPassword = TRUE;
  336         encryptBufferRF( sizeof( TDP2Confirm ) / 16 );
  337         // And finally send DP2P Confirm
  338         RFTXpacket();
  339         // Indicate reception
  340         pulseLEDG();
  341 
  342         // Finish UART TX work
  343         WaitTxUART();
  344         // Prepare SPI Invite
  345         TSPI_TDP2Pinvite SPI_TDP2Pinvite @ TxUartBuffer;
  346         SPI_TDP2Pinvite.NADR = addr;
  347         SPI_TDP2Pinvite.Header[0] = 0x01;
  348         SPI_TDP2Pinvite.Header[1] = 0x00;
  349         SPI_TDP2Pinvite.Header[2] = 0x00;
  350         // Send UART outgoing Invite packet
  351         TxUART( sizeof( TSPI_TDP2Pinvite ) );
  352       }
  353       else
  354       {
  355         // DP2P Response variable
  356         TDP2Presponse DP2Presponse @ bufferRF;
  357         // Check the DP2P Response validity
  358         if ( PPAR >= offsetof( TDP2Presponse, PDATA ) && ( DP2Presponse.Header[0] & DP2Presponse.Header[1] & DP2Presponse.Header[2] ) == 0xFF )
  359         {
  360           // SPI outgoing Response packet variable
  361           TSPI_TDP2Presponse SPI_TDP2Presponse @ TxUartBuffer;
  362           // Finish UART TX work
  363           WaitTxUART();
  364           // Copy to TX buffer
  365           copyMemoryBlock( bufferRF, &SPI_TDP2Presponse, sizeof( SPI_TDP2Presponse ) );
  366           // Report PNUM and PCMD back via SPI
  367           SPI_TDP2Presponse.DP2PresponseParams.PNUM = _PNUM;
  368           SPI_TDP2Presponse.DP2PresponseParams.PCMD = _PCMD;
  369           // Send response to UART
  370           TxUART( PPAR );
  371           // Indicate reception
  372           pulseLEDR();
  373           pulseLEDG();
  374         }
  375       }
  376     }
  377   }
  378 }
  379 
  380 //############################################################################################
  381 void OpenUART()
  382 //############################################################################################
  383 {
  384   // Enable user ISR
  385   _enableUserInterrupt = 1;
  386 
  387   // If TR module with connected pins, disconnect them from RX and TX
  388   _OUT_C5 = 1;      // pin connected with RX
  389   _OUT_A5 = 1;      // pin connected with TX
  390   _OUT_B4 = 1;      // pin connected with TX
  391   // Set RX, TX pins
  392   _OUT_C7 = 1;      // RX input
  393   _OUT_C6 = 0;      // TX output
  394 
  395 #if defined( TR7xG )
  396   UART1MD = 0;
  397   unlockPPS();
  398   RC6PPS = 0x10;  // TX (Note: RX does not have to be set, use default)
  399   lockPPS();
  400 #endif
  401 
  402   // Setup baud rate
  403   _SPBRGL = UART_SPBRG_VALUE( BAUDRATE ) & 0xff;
  404   _SPBRGH = UART_SPBRG_VALUE( BAUDRATE ) >> 8;
  405   // baud rate control setup: BRG16 = 1
  406 #if defined( TR7xG )
  407   setBAUD1CON( 0b0000.1.000 );
  408 #else
  409   BAUDCON = 0b0000.1.000;
  410 #endif
  411 
  412   // Async UART, high speed, 8 bit, TX enabled
  413   // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D
  414 #if defined( TR7xG )
  415   setTX1STA( 0b0010.0100 );
  416 #else
  417   TXSTA = 0b0010.0100;
  418 #endif
  419 
  420   // Continuous receive, enable port, 8 bit
  421   // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D
  422   _RCSTA = 0b1001.0000;
  423 
  424   // Enable UART RX
  425   RCIE = TRUE;
  426 }
  427 
  428 //############################################################################################
  429 void TxUART( uns8 dataLength @ W )
  430 //############################################################################################
  431 {
  432 #pragma updateBank exit = 11
  433 
  434   // Length of the TX packet
  435   UartTxSize = dataLength;
  436   // Initial UART TX state
  437   UartTxState = UART_TX_START;
  438   // Enable UART TX
  439   TXIE = TRUE;
  440 }
  441 
  442 //############################################################################################
  443 uns8 UpdateCrc( uns8 crc @ FSR1H, uns8 value @ W )
  444 //############################################################################################
  445 {
  446 #define Poly 0x8c // 0x8C (OneWire x^8 + x^5 + x^4 + 1) is reverse polynomial representation (normal is 0x31, reciprocal is 0x98)
  447 
  448   // Based on http://www.dattalo.com/technical/software/pic/crc_8bit.c
  449   // 19 instructions, 20 machine cycles per byte. [Hynek]
  450 
  451   // Data in = W
  452   // CRC in = FSR1H
  453   // CRC out = W
  454 
  455   /*
  456   *  Xn = (Xn-1 >> 1 ) ^ ( Xn-1.0 ? 0x8C : 0x00 )
  457   *  For 0x8c: 0x46, 0x23, 0x9d, 0xc2, 0x61, 0xbc, 0x5e
  458   */
  459 
  460 #define NextP(CurPoly,Poly) ((uns8)( ((CurPoly) >> 1) ^ ( ( (((CurPoly) & 1) ^ 1 ) - 1 ) & (Poly) )))
  461 
  462   crc ^= value;
  463 #pragma updateBank 0 /* OFF */
  464   value = 0;
  465   if ( crc.7 )
  466     value ^= Poly;  // Reverse polynomial representation
  467   if ( crc.6 )
  468     value ^= NextP( Poly, Poly );
  469   if ( crc.5 )
  470     value ^= NextP( NextP( Poly, Poly ), Poly );
  471   if ( crc.4 )
  472     value ^= NextP( NextP( NextP( Poly, Poly ), Poly ), Poly );
  473   if ( crc.3 )
  474     value ^= NextP( NextP( NextP( NextP( Poly, Poly ), Poly ), Poly ), Poly );
  475   if ( crc.2 )
  476     value ^= NextP( NextP( NextP( NextP( NextP( Poly, Poly ), Poly ), Poly ), Poly ), Poly );
  477   if ( crc.1 )
  478     value ^= NextP( NextP( NextP( NextP( NextP( NextP( Poly, Poly ), Poly ), Poly ), Poly ), Poly ), Poly );
  479   if ( crc.0 )
  480     value ^= NextP( NextP( NextP( NextP( NextP( NextP( NextP( Poly, Poly ), Poly ), Poly ), Poly ), Poly ), Poly ), Poly );
  481 
  482   return value;
  483 #pragma updateBank 1 /* ON */
  484 }
  485 
  486 //############################################################################################
  487 #pragma origin __USER_INTERRUPT // User interrupt service routine
  488 #pragma library 0               // Always compile
  489 void userIntRoutine()
  490 //############################################################################################
  491 {
  492   if ( RCIF )
  493   {
  494     // Received byte, we ignore FERR
  495     uns8 RxByte @ FSR1L;
  496     RxByte = _RCREG;
  497 
  498     // 8b RX checksum
  499     static uns8 uartRxCrc;
  500 
  501     // State RX machine
  502     skip( UartRxState );
  503 #pragma computedGoto 1
  504     goto _UART_RX_WAITHEAD;
  505     goto _UART_RX_STOP;
  506     goto _UART_RX_PACKET;
  507     goto _UART_RX_ESCAPE;
  508 #pragma computedGoto 0
  509     ;
  510     // ---------------------------
  511 _UART_RX_ESCAPE:
  512     switch ( RxByte )
  513     {
  514       case HDLC_FRM_FLAG_SEQUENCE:
  515         goto _SetUART_RX_PACKET;
  516 
  517       case HDLC_FRM_CONTROL_ESCAPE:
  518       {
  519 _Set_UART_RX_WAITHEAD:
  520         W = UART_RX_WAITHEAD;
  521         goto _W2state;
  522       }
  523     }
  524 
  525     // UART_RX_PACKET
  526     UartRxState -= UART_RX_ESCAPE - UART_RX_PACKET;
  527     RxByte ^= HDLC_FRM_ESCAPE_BIT;
  528     goto _UART_RX_StoreByte;
  529 
  530     // ---------------------------
  531 _UART_RX_PACKET:
  532     switch ( RxByte )
  533     {
  534       case HDLC_FRM_CONTROL_ESCAPE:
  535         W = UART_RX_ESCAPE;
  536         goto _W2state;
  537 
  538       case HDLC_FRM_FLAG_SEQUENCE:
  539       {
  540         if ( uartRxCrc == 0 && UartRxSize >= ( MinRxPacketSize + sizeof( uartRxCrc ) ) )
  541         {
  542           // Packet received
  543           UartRxSize -= sizeof( uartRxCrc );
  544           W = UART_RX_STOP;
  545           goto _W2state;
  546         }
  547 
  548         goto _SetUART_RX_PACKET;
  549       }
  550     }
  551 
  552 _UART_RX_StoreByte:
  553     // Update checksum
  554     uartRxCrc = UpdateCrc( uartRxCrc, RxByte );
  555 
  556     // If too many bytes
  557     if ( UartRxSize >= ( MaxRxPacketSize + sizeof( uartRxCrc ) ) )
  558       goto _Set_UART_RX_WAITHEAD;
  559 
  560     // Address to store the byte
  561     // FSR0 = (uns16)&RxUartBuffer[0] + UartRxSize;
  562     IndexedPtr( FSR0, RxUartBuffer, UartRxSize, sizeof( RxUartBuffer ) / sizeof( RxUartBuffer[0] ) - 1 );
  563     // Next index
  564     UartRxSize++;
  565     // Store byte
  566     setINDF0( RxByte );
  567 
  568     goto _UART_RX_STOP;
  569 
  570     // ---------------------------
  571 _UART_RX_WAITHEAD:
  572     if ( RxByte == HDLC_FRM_FLAG_SEQUENCE )
  573     {
  574 _SetUART_RX_PACKET:
  575       // Start new packet
  576       UartRxSize = 0;
  577       // Initialize RX checksum
  578       uartRxCrc = 0xff;
  579       W = UART_RX_PACKET;
  580 
  581 _W2state:
  582       UartRxState = W;
  583       goto _UART_RX_STOP;
  584     }
  585 
  586 _UART_RX_STOP:
  587   }
  588 
  589   // Overrun recovery (we do it after receiving UART byte in order to receive it as soon as possible)
  590   if ( OERR )
  591     CREN = 0;
  592 #pragma updateBank 0  // Note: to avoid unnecessary MOVLB generated by CC5X, OERR and CREN must share the same bank!
  593   // Seems excess, but at the end it is shorted and faster than having this statement at else branch
  594   CREN = 1;
  595 #pragma updateBank 1
  596 
  597   // If not transmit interrupt and have some data to send then skip
  598   if ( !TXIF )
  599     goto UART_TX_END;
  600 
  601   if ( !TXIE )
  602     goto UART_TX_END;
  603 
  604   skip( UartTxState );
  605 #pragma computedGoto 1
  606   goto _UART_TX_START;     // UART_TX_START
  607   goto _UART_TX_DATA_CHSUM;// UART_TX_DATA
  608   goto _UART_TX_DATA_CHSUM;// UART_TX_CHSUM
  609   goto _UART_TX_STOP;      // UART_TX_STOP
  610 #pragma computedGoto 0
  611   ;
  612   // -------------------------
  613 _UART_TX_STOP:
  614   // Stop UART TX
  615   TXIE = FALSE;
  616   // Send end of packet byte (will also set invalid state but the interrupt UART TX machine is stopped so it does not matter)
  617   goto UART_TX_HDLC_FRM_FLAG_SEQUENCE;
  618 
  619   // TRUE when previous byte was escaped one
  620   static bit wasTxEscape;
  621   // Pointer to the data to send
  622   static uns16 txPtr;
  623   // 8b check sum
  624   static uns8 uartTxCrc;
  625 
  626   // -------------------------
  627 _UART_TX_START:
  628   // No escape character was sent
  629   wasTxEscape = FALSE;
  630   // Pointer to the 1st byte to send
  631   txPtr = ( (uns16)&TxUartBuffer[0] );
  632   // Initialize ChSum
  633   uartTxCrc = 0xff;
  634 
  635   // Send start/end of packet byte
  636 UART_TX_HDLC_FRM_FLAG_SEQUENCE:
  637   _TXREG = HDLC_FRM_FLAG_SEQUENCE;
  638   // Next state (UART_DATA)
  639   goto UART_TX_NEXT_STATE;
  640 
  641   // -------------------------
  642 _UART_TX_DATA_CHSUM:
  643   // FSR0 points to the data to send
  644   FSR0 = txPtr;
  645   // Was the last byte escaped?
  646   if ( wasTxEscape )
  647   {
  648     // Escaping is over
  649     wasTxEscape = FALSE;
  650     // Send modified byte & Update ChSum
  651     W = *FSR0 ^ HDLC_FRM_ESCAPE_BIT;
  652   }
  653   else
  654     // Is byte to be escaped?
  655     switch ( *FSR0 )
  656     {
  657       case HDLC_FRM_FLAG_SEQUENCE:
  658       case HDLC_FRM_CONTROL_ESCAPE:
  659       {
  660         // Mark the byte was escaped
  661         wasTxEscape = TRUE;
  662         // Send escape byte
  663         W = HDLC_FRM_CONTROL_ESCAPE;
  664 
  665 UART_TXW_END:
  666         // TX WREG
  667         _TXREG = W;
  668         goto UART_TX_END;
  669       }
  670 
  671       // No escape
  672       default:
  673         // Send non-modified byte
  674         W = *FSR0;
  675         break;
  676     }
  677 
  678   // TX WREG
  679   _TXREG = W;
  680   // Update checksum
  681   uartTxCrc = UpdateCrc( uartTxCrc, *FSR0 );
  682   // Move to the next byte
  683   txPtr++;
  684   // More bytes to send next time?
  685   if ( --UartTxSize != 0 )
  686     // Yes, keep state
  687     goto UART_TX_END;
  688 
  689   // Next "data" is (not when going to state UART_TX_STOP) checksum
  690   txPtr = (uns16)&uartTxCrc;
  691   // Size is 1 byte
  692   UartTxSize = sizeof( uartTxCrc );
  693 
  694   // Next state (UART_TX_CHSUM or UART_TX_STOP)
  695 UART_TX_NEXT_STATE:
  696   // Next UART state
  697   UartTxState++;
  698 
  699 UART_TX_END:
  700 }
  701 
  702 //############################################################################################