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