1 // *********************************************************************
    2 //   Custom DPA Handler - Bridge using UART                            *
    3 // *********************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-Bridge-UART.c,v $
    7 // Version: $Revision: 1.11 $
    8 // Date:    $Date: 2019/02/28 09:11:23 $
    9 //
   10 // Revision history:
   11 //   2018/10/25  Release for DPA 3.03
   12 //
   13 // *********************************************************************
   14 
   15 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   16 
   17 // Default IQRF include (modify the path according to your setup)
   18 #include "IQRF.h"
   19 
   20 // Default DPA header (modify the path according to your setup)
   21 #include "DPA.h"
   22 // Default Custom DPA Handler header (modify the path according to your setup)
   23 #include "DPAcustomHandler.h"
   24 
   25 //############################################################################################
   26 /*
   27 This handler forwards [1] DpaEvent_DpaRequest and [2] DpaEvent_FrcValue events via UART to the external device. See UART_BAUD for the baud rate setting.
   28 The external device is responsible for preparing the proper responses to these events. It sends the response back to the handler via UART.
   29 Handler then responses with the response prepared by the external device back to the IQRF network.
   30 
   31 There is an example CustomDpaHandler-Bridge-UART.ino for Arduino that shows how the external device prepares the response. In this case the device behaves as Standard IQRF Sensor.
   32 Use an IQRF Breakout board IQRF-BB-01 or IQRF-SHIELD-02 containing the level shifters to connect TR module (3.3 V logic) to the Arduino board (5 V logic):
   33 #IQRF-BB-01    Arduino
   34  GND           GND
   35  +3V3/+5V      3.3V
   36  MISO (C8=RX)  TX -> Digital 1
   37  SS (C5=TX)    RX <- Digital 0
   38 
   39 This handler and the external device exchange data (packets) using the HDLC protocol the same way the DPA UART Interface except the CRC is not used.
   40 Please see https://www.iqrf.org/DpaTechGuide/#2.3.2%20UART for details.
   41 
   42 The handler and the external device pack a different set of information to the packet depending on the event.
   43 
   44 [1] DpaEvent_DpaRequest
   45 The packet (data part) sent to the device has the following structure:
   46 #Byte  Content         Notes
   47  0     0               = DpaEvent_DpaRequest
   48  1     _DpaDataLength
   49  2     NADR
   50  3     NADRhigh
   51  4     PNUM
   52  5     PCMD
   53  6     HWPID.low8
   54  7     HWPID.high8
   55  8...  PDATA           Optional DPA Request data.
   56 
   57 Please note that _DpaDataLength does not have to equal (in case of enumeration DPA Requests) length of PDATA.
   58 The handler waits maximum RESPONSE_TIMEOUT ms for the response from the external device otherwise ERROR_FAIL is returned.
   59 
   60 The device responses with a packet of the following structure:
   61 #Byte  Content         Notes
   62  0     see Notes       = DpaEvent_DpaRequest with ORed optional flags. When bit7 set, the handler returns TRUE otherwise returns FALSE. When bit6 is set, the handler returns DPA error specified at byte #2 i.e. PDATA[0]
   63  1     _DpaDataLength
   64  2...  PDATA           Optional DPA Response data.
   65 
   66 [2] DpaEvent_FrcValue
   67 The 32 bytes long packet (data part) sent to the device has the following structure:
   68 #Byte   Content        Notes
   69  0      10             = DpaEvent_FrcValue
   70  1      RFC Command
   71  2...31 FRC user data
   72 
   73 The handler waits maximum FRC_RESPONSE_TIMEOUT ms for the response from the external device otherwise the FRC is not handled and the default DPA value (bit0=1) is returned.
   74 
   75 The device responses with a 5 bytes long packet of the following structure:
   76 #Byte  Content         Notes
   77  0     10              DpaEvent_DpaRequest
   78  1...4 FRC value       Content will be written to the responseFRCvalue, responseFRCvalue2B, responseFRCvalue4B variables. Coded using Little Endian.
   79 */
   80 //############################################################################################
   81 
   82 // UART baud rate
   83 #define UART_BAUD                 115200
   84 
   85 // DPA response timeout from external device [ms]
   86 #define RESPONSE_TIMEOUT          ( 30 / 10 ) 
   87 // FRC value response timeout from external device [ms]
   88 #define FRC_RESPONSE_TIMEOUT      ( 20 / 10 )
   89 // DPA reported fixed FRC response time
   90 #define _FRC_RESPONSE_TIME_FIXED  _FRC_RESPONSE_TIME_40_MS
   91 
   92 //############################################################################################
   93 
   94 // Division macro with rounding
   95 #define DIV(Dividend,Divisor) (((Dividend+((Divisor)>>1))/(Divisor)))
   96 // PIC baud register computation
   97 #define UART_SPBRG_VALUE( Baud )  ( DIV( F_OSC, ( ( ( uns24 )4  ) * ( Baud ) ) ) - 1 )
   98 
   99 // HDLC byte stuffing bytes
  100 // Flag Sequence
  101 #define   HDLC_FRM_FLAG_SEQUENCE    0x7e
  102 // Asynchronous Control Escape
  103 #define   HDLC_FRM_CONTROL_ESCAPE   0x7d
  104 // Asynchronous transparency modifier
  105 #define   HDLC_FRM_ESCAPE_BIT       0x20
  106 
  107 // Flag to DpaEvent_DpaRequest event value to indicate return TRUE not FALSE
  108 #define EVENT_RETURN_TRUE           0x80
  109 // Flag to DpaEvent_DpaRequest event value to report error, error value is the 1st data byte
  110 #define EVENT_RESPONSE_ERROR        0x40
  111 
  112 //############################################################################################
  113 
  114 // Buffer used for exchange data with external device
  115 #define RxBuffer  bufferCOM
  116 // Sends byte to the external device
  117 void TxByte( uns8 data );
  118 // Sends HDLC byte to the external device
  119 void TxHdlcByte( uns8 data );
  120 // Receives data from external device, returns length, 0 if timeout occurred
  121 uns8 RxPacket( uns8 timeout );
  122 // Initialization
  123 void Init();
  124 
  125 //############################################################################################
  126 bit CustomDpaHandler()
  127 //############################################################################################
  128 {
  129   // Handler presence mark
  130   clrwdt();
  131 
  132   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
  133   switch ( GetDpaEvent() )
  134   {
  135     // -------------------------------------------------
  136     // Handler these unused events as fast as possible
  137     case DpaEvent_Idle:
  138       Init();
  139 
  140     case DpaEvent_Interrupt:
  141       return Carry;
  142 
  143       // -------------------------------------------------
  144     case DpaEvent_FrcValue:
  145       // Called to get FRC value
  146 
  147       // Initialize HW if needed
  148       Init();
  149       // Start measuring timeout for RxPacket() function
  150       startCapture();
  151 
  152       // Start packet
  153       TxByte( HDLC_FRM_FLAG_SEQUENCE );
  154       // Send event value
  155       TxHdlcByte( DpaEvent_FrcValue );
  156       // Send FRC command
  157       TxHdlcByte( _PCMD );
  158       // Now send all FRC user data
  159       uns8 loop = sizeof( DataOutBeforeResponseFRC );
  160       FSR0 = &DataOutBeforeResponseFRC[0];
  161       do
  162       {
  163         TxHdlcByte( *FSR0++ );
  164       } while ( --loop != 0 );
  165       // Stop packet
  166       TxByte( HDLC_FRM_FLAG_SEQUENCE );
  167 
  168       // Receive the FRC value from the device via UART, length must equal to the event value + 4 FRC value bytes
  169       if ( RxPacket( FRC_RESPONSE_TIMEOUT ) == ( sizeof( uns8 ) + sizeof( uns32 ) ) && RxBuffer[0] == DpaEvent_FrcValue )
  170       {
  171         // Return FRC values to DPA
  172 #if IQRFOS >= 403
  173         responseFRCvalue4B.low8 = RxBuffer[1];
  174         responseFRCvalue4B.midL8 = RxBuffer[2];
  175         responseFRCvalue4B.midH8 = RxBuffer[3];
  176         responseFRCvalue4B.high8 = RxBuffer[4];
  177 #else
  178         responseFRCvalue = RxBuffer[1];
  179         responseFRCvalue2B.low8 = RxBuffer[1];
  180         responseFRCvalue2B.high8 = RxBuffer[2];
  181 #endif
  182       }
  183 
  184       break;
  185 
  186       // -------------------------------------------------
  187     case DpaEvent_FrcResponseTime:
  188       // Called to get FRC response time
  189       // ToDo - Improve, make value dynamic?
  190       responseFRCvalue = _FRC_RESPONSE_TIME_FIXED;
  191       break;
  192 
  193       // -------------------------------------------------
  194     case DpaEvent_DpaRequest:
  195       // Called to interpret DPA request for peripherals
  196 
  197       // Initialize HW if needed
  198       Init();
  199       // Pass the request to the connected device via UART (must not modify FSR0 till it is used)
  200       // Start packet
  201       TxByte( HDLC_FRM_FLAG_SEQUENCE );
  202       // Send event value
  203       TxHdlcByte( DpaEvent_DpaRequest );
  204       // Send DPA variable data length (must not equal to the actual data length sent)
  205       TxHdlcByte( _DpaDataLength );
  206       // Send DPA message fields
  207       TxHdlcByte( _NADR );
  208       TxHdlcByte( _NADRhigh );
  209       TxHdlcByte( _PNUM );
  210       TxHdlcByte( _PCMD );
  211       TxHdlcByte( _HWPID.low8 );
  212       TxHdlcByte( _HWPID.high8 );
  213 
  214       // How much data to pass to the device?
  215       uns8 dataLength;
  216       if ( IsDpaEnumPeripheralsRequest() )
  217         dataLength = sizeof( _DpaMessage.EnumPeripheralsAnswer );
  218       else if ( IsDpaPeripheralInfoRequest() )
  219         dataLength = sizeof( _DpaMessage.PeripheralInfoAnswer );
  220       else
  221         // Same amount as available
  222         dataLength = _DpaDataLength;
  223 
  224       // FSRx might have been destroyed by Init()
  225       setFSR01( _FSR_RF, _FSR_RF );
  226       // Now send the data byte by byte
  227       for ( ; dataLength != 0; dataLength-- )
  228         TxHdlcByte( *FSR0++ );
  229       // Stop packet
  230       TxByte( HDLC_FRM_FLAG_SEQUENCE );
  231 
  232       // Start measuring timeout for RxPacket() function
  233       startCapture();
  234 
  235       // Receive the response from the device via UART
  236       dataLength = RxPacket( RESPONSE_TIMEOUT );
  237       // Check for timeout and correct event
  238       if ( dataLength == 0 || ( RxBuffer[0] & ~( EVENT_RETURN_TRUE | EVENT_RESPONSE_ERROR ) ) != DpaEvent_DpaRequest )
  239         DpaApiReturnPeripheralError( ERROR_FAIL );
  240 
  241       // Report DPA error?
  242       if ( ( RxBuffer[0] & EVENT_RESPONSE_ERROR ) != 0 )
  243         DpaApiReturnPeripheralError( RxBuffer[2] );
  244 
  245       // Get DPA data length field
  246       _DpaDataLength = RxBuffer[1];
  247       // Copy DPA response data (all data minus event value and _DpaDataLength value)
  248       copyMemoryBlock( &RxBuffer[2], &_DpaMessage.Response.PData[0], dataLength - 2 );
  249 
  250       // Return TRUE or FALSE
  251 #if EVENT_RETURN_TRUE != 0x80
  252 #error Cannot optimize
  253 #endif
  254       // Carry = TRUE of FALSE to return, got from EVENT_RETURN_TRUE part of the 1st byte which is header
  255       W = rl( RxBuffer[0] );
  256       return Carry;
  257   }
  258 
  259   return FALSE;
  260 }
  261 
  262 //############################################################################################
  263 uns8 RxByteValue;
  264 bit RxByte()
  265 //############################################################################################
  266 {
  267   if ( !RCIF )
  268     goto _returnFALSE;
  269 
  270   if ( FERR )
  271   {
  272     W = RCREG;
  273     goto _returnFALSE;
  274   }
  275 
  276   RxByteValue = RCREG;
  277 
  278   if ( OERR )
  279   {
  280     CREN = 0;
  281     CREN = 1;
  282 _returnFALSE:
  283     return FALSE;
  284   }
  285 
  286   return TRUE;
  287 }
  288 
  289 //############################################################################################
  290 uns8 RxPacket( uns8 timeout )
  291 //############################################################################################
  292 {
  293 #define MIN_RX_PACKET_DATA_LENGTH  1
  294 #define MAX_RX_PACKET_DATA_LENGTH  sizeof( RxBuffer )
  295 
  296   typedef enum { RXstateWaitHead, RXstatePacket, RXstateEscape } TState;
  297 
  298   // Make sure buffered UART RX is empty
  299   RxByte();
  300   RxByte();
  301 
  302   TState state = RXstateWaitHead;
  303   uns8 rxLength;
  304   for ( ; ; )
  305   {
  306     clrwdt();
  307     // Timeout?
  308     captureTicks();   // Note: must not modify FSR0
  309     if ( param3 > timeout )
  310       return 0;
  311 
  312     // If anything received via UART
  313     if ( RxByte() )
  314     {
  315       // HDLC machine
  316       skip( (uns8)state );
  317 #pragma computedGoto 1
  318       goto _RXstateWaitHead;
  319       goto _RXstatePacket;
  320       goto _RXstateEscape;
  321 #pragma computedGoto 0
  322       ;
  323       // ---------------------------
  324 _RXstateEscape:
  325       switch ( RxByteValue )
  326       {
  327         case HDLC_FRM_FLAG_SEQUENCE:
  328           goto _SetRXstatePacket;
  329 
  330         case HDLC_FRM_CONTROL_ESCAPE:
  331 _SetRXstateWaitHead:
  332           state = RXstateWaitHead;
  333           continue;
  334       }
  335 
  336       state--; // RXstatePacket
  337       RxByteValue ^= HDLC_FRM_ESCAPE_BIT;
  338       goto _StoreByte;
  339 
  340       // ---------------------------
  341 _RXstatePacket:
  342       switch ( RxByteValue )
  343       {
  344         case HDLC_FRM_CONTROL_ESCAPE:
  345           // RXstateEscape
  346           state++;
  347           continue;
  348 
  349         case HDLC_FRM_FLAG_SEQUENCE:
  350         {
  351           if ( rxLength >= MIN_RX_PACKET_DATA_LENGTH )
  352             return rxLength;
  353 
  354           goto _SetRXstatePacket;
  355         }
  356       }
  357 
  358 _StoreByte:
  359       if ( rxLength == ( MAX_RX_PACKET_DATA_LENGTH + 2 ) )
  360         goto _SetRXstateWaitHead;
  361 
  362       setINDF0( RxByteValue );
  363       FSR0++;
  364       rxLength++;
  365       continue;
  366 
  367       // ---------------------------
  368 _RXstateWaitHead:
  369       if ( RxByteValue == HDLC_FRM_FLAG_SEQUENCE )
  370       {
  371 _SetRXstatePacket:
  372         rxLength = 0;
  373         FSR0 = RxBuffer;
  374         state = RXstatePacket;
  375       }
  376 
  377       continue;
  378     }
  379   }
  380 }
  381 
  382 //############################################################################################
  383 void TxByte( uns8 data )
  384 //############################################################################################
  385 {
  386   while ( !TXIF );
  387   TXREG = data;
  388 }
  389 
  390 //############################################################################################
  391 void TxHdlcByte( uns8 data )
  392 //############################################################################################
  393 {
  394   switch ( data )
  395   {
  396     default:
  397       TxByte( data );
  398       return;
  399 
  400     case HDLC_FRM_FLAG_SEQUENCE:
  401     case HDLC_FRM_CONTROL_ESCAPE:
  402     {
  403       TxByte( HDLC_FRM_CONTROL_ESCAPE );
  404       TxByte( data ^ HDLC_FRM_ESCAPE_BIT );
  405       return;
  406     }
  407   }
  408 }
  409 
  410 //############################################################################################
  411 void Init()
  412 //############################################################################################
  413 {
  414   // Initialize UART
  415   if ( !SPEN )
  416   {
  417     // Connected TR pins (e.g. TR72D)?
  418     moduleInfo();
  419     if ( !bufferINFO[5].7 )
  420     {
  421       // Set them as inputs
  422       TRISC.5 = 1;
  423       TRISA.5 = 1;
  424       TRISB.4 = 1;
  425     }
  426 
  427     // RX input
  428     TRISC.7 = 1;
  429     // TX output
  430     TRISC.6 = 0;
  431 
  432     // Set baud rate
  433     SPBRGL = UART_SPBRG_VALUE( UART_BAUD ) & 0xff;
  434     SPBRGH = UART_SPBRG_VALUE( UART_BAUD ) >> 8;
  435     // baud rate control setup: BRG16 = 1
  436     BAUDCON = 0b0000.1.000;
  437 
  438     // CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D
  439     // TXEN = 1
  440     // BRGH = 1
  441     // async UART, high speed, 8 bit, TX enabled
  442     TXSTA = 0b0010.0100;
  443 
  444     // SPEN RX9 SREN CREN ADDEN FERR OERR RX9D
  445     // SPEN = 1
  446     // CREN = 1
  447     // Continuous receive, enable port, 8 bit
  448     RCSTA = 0b1001.0000;
  449   }
  450 }
  451 
  452 //############################################################################################
  453 // Default Custom DPA Handler header; 2nd include to implement Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup) 
  454 #include "DPAcustomHandler.h"
  455 //############################################################################################