1 // *********************************************************************
    2 //   Custom DPA Handler code template                                  *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-SerialSpiRam.c,v $
    7 // Version: $Revision: 1.1 $
    8 // Date:    $Date: 2026/05/28 11:31:00 $
    9 //
   10 // Revision history:
   11 //   2026/05/27  Release for DPA 4.33
   12 //
   13 // *********************************************************************
   14 
   15 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   16 
   17 // This example uses the serial SPI RAM 23LCV512.
   18 // The RAM can be written to and read from using the same commands as the embedded EEPROM peripheral, with a user peripheral PNUM=0x20 (https://doc.iqrf.org/DpaTechGuide/pages/eeeprom.html).
   19 //
   20 // The SDI and SDO signals are connected to save used GPIOs when the SDO_SDI symbol is defined.
   21 // To minimize short-circuit current spikes, a 100R-1k resistor must then be placed between the connected SDI and SDO at the RAM and the PIC GPIO.
   22 
   23 // Default IQRF include (modify the path according to your setup)
   24 #include "IQRF.h"
   25 
   26 // Default DPA header (modify the path according to your setup)
   27 #include "DPA.h"
   28 // Default Custom DPA Handler header (modify the path according to your setup)
   29 #include "DPAcustomHandler.h"
   30 
   31 // Define to use connected SDO and SDI, they share the same signal
   32 // #define SDO_SDI
   33 
   34 // SPI signals to PIN assignment:
   35 //      TR module pin   DK-EVAL-04x pin     SPI
   36 //      ---------------------------------------
   37 //      C8 (RC5)        1                   SDO, unused when SDO_SDI
   38 //      C7 (RC4)        2                   SDI, also SDO when SDO_SDI
   39 //      C6 (RC3)        3                   SCK
   40 //      C5 (RA5)        4                   CS
   41 
   42 // _SS_ pin assignment (C5 = PORTA.5)
   43 #define CS_PIN      LATA.5
   44 
   45 // Serial RAM commands
   46 #define RAM_READ    0b0000.0011
   47 #define RAM_WRITE   0b0000.0010
   48 
   49 // Writes to and reads from RAM
   50 void Ram( uns16 address, uns16 buffer, uns8 length, uns8 command );
   51 
   52 //############################################################################################
   53 // https://doc.iqrf.org/DpaTechGuide/pages/custom-dpa-handler.html
   54 bit CustomDpaHandler()
   55 //############################################################################################
   56 {
   57   // Handler presence mark
   58   clrwdt();
   59 
   60   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
   61   switch ( GetDpaEvent() )
   62   {
   63     // -------------------------------------------------
   64     case DpaEvent_Interrupt:
   65       // Do an extra quick background interrupt work
   66       // ! The time spent handling this event is critical.If there is no interrupt to handle return immediately otherwise keep the code as fast as possible.
   67       // ! Make sure the event is the 1st case in the main switch statement at the handler routine.This ensures that the event is handled as the 1st one.
   68       // ! It is desirable that this event is handled with immediate return even if it is not used by the custom handler because the Interrupt event is raised on every MCU interrupt and the “empty” return handler ensures the shortest possible interrupt routine response time.
   69       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   70       // ! Make sure race condition does not occur when accessing those variables at other places.
   71       // ! Make sure( inspect.lst file generated by C compiler ) compiler does not create any hidden temporary local variable( occurs when using division, multiplication or bit shifts ) at the event handler code.The name of such variable is usually Cnumbercnt.
   72       // ! Do not call any OS functions except setINDFx().
   73       // ! Do not use any OS variables especially for writing access.
   74       // ! All above rules apply also to any other function being called from the event handler code, although calling any function from Interrupt event is not recommended because of additional MCU stack usage.
   75       // https://doc.iqrf.org/DpaTechGuide/pages/EventInterrupt.html
   76       return Carry;
   77 
   78       // -------------------------------------------------
   79     case DpaEvent_Init:
   80       // Do a one time initialization before main loop starts
   81       // https://doc.iqrf.org/DpaTechGuide/pages/init.html
   82 
   83       // Do a one time initialization before main loop starts
   84     {
   85       { // Initialize GPIOs to inputs and outputs
   86 
   87         // Set idle level of SS
   88         CS_PIN = 1;
   89 
   90 #ifndef SDO_SDI
   91         TRISC.5 = 0;        // RC5 is output SDO (C8)
   92         TRISC.4 = 1;        // RC4 is input SDI (C7)
   93 #else
   94         TRISC.4 = 0;        // RC4 is output SDO (C7)
   95 #endif
   96 
   97         TRISC.3 = 0;        // RC3 is output SCK (C6)
   98         TRISA.5 = 0;        // RA5 is output SS (C5)
   99 
  100         // TR module with connected pins?
  101         moduleInfo();
  102         if ( ModuleInfo.TrType.7 == 0 )
  103         {
  104           TRISC.6 = 1;      // RC6 is input (connected to RA5 in parallel)
  105           TRISB.4 = 1;      // RB4 is input (connected to RA5 in parallel)
  106           TRISC.7 = 1;      // RC7 is input (connected to RC5 in parallel)
  107         }
  108       }
  109 
  110       // Enable SPI module
  111       MSSP1MD = 0;
  112 
  113       { // PPS
  114         unlockPPS();
  115 #ifndef SDO_SDI
  116         SSP1DATPPS = 0x14;  // RC4 is SDI
  117         RC5PPS = 0x15;      // RC5 is SDO
  118 #else
  119         SSP1DATPPS = 0x14;  // RC4 is SDI
  120         RC4PPS = 0x15;      // RC4 is SDO
  121 #endif
  122 
  123         RC3PPS = 0x14;      // RC3 is SCK
  124         lockPPS();
  125       }
  126 
  127       { // Setup SPI module
  128         // SSPSTAT
  129         //  Transmit occurs on SCK rising edge
  130         _CKE = 1;
  131         // SSPCON1
  132         //  SPI enabled
  133         //  Idle state for clock is a low level
  134         //  CLK = 4 MHz @ 16 MHz
  135         _SSPCON1 = 0b0.0.1.0.0000;
  136       }
  137 
  138       break;
  139     }
  140 
  141     // -------------------------------------------------
  142     case DpaEvent_DpaRequest:
  143       // Called to interpret DPA request for peripherals
  144       // https://doc.iqrf.org/DpaTechGuide/pages/EventDpaRequest.html
  145       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  146       {
  147         // -------------------------------------------------
  148         // Peripheral enumeration
  149         // https://doc.iqrf.org/DpaTechGuide/pages/enumerate-peripherals.html
  150 
  151         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  152         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER );
  153         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x581F;
  154 
  155 
  156 DpaHandleReturnTRUE:
  157         return TRUE;
  158       }
  159       else
  160       {
  161         // -------------------------------------------------
  162         // Get information about peripheral
  163         // https://doc.iqrf.org/DpaTechGuide/pages/get-peripheral-info.html
  164 
  165         if ( _PNUM == PNUM_USER )
  166         {
  167           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_BLOCK_EEPROM;
  168           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  169           _DpaMessage.PeripheralInfoAnswer.Par1 = ( 0x10000 / 256 ) & 0xFF;
  170           goto DpaHandleReturnTRUE;
  171         }
  172 
  173         break;
  174         }
  175 
  176         // -------------------------------------------------
  177         // Handle peripheral command
  178         // https://doc.iqrf.org/DpaTechGuide/pages/handle-peripheral-request.html
  179 
  180         if ( _PNUM == PNUM_USER )
  181         {
  182           switch ( _PCMD )
  183           {
  184             case CMD_EEEPROM_XREAD:
  185               if ( _DpaMessage.XMemoryRequest.ReadWrite.Read.Length > sizeof( _DpaMessage.Response.PData ) )
  186                 DpaApiReturnPeripheralError( ERROR_DATA );
  187 
  188               Ram( _DpaMessage.XMemoryRequest.Address, FSR0 /* same as _DpaMessage.Response.PData */, _DpaDataLength = _DpaMessage.XMemoryRequest.ReadWrite.Read.Length, RAM_READ );
  189               goto DpaHandleReturnTRUE;
  190 
  191             case CMD_EEEPROM_XWRITE:
  192               Ram( _DpaMessage.XMemoryRequest.Address, _DpaMessage.XMemoryRequest.ReadWrite.Write.PData, _DpaDataLength - XMEMORY_WRITE_REQUEST_OVERHEAD, RAM_WRITE );
  193               _DpaDataLength = 0;
  194               goto DpaHandleReturnTRUE;
  195 
  196             default:
  197               DpaApiReturnPeripheralError( ERROR_PCMD );
  198           }
  199         }
  200 
  201         break;
  202   }
  203 
  204 DpaHandleReturnFALSE:
  205   return FALSE;
  206 }
  207 
  208 //############################################################################################
  209 uns8 SPIwriteRead( uns8 data @ W )
  210 //############################################################################################
  211 {
  212   // Reset interrupt flag
  213   _SSPIF = 0;  // Note: must not modify W
  214   // Write the byte
  215   _SSPBUF = W;
  216   // Wait until the byte is transmitted/received
  217   while ( !_SSPIF );
  218   // Return result
  219   return _SSPBUF;
  220 }
  221 
  222 //############################################################################################
  223 void Ram( uns16 address @ param3, uns16 buffer @ FSR0, uns8 length @ param2, uns8 command )
  224 //############################################################################################
  225 {
  226   // SPI SS - activate
  227   CS_PIN = 0;
  228 
  229 #ifdef SDO_SDI
  230   // Get ready for the later PPS
  231   unlockPPS();
  232   GIE = 1;
  233 #endif
  234 
  235   // Increase length for the upcoming optimized loop
  236   length++;
  237 
  238   SPIwriteRead( command );
  239   SPIwriteRead( address.high8 );
  240   SPIwriteRead( address.low8 );
  241 
  242 #ifdef SDO_SDI
  243   // Switch from SDO to SDI in case of RAM_READ?
  244   if ( ( command & ( RAM_READ ^ RAM_WRITE ) ) == ( RAM_READ & ~RAM_WRITE ) )  // Note: The expression is optimized by the compiler for the bit test because RAM_READ and RAM_WRITE differ by only one bit.
  245   {
  246     TRISC.4 = 1;    // RC4 is input ready for SDI
  247     RC4PPS = 0x00;  // RC4 is not SDO any more
  248   }
  249 #endif
  250 
  251   goto _inLoop;
  252   do
  253   {
  254     setINDF0( SPIwriteRead( *FSR0 ) );
  255     FSR0++;
  256 _inLoop:
  257   } while ( --length );
  258 
  259   // SPI SS - deactivate
  260   CS_PIN = 1;
  261 
  262 #ifdef SDO_SDI
  263   // Switch back from SDI to SDO in case of RAM_READ?
  264   if ( ( command & ( RAM_READ ^ RAM_WRITE ) ) == ( RAM_READ & ~RAM_WRITE ) )  // Note: The expression is optimized by the compiler for the bit test because RAM_READ and RAM_WRITE differ by only one bit.
  265   {
  266     RC4PPS = 0x15;  // RC4 is SDO again
  267     TRISC.4 = 0;    // RC4 is output ready for SDO again
  268   }
  269 
  270   GIE = 0;
  271   lockPPS();
  272 #endif
  273 }
  274 
  275 //############################################################################################
  276 // 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)
  277 #include "DPAcustomHandler.h"
  278 //############################################################################################