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