1 // *********************************************************************************
    2 //   Custom DPA Handler code example - User peripheral implementation - SPI master *
    3 // *********************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UserPeripheral-SPImaster.c,v $
    7 // Version: $Revision: 1.29 $
    8 // Date:    $Date: 2021/04/26 15:13:51 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2015/08/05  Release for DPA 2.20
   13 //   2015/01/16  Release for DPA 2.12
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 
   19 // This example implements Master SPI as a user peripheral
   20 
   21 // Default IQRF include (modify the path according to your setup)
   22 #include "IQRF.h"
   23 
   24 // Default DPA header (modify the path according to your setup)
   25 #include "DPA.h"
   26 // Default Custom DPA Handler header (modify the path according to your setup)
   27 #include "DPAcustomHandler.h"
   28 
   29 // *********************************************************************
   30 
   31 // SPI master is controlled by data sent to the user peripheral PNUM=0x20, PCMD=0x00
   32 // Data consists of subcommands. Each four available subcommand consists of one SCMD byte and optional data.
   33 // If the subcommand reads data from SPI slave, then the read data is appended to the DPA response data
   34 
   35 // #  Subcommand:  SCMD      data                        Read data
   36 // 1. WriteRead:   00LL.LLLL _byte_1_ ... _byte_n_       _byte_1_ ... _byte_n_
   37 // 2. Write:       10LL.LLLL _byte_1_ ... _byte_n_
   38 // 3. Read:        01LL.LLLL                             _byte_1_ ... _byte_n_
   39 // 4. Delay:       11DD.DDDD dddddddd
   40 //
   41 // Notes:
   42 //  1.-3.  LLLLLL specifies length of written and/or read bytes to/from SPI slave
   43 //  1.-3.  When LLLLLL is zero (SCMD values 0x00, 0x40, 0x80), then no data is written and/or read but next command 1.-3. will not deactivate _SS_ signal
   44 //  3.     SDO is 0 during reading from SPI
   45 //  4.     DDDDDD.dddddddd specifies 14 bit delay in 1 ms units to be performed
   46 
   47 // Example:
   48 // The following example shows how to control serial 32 kB SRAM memory 23K256 (http://www.microchip.com/23K256) connected as SPI slave to the SPI master peripheral
   49 // 23K256 uses "SCK low when inactive" and "SDO/SDI valid on SCK rising edge"
   50 //
   51 // The example executes 3 actions using 5 subcommands:
   52 //  1. Sets serial RAM to "Sequential mode"
   53 //   Write to serial RAM SPI:  0x01{WRSR}, 0x40{status value}
   54 //   Subcommand #1:            0x82{#1:write 2 bytes to SPI}, 0x01{WRSR}, 0x40{status value}
   55 //
   56 //  2. Writes 2 bytes 0xAA and 0xBB starting from address 2 to the RAM
   57 //   Write to serial RAM SPI:  0x02{WRITE}, 0x00{address high8}, 0x02{address low8}, 0xAA{1st byte@2}, 0xBB{2nd byte@3}
   58 //   Subcommand #2:            0x85{#2:write 5 bytes}, 0x02{WRITE}, 0x00{address high8}, 0x02{address low8}, 0xAA{1st byte@2}, 0xBB{2nd byte@3}
   59 //
   60 //  3. Reads 4 bytes from address 1 from the RAM
   61 //   Write to serial RAM SPI:  0x03{READ}, 0x00{address high8}, 0x01{address low8} (keep _SS_ active after this SPI write)
   62 //   Read from serial RAM SPI: 0x??{unknown byte@1}, 0xAA{previously written byte@2}, 0xBB{previously written byte@3}, 0x??{unknown byte@4}
   63 //   Subcommands #3-5:         0x00{#3:keep _SS_ after subcommand #4}, 0x83{#4:write 3 bytes}, 0x03{READ}, 0x00{address high8}, 0x01{address low8}, 0x44{#5:read 4 bytes}
   64 //
   65 // DPA request:  PNUM=0x20, PCMD=0x00, Data[0x0F]=0x82, 0x01, 0x40, 0x85, 0x02, 0x00, 0x02, 0xAA, 0xBB, 0x00, 0x83, 0x03, 0x00, 0x01, 0x44
   66 // DPA response: PNUM=0x20, PCMD=0x80, Data[0x04]=0x??, 0xAA, 0xBB, 0x??
   67 
   68 // *********************************************************************
   69 
   70 // SPI signals to PIN assignment:
   71 //      TR module pin   DK-EVAL-04x pin     SPI
   72 //      ---------------------------------------
   73 //      C8              1                   SDO
   74 //      C7              2                   SDI
   75 //      C6              3                   SCK
   76 //      C5              4                   _SS_
   77 //      C4              7                   GND
   78 
   79 // _SS_ pin assignment (C5 = PORTA.5)
   80 #define SS_PIN                      LATA.5
   81 
   82 // Custom peripheral request internal subcommands
   83 // Write and read from SPI slave
   84 #define CMD_WRITE_READ              0x00
   85 // Write to SPI slave
   86 #define CMD_WRITE                   ((uns8)0x80)
   87 // Read from SPI slave
   88 #define CMD_READ                    0x40
   89 // Execute delay
   90 #define CMD_DELAY                   ((uns8)0xC0)
   91 
   92 // Mask to get the data from the subcommand
   93 #define CMD_MASK                    ((uns8)0xC0)
   94 
   95 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   96 //############################################################################################
   97 bit CustomDpaHandler()
   98 //############################################################################################
   99 {
  100   // Handler presence mark
  101   clrwdt();
  102 
  103   // Detect DPA event to handle
  104   switch ( GetDpaEvent() )
  105   {
  106     // -------------------------------------------------
  107     case DpaEvent_Interrupt:
  108       // Do an extra quick background interrupt work
  109       // ! 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.
  110       // ! 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.
  111       // ! 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.
  112       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  113       // ! Make sure race condition does not occur when accessing those variables at other places.
  114       // ! 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.
  115       // ! Do not call any OS functions except setINDFx().
  116       // ! Do not use any OS variables especially for writing access.
  117       // ! 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.
  118 
  119 DpaHandleReturnTRUE:
  120       return TRUE;
  121 
  122       // -------------------------------------------------
  123     case DpaEvent_Init:
  124       // Do a one time initialization before main loop starts
  125 
  126       // Initialize PINs to inputs and outputs
  127       TRISC.5 = 0;              // RC5 as output SDO (C8)
  128       TRISC.4 = 1;              // RC4 as input SDI (C7)
  129       TRISC.3 = 0;              // RC3 as output SCK (C6)
  130       TRISA.5 = 0;              // RA5 as output SS (C5)
  131 
  132       // TR module with connected pins?
  133       moduleInfo();
  134       if ( bufferINFO[5].7 == 0 )
  135       {
  136         // Yes
  137         TRISC.6 = 1;                // RC6 as input (connected to RA5 in parallel)
  138         TRISB.4 = 1;                // RB4 as input (connected to RA5 in parallel)
  139         TRISC.7 = 1;                // RC7 as input (connected to RC5 in parallel)
  140       }
  141 
  142       // Set idle level of SS
  143       SS_PIN = 1;
  144 
  145       // Setup SPI
  146       // SSPSTAT
  147       //  Transmit occurs on SCK rising edge
  148       CKE = 1;
  149       // SSPCON1: (SSPCON1 cannot be accessed directly due to OS restriction)
  150       //  SPI enabled
  151       //  Idle state for clock is a low level
  152       //  CLK = 250kHz @ 16 MHz
  153       writeToRAM( &SSPCON1, 0b0.0.1.0.0010 );
  154 
  155       break;
  156 
  157       // -------------------------------------------------
  158     case DpaEvent_DpaRequest:
  159       // Called to interpret DPA request for peripherals
  160       // -------------------------------------------------
  161       // Peripheral enumeration
  162       if ( IsDpaEnumPeripheralsRequest() )
  163       {
  164         // We implement 1 user peripheral
  165         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  166         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  167         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  168         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0x6789;
  169 
  170         goto DpaHandleReturnTRUE;
  171       }
  172       // -------------------------------------------------
  173       // Get information about peripheral
  174       else if ( IsDpaPeripheralInfoRequest() )
  175       {
  176         if ( _PNUM == PNUM_USER + 0 )
  177         {
  178           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  179           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  180           goto DpaHandleReturnTRUE;
  181         }
  182 
  183         break;
  184       }
  185       // -------------------------------------------------
  186       else
  187       {
  188         // Handle peripheral command
  189         if ( _PNUM == PNUM_USER + 0 )
  190         {
  191           // Check DPA command value
  192           if ( _PCMD != 0 )
  193             DpaApiReturnPeripheralError( ERROR_PCMD );
  194 
  195           // Check total data length
  196           if ( _DpaDataLength == 0 )
  197             DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  198 
  199           // FSR0 points to the next byte from DPA request
  200           FSR0 = _DpaMessage.Request.PData;
  201           // FSR1 points to the place for the next read byte
  202           FSR1 = bufferINFO;
  203           // Flag for keeping SS
  204           bit keepSS = FALSE;
  205           do
  206           {
  207             // Get subcommand type from the 1st byte
  208             uns8 cmd @ userReg0;
  209             cmd = *FSR0 & CMD_MASK;
  210             // Get data part from the 1st byte
  211             uns8 dataFromCmd @ userReg1;
  212             dataFromCmd = *FSR0++ & ~CMD_MASK;
  213 
  214             // Write and/or read subcommand?
  215             if ( cmd != CMD_DELAY )
  216             {
  217               if ( dataFromCmd == 0 )
  218                 // Keep SS after next SPI command
  219                 keepSS = TRUE;
  220               else
  221               {
  222                 // Yes!
  223                 // SS - active
  224                 SS_PIN = 0;
  225 
  226                 // Loop all the bytes
  227                 do
  228                 {
  229                   // Writing?
  230                   if ( cmd == CMD_WRITE || cmd == CMD_WRITE_READ )
  231                     // Yes, get the byte from request
  232                     W = *FSR0++;
  233                   else
  234                     // No data to write, write 0 instead
  235                     W = 0;
  236 
  237                   // Reset interrupt flag
  238                   SSPIF = 0;
  239                   // Write the byte
  240                   SSPBUF = W;
  241                   // Wait until the byte is transmitted/received
  242                   while ( !SSPIF );
  243 
  244                   // Reading?
  245                   if ( cmd == CMD_WRITE_READ || cmd == CMD_READ )
  246                   {
  247                     // Yes!
  248                     setINDF1( SSPBUF );
  249                     FSR1++;
  250                   }
  251                   // Next byte to write and/or read?
  252                 } while ( --dataFromCmd != 0 );
  253 
  254                 if ( !keepSS )
  255                   // SS - deactivate
  256                   SS_PIN = 1;
  257                 keepSS = FALSE;
  258               }
  259             }
  260             else
  261             {
  262               // Delay subcommand
  263               // Wait low8
  264               // WaitMS must not destroy FSRx registers (works well with current OS version)!
  265               waitMS( *FSR0++ );
  266               // Wait high6 * 256 ms
  267               for ( ; dataFromCmd != 0; dataFromCmd-- )
  268               {
  269                 waitMS( 256 / 2 );
  270                 clrwdt();
  271                 waitMS( 256 / 2 );
  272               }
  273             }
  274             // Another subcommand?
  275           } while ( FSR0.low8 - (uns8)_DpaMessage.Request.PData != _DpaDataLength );
  276 
  277           // Compute data length
  278           _DpaDataLength = FSR1.low8 - (uns8)bufferINFO;
  279           // Copy read bytes to the response
  280           copyMemoryBlock( bufferINFO, _DpaMessage.Response.PData, _DpaDataLength );
  281           // The previous statement can be replaced by shorter copyBufferINFO2RF(), because _DpaMessage.Response.PData == bufferRF
  282           goto DpaHandleReturnTRUE;
  283         }
  284       }
  285   }
  286 
  287   return FALSE;
  288 }
  289 
  290 //############################################################################################
  291 // 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)
  292 #include "DPAcustomHandler.h"
  293 //############################################################################################