1 // ***********************************************************************************
    2 //   Custom DPA Handler code example - User peripheral implementation - i2c          *
    3 // ***********************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UserPeripheral-i2c.c,v $
    7 // Version: $Revision: 1.16 $
    8 // Date:    $Date: 2018/01/03 09:55:12 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2015/12/01  Release for DPA 2.24
   13 //   2015/08/05  Release for DPA 2.20
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   18 
   19 // This example implements the user peripheral reading from MCP9802 at DDC-SE-01
   20 // PNUM = 0x20 and PCMD = 0 returns 2 bytes with result read from MCP9802
   21 // Based on example DDC-SE-01-i2c.c
   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 //############################################################################################
   32 
   33 void i2c_init();
   34 void i2c_shutdown();
   35 void i2c_waitForIdle();
   36 void i2c_start();
   37 void i2c_repStart();
   38 void i2c_stop();
   39 uns8 i2c_read( bit ack );
   40 void i2c_write( uns8 i2cWriteData );
   41 
   42 #define I2C_ADR             0b10010110
   43 #define PWR_SENSOR_TRIS     TRISC.7
   44 #define PWR_SENSOR_IO       LATC.7
   45 
   46 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   47 //############################################################################################
   48 bit CustomDpaHandler()
   49 //############################################################################################
   50 {
   51   // Handler presence mark
   52   clrwdt();
   53 
   54   // Detect DPA event to handle
   55   switch ( GetDpaEvent() )
   56   {
   57     // -------------------------------------------------
   58     case DpaEvent_Interrupt:
   59       // Do an extra quick background interrupt work
   60       // ! 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.
   61       // ! 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.
   62       // ! 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.
   63       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   64       // ! Make sure race condition does not occur when accessing those variables at other places.
   65       // ! 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.
   66       // ! Do not call any OS functions except setINDFx().
   67       // ! Do not use any OS variables especially for writing access.
   68       // ! 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.
   69 
   70 DpaHandleReturnTRUE:
   71       return TRUE;
   72 
   73       // -------------------------------------------------
   74     case DpaEvent_Init:
   75       // Do a one time initialization work before main loop starts
   76     case DpaEvent_AfterSleep:
   77       // Called after woken up after sleep
   78 
   79       i2c_init();
   80       break;
   81 
   82       // -------------------------------------------------
   83     case DpaEvent_BeforeSleep:
   84       // Called before going to sleep
   85 
   86       i2c_shutdown();
   87       break;
   88 
   89       // -------------------------------------------------
   90     case DpaEvent_DpaRequest:
   91       // Called to interpret DPA request for peripherals
   92       // -------------------------------------------------
   93       // Peripheral enumeration
   94       if ( IsDpaEnumPeripheralsRequest() )
   95       {
   96         // We implement 1 user peripheral
   97         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
   98         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
   99         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  100         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0xAbCd;
  101 
  102         goto DpaHandleReturnTRUE;
  103       }
  104       // -------------------------------------------------
  105       // Get information about peripheral
  106       else if ( IsDpaPeripheralInfoRequest() )
  107       {
  108         if ( _PNUM == PNUM_USER + 0 )
  109         {
  110           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  111           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  112           goto DpaHandleReturnTRUE;
  113         }
  114 
  115         break;
  116       }
  117       // -------------------------------------------------
  118       else
  119       {
  120         // Handle peripheral command
  121         if ( _PNUM == PNUM_USER + 0 )
  122         {
  123           // Check command
  124           switch ( _PCMD )
  125           {
  126             case 0:
  127               // -------------------------------------------------
  128               // Read temperature
  129               if ( _DpaDataLength != 0 )
  130                 DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  131 
  132               i2c_start();
  133               i2c_write( I2C_ADR );                 // MCP9802 address
  134               i2c_write( 0x01 );                    // pointer: 1 = configuration register
  135               i2c_write( 0x60 );                    // configuration: 12-bit ADC
  136               i2c_stop();
  137 
  138               i2c_start();
  139               i2c_write( I2C_ADR );                 // MCP9802 address
  140               i2c_write( 0 );                       // pointer: 0 = temperature
  141               i2c_stop();
  142 
  143               i2c_start();
  144               i2c_write( I2C_ADR | 1 );             // MCP9802 address + read
  145               uns16 temperature  @ _DpaMessage.Response.PData;
  146               temperature.high8 = i2c_read( TRUE );  // store the result
  147               temperature.low8 = i2c_read( FALSE );
  148               i2c_stop();
  149 
  150               _DpaDataLength = sizeof( temperature );
  151               goto DpaHandleReturnTRUE;
  152 
  153             default:
  154               // -------------------------------------------------
  155               // Invalid command
  156               DpaApiReturnPeripheralError( ERROR_PCMD );
  157           }
  158         }
  159       }
  160   }
  161 
  162   return FALSE;
  163 }
  164 
  165 //############################################################################################
  166 
  167 #pragma library 1   // Compile only used functions
  168 
  169 // *********************************************************************
  170 
  171 void writeToSSPCON2( uns8 value @ param4.high8 )
  172 {
  173   writeToRAM( &SSPCON2, value );
  174 }
  175 //----------------------------------------------------------------------
  176 
  177 void writeOredToSSPCON2( uns8 value @ param4.high8 )
  178 {
  179   writeToSSPCON2( SSPCON2 | value );
  180 }
  181 
  182 //----------------------------------------------------------------------
  183 
  184 void i2c_init()
  185 {
  186   PORTC = 0x80;                       // port
  187 
  188   TRISC.3 = 1;                        // SCL as input (SIM C6)
  189   TRISC.4 = 1;                        // SDA as input (SIM C7)
  190 
  191   PWR_SENSOR_TRIS = 0;                // sensor power as output (SIM C8)
  192   TRISC.5 = 1;                        // shared with SIM C8
  193 
  194   TRISA.5 = 1;                        // sensor ALERT as input (SIM C5)
  195   TRISB.4 = 1;                        // shared with SIM C5
  196   TRISC.6 = 1;                        // shared with SIM C5
  197 
  198   writeToRAM( &SSPCON1, 0x38 );       // I2C master mode     SSPCON = 0b00111000
  199   writeToSSPCON2( 0x00 );
  200 
  201   SSPADD = ( F_OSC / 50000 / 4 ) - 2; // 50 kHz SCL frequency
  202 
  203   SMP = 1;                            // Disable slew rate control
  204 }
  205 
  206 //------------------------------------------------------------------------
  207 
  208 void i2c_shutdown()
  209 {
  210   writeToRAM( &SSPCON1, 0x00 );       // I2C master mode     SSPCON = 0
  211 }
  212 
  213 //------------------------------------------------------------------------
  214 void i2c_waitForIdle()
  215 {
  216   while ( SSPCON2 & 0x1F );           // Wait for idle and not writing
  217   while ( RW_ );                      // Wait for idle and not writing
  218 }
  219 //----------------------------------------------------------------------
  220 
  221 void i2c_start()
  222 {
  223   i2c_waitForIdle();
  224   writeOredToSSPCON2( 0x01 );       // SEN = 1
  225 }
  226 //----------------------------------------------------------------------
  227 
  228 void i2c_repStart()
  229 {
  230   i2c_waitForIdle();
  231   writeOredToSSPCON2( 0x02 );       // RSEN = 1
  232 }
  233 //----------------------------------------------------------------------
  234 
  235 void i2c_stop()
  236 {
  237   i2c_waitForIdle();
  238   writeOredToSSPCON2( 0x04 );       // PEN = 1
  239 }
  240 //----------------------------------------------------------------------
  241 
  242 uns8 i2c_read( bit ack )
  243 {
  244   i2c_waitForIdle();
  245   writeOredToSSPCON2( 0x08 );       // RCEN = 1
  246 
  247   i2c_waitForIdle();
  248 
  249   uns8 i2cReadData @ userReg0;
  250   i2cReadData = SSPBUF;
  251 
  252   i2c_waitForIdle();
  253 
  254   if ( ack )
  255     writeToSSPCON2( SSPCON2 & 0xDF ); // Acknowledge, ACKDT = 0
  256   else
  257     writeOredToSSPCON2( 0x20 );       // Not acknowledge, ACKDT = 1 
  258 
  259   writeOredToSSPCON2( 0x10 );         // Send acknowledge sequence, ACKEN = 1 
  260   return i2cReadData;
  261 }
  262 //----------------------------------------------------------------------
  263 
  264 void i2c_write( uns8 i2cWriteData @ param2 )
  265 {
  266   i2c_waitForIdle();
  267   SSPBUF = i2cWriteData;
  268 }
  269 
  270 // ############################################################################################
  271 #pragma library 0   // Compile all
  272 //############################################################################################
  273 // 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) 
  274 #include "DPAcustomHandler.h"
  275 //############################################################################################