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