1 // *********************************************************************************
    2 //   Custom DPA Handler code example - User peripheral implementation - I2C master *
    3 // *********************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UserPeripheral-I2Cmaster.c,v $
    7 // Version: $Revision: 1.20 $
    8 // Date:    $Date: 2023/03/16 10:27:10 $
    9 //
   10 // Revision history:
   11 //   2022/02/24  Release for DPA 4.17
   12 //   2021/08/20  Release for DPA 4.16
   13 //   2020/01/02  Release for DPA 4.11
   14 //   2019/10/09  Release for DPA 4.10
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 
   20 // This example implements Master I2C 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  I2C master is controlled by DPA Request data sent to the user peripheral PNUM=0x20, PCMD=0x00.
   32  The command allows to (optionally) write bytes to the I2C bus and then (optionally) read bytes from the bus.
   33 
   34  *** DPA Request format ***
   35   Byte index | Description
   36   ---------------------------------------------------------------------------------------------------------------------------
   37   0          | I2C 8-bit device address to write/read data to/from (bit.0 of the address is set when reading from the I2C bus).
   38   1          | Number of bytes to read from I2C after data are written to the I2C bus.
   39   2-55       | Optional bytes to write to the I2C before data are read from I2C bus.
   40 
   41   *** DPA Response format ***
   42   Byte index | Description
   43   ---------------------------------------------------------------------------------------------------------------------------
   44   0-55       | Bytes read from I2C bus. If no bytes are read, no data is returned i.e. DPA Response is empty.
   45 
   46 
   47   *** Example #1 ***
   48   Configuring MCP9802 temperature sensor in the DDC-SE-01 connected to the DK-EVAL-04x for the 12-bit ADC, i.e. 0.0625 °C resolution.
   49 
   50   DPA Request:
   51   Byte index | Value [hex] | Description
   52   ---------------------------------------------------------------------------------------------------------------------------
   53   0          | 96          | MCP9802 I2C 8-bit address
   54   1          | 00          | Read no data
   55   2          | 01          | Write MCP9802 configuration register address
   56   3          | 60          | Write MCP9802 configuration register value: 12-bit ADC, i.e. 0.0625°C resolution
   57 
   58   DPA Response:
   59   Empty as no data is read.
   60 
   61 
   62   *** Example #2 ***
   63   Reading temperature from  MCP9802 temperature sensor in the DDC-SE-01 connected to the DK-EVAL-04x.
   64 
   65   DPA Request:
   66   Byte index | Value [hex] | Description
   67   ---------------------------------------------------------------------------------------------------------------------------
   68   0          | 96          | MCP9802 I2C 8-bit address
   69   1          | 02          | Read 2 bytes with the temperature value
   70   2          | 00          | Write MCP9802 ambient temperature register address
   71 
   72   DPA Response:
   73   Byte index | Value [hex] | Description
   74   ---------------------------------------------------------------------------------------------------------------------------
   75   0          | 17          | Upper half of the temperature register
   76   1          | E0          | Lower half of the temperature register (0x17E0 = 23.875 °C)
   77 
   78 *********************************************************************
   79 
   80   I2C signals to PIN assignment:
   81 
   82     TR module pin | DK-EVAL-04x pin | I2C
   83     -------------------------------------
   84     C7            | 2               | SDA
   85     C6            | 3               | SCL
   86     C4            | 7               | GND
   87 
   88   ! Do not forget to connect pull-up resistors (e.g. 10k) between SDA and SCL and Vcc = 3 Volts preferably at I2C master side!
   89 
   90 *********************************************************************/
   91 
   92 // I2C SCL frequency [Hz]
   93 #define I2Cfrequency                100000
   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   // I2C master peripheral command data structure
  101   typedef struct
  102   {
  103     uns8  I2Caddress;
  104     uns8  ReadDataCount;
  105     uns8  WriteData[DPA_MAX_DATA_LENGTH - 2 * sizeof( uns8 )];
  106   } TI2Crequest;
  107 
  108   // Handler presence mark
  109   clrwdt();
  110 
  111   // Detect DPA event to handle
  112   switch ( GetDpaEvent() )
  113   {
  114     // -------------------------------------------------
  115     case DpaEvent_Interrupt:
  116       // Do an extra quick background interrupt work
  117       return Carry;
  118 
  119       // -------------------------------------------------
  120     case DpaEvent_DpaRequest:
  121       // Called to interpret DPA request for peripherals
  122       // -------------------------------------------------
  123       // Peripheral enumeration
  124       if ( IsDpaEnumPeripheralsRequest() )
  125       {
  126         // We implement 1 user peripheral
  127         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |= 1;
  128         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  129         _DpaMessage.EnumPeripheralsAnswer.HWPID |= 0x123F;
  130         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0xABCD;
  131 
  132 DpaHandleReturnTRUE:
  133         return TRUE;
  134       }
  135       // -------------------------------------------------
  136       // Get information about peripheral
  137       else if ( IsDpaPeripheralInfoRequest() )
  138       {
  139         if ( _PNUM == PNUM_USER + 0 )
  140         {
  141           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  142           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  143           goto DpaHandleReturnTRUE;
  144         }
  145 
  146         break;
  147       }
  148       // -------------------------------------------------
  149       else
  150       {
  151         // Handle peripheral command
  152         if ( _PNUM == PNUM_USER + 0 && _PCMD == 0 )
  153         {
  154           // Check for minimum request data length
  155           if ( _DpaDataLength < offsetof( TI2Crequest, WriteData ) )
  156             DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  157 
  158           if ( FSR0[offsetof( TI2Crequest, ReadDataCount )] > DPA_MAX_DATA_LENGTH )
  159             DpaApiReturnPeripheralError( ERROR_DATA );
  160 
  161           // Looping variable
  162           uns8 loop;
  163           // Count of bytes to write to I2C
  164           loop = _DpaDataLength - offsetof( TI2Crequest, WriteData );
  165           // Anything to write to I2C?
  166           if ( loop != 0 )
  167           {
  168             // Start I2C writing
  169             _DpaApiI2Cstart( FSR1[offsetof( TI2Crequest, I2Caddress )] );
  170             // Pointer to the data to write
  171             FSR1 += offsetof( TI2Crequest, WriteData );
  172             // Now write byte by byte to the I2C
  173             do
  174             {
  175               DpaApiI2Cwrite( *FSR1++ );
  176             } while ( --loop != 0 );
  177             // Stop I2C
  178             _DpaApiI2Cstop();
  179           }
  180 
  181           // Number of bytes to read from I2C
  182           loop = _DpaDataLength = FSR0[offsetof( TI2Crequest, ReadDataCount )];
  183           // Anything to read from I2C?
  184           if ( loop == 0 )
  185           {
  186             // No, return with empty DPA response
  187             goto DpaHandleReturnTRUE;
  188           }
  189 
  190           // Start I2C reading
  191           _DpaApiI2Cstart( FSR0[offsetof( TI2Crequest, I2Caddress )] | 1 );
  192           // Read byte by byte from I2C
  193           do
  194           {
  195             // Only very last read byte is not acknowledged
  196             setINDF0( DpaApiI2Cread( loop != 1 ) );
  197             FSR0++;
  198           } while ( --loop != 0 );
  199           // Stop I2C
  200           _DpaApiI2Cstop();
  201 
  202           goto DpaHandleReturnTRUE;
  203         }
  204         break;
  205       }
  206 
  207       // -------------------------------------------------
  208     case DpaEvent_Init:
  209       // Do a one time initialization before main loop starts
  210       // Fall through!
  211       // -------------------------------------------------
  212     case DpaEvent_AfterSleep:
  213       // Called after woken up after sleep
  214       // Enable I2C master
  215 
  216 #if defined( TR7xG )
  217         // Do PPS for I2C
  218       unlockPPS();
  219       SSP1CLKPPS = 0x13;  // RC3
  220       SSP1DATPPS = 0x14;  // RC4
  221       RC3PPS = 0x14;      // SCK1/SCL1
  222       RC4PPS = 0x15;      // SD01/SDA1
  223       lockPPS();
  224 #endif
  225 
  226       DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  227       return Carry;
  228 
  229       // -------------------------------------------------
  230     case DpaEvent_BeforeSleep:
  231       // Called before going to sleep
  232       // Disable I2C master
  233       DpaApiI2Cshutdown();
  234       return Carry;
  235   }
  236 
  237   return FALSE;
  238 }
  239 
  240 //############################################################################################
  241 // 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)
  242 #include "DPAcustomHandler.h"
  243 //############################################################################################