1 // **************************************************************************
    2 //   Custom DPA Handler code example - User peripheral implementation - PWM *
    3 // **************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-UserPeripheral-PWMandTimer.c,v $
    7 // Version: $Revision: 1.15 $
    8 // Date:    $Date: 2020/02/20 17:18:58 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2015/09/03  Release for DPA 2.21
   13 //
   14 // *********************************************************************
   15 
   16 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   17 
   18 // This example demonstrates usage of PWM and Timer at same time
   19 // It implements one user peripheral PNUM=0x20, PCMD=0x00
   20 // Data contains list of commands at the following format
   21 // 0x00, PWM
   22 //  Sets 8bit PWM duty cycle to value "PWM"
   23 // 0x01, loopLow8, loopHigh8, pwmAddlow8, pwmAddhigh8
   24 //  Adds 16bit "pwmAdd" value divided by 256 "loop" times. Time unit is approx 16 ms
   25 // 0x02
   26 //  Starts executing command from the beginning
   27 // 0x03
   28 //  Stops execution of commands
   29 
   30 // This example works only at STD mode, not at LP mode
   31 
   32 // Example data for PNUM=0x20, PCMD=0x00
   33 // 0x00, 0x00,
   34 //  - Sets PWM duty cycle to 0
   35 // 0x01, 0x00, 0x01, 0x40, 0x00,
   36 //  - Adds 0x0100 = 256 times every 16 ms value 0x0040 / 256 = 0.25 to the duty cycle
   37 // 0x01, 0x00, 0x01, 0xC0, 0xFF,
   38 //  - Adds 0x0100 = 256 times every 16 ms value 0xFFC0 / 256 = -0x0040 / 256 = -0.25 to the duty cycle
   39 // 0x01, 0x80, 0x00, 0x00, 0x00,
   40 //  - Adds 0x0800 = 128 times every 16 ms value 0 to the duty cycle (wait delay for approx 2 s)
   41 // 0x02
   42 //  - Starts again
   43 
   44 // Default IQRF include (modify the path according to your setup)
   45 #include "IQRF.h"
   46 
   47 // Default DPA header (modify the path according to your setup)
   48 #include "DPA.h"
   49 // Default Custom DPA Handler header (modify the path according to your setup)
   50 #include "DPAcustomHandler.h"
   51 
   52 // PWM commands
   53 typedef enum
   54 {
   55   CMD_SET_PWM = 0,
   56   CMD_CHANGE_PWM = 1,
   57   CMD_LOOP = 2,
   58   CMD_STOP = 3
   59 };
   60 
   61 //############################################################################################
   62 
   63 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   64 //############################################################################################
   65 bit CustomDpaHandler()
   66 //############################################################################################
   67 {
   68   // Handler presence mark
   69   clrwdt();
   70 
   71   // List of commands
   72   static uns8 Cmds[40];
   73   // Index of the next command
   74   static uns8 CmdsIndex;
   75   // Loop for changing  PWM
   76   static uns16 ChangePWMloop;
   77   // Value to add to PWM during change
   78   static uns16 ChangePWMadd;
   79   // Full 16b PWM duty cycle value, only higher part is used at MCU
   80   static uns16 CCPR3Lshadow;
   81 
   82   // Detect DPA event to handle
   83   switch ( GetDpaEvent() )
   84   {
   85     // -------------------------------------------------
   86     case DpaEvent_Interrupt:
   87       // Do an extra quick background interrupt work
   88       // ! 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.
   89       // ! 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.
   90       // ! 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.
   91       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   92       // ! Make sure race condition does not occur when accessing those variables at other places.
   93       // ! 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.
   94       // ! Do not call any OS functions except setINDFx().
   95       // ! Do not use any OS variables especially for writing access.
   96       // ! 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.
   97 
   98       //  If TMR6 interrupt occurred
   99       if ( TMR6IF )
  100       {
  101         // Unmask interrupt
  102         TMR6IF = 0;
  103 
  104         // Changing PWM?
  105         if ( ChangePWMloop != 0 )
  106         {
  107           // Decrease loop
  108           ChangePWMloop--;
  109           // Add PWM
  110           CCPR3Lshadow += ChangePWMadd;
  111           // Copy to MCU
  112           CCPR3L = CCPR3Lshadow.high8;
  113         }
  114         else
  115         {
  116           // Get command pointer
  117           FSR0 = Cmds + CmdsIndex;
  118           switch ( FSR0[0] )
  119           {
  120             // Set PWM
  121             case CMD_SET_PWM:
  122               // Next cmd
  123               CmdsIndex += 1 + 1;
  124               // Get value
  125               CCPR3Lshadow.high8 = CCPR3L = FSR0[1];
  126               CCPR3Lshadow.low8 = 0;
  127               break;
  128 
  129               // Change PWM
  130             case CMD_CHANGE_PWM:
  131               // Next cmd
  132               CmdsIndex += 1 + 4;
  133               // Get loop count
  134               ChangePWMloop.low8 = FSR0[1];
  135               ChangePWMloop.high8 = FSR0[2];
  136               // Get added value
  137               ChangePWMadd.low8 = FSR0[3];
  138               ChangePWMadd.high8 = FSR0[4];
  139               break;
  140 
  141               // Loop commands
  142             case CMD_LOOP:
  143               // Start from the 1st command
  144               CmdsIndex = 0;
  145               break;
  146 
  147               // Stop commands
  148             case CMD_STOP:
  149             default:
  150               break;
  151           }
  152         }
  153       }
  154       return Carry;
  155 
  156       // -------------------------------------------------
  157     case DpaEvent_Init:
  158       // Do a one time initialization before main loop starts
  159 
  160       // Start with no commands
  161       Cmds[0] = CMD_STOP;
  162       // CmdsIndex = 0 // By C definition
  163 
  164       // Definitions used for TR72 having connected pins
  165 #define _OUT_A5 TRISA.5
  166 #define _OUT_B4 TRISB.4
  167 #define _OUT_C6 TRISC.6
  168 #define _PIN_C6 LATC.6
  169 
  170       // Read module info into bufferINFO
  171       moduleInfo();
  172       // TR module with connected pins?
  173       if ( bufferINFO[5].7 == 0 )
  174       {
  175         _OUT_A5 = 1;
  176         _OUT_B4 = 1;
  177       }
  178 
  179       // Single output; PxA modulated; PxB, PxC, PxD assigned as port pins
  180       // PWM mode: PxA, PxC active-high; PxB, PxD active-high
  181       CCP3CON = 0b00.00.1100;
  182       // PWM duty cycle
  183       CCPR3L = 0;
  184       // Period
  185       PR6 = 0xff;
  186       // CCP3 is based off Timer6 in PWM mode
  187       CCPTMRS0 = 0b00100000;
  188       // CCP3/P3A function is CCP3
  189       CCP3SEL = 0;
  190 #if F_OSC == 16000000
  191       // Prescaler 16, Postscaler 8, 16 * 16 * 256 = 65536 = 61 Hz = 16 ms @16MHz
  192       // TMR6 on
  193       T6CON = 0b0.1111.1.10;
  194 #else
  195 #error Unsupported oscillator frequency
  196 #endif
  197 
  198       // TMR6 interrupt
  199       TMR6IE = 1;
  200       // Set output
  201       _OUT_C6 = 0;
  202 
  203       break;
  204 
  205       // -------------------------------------------------
  206     case DpaEvent_AfterSleep:
  207       // Called on wake-up from sleep
  208       TMR6IE = TRUE;
  209       TMR6ON = TRUE;
  210       break;
  211 
  212       // -------------------------------------------------
  213     case DpaEvent_BeforeSleep:
  214       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  215 
  216       // -------------------------------------------------
  217     case DpaEvent_DisableInterrupts:
  218       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  219       // Must not use TMR6 any more
  220       TMR6ON = FALSE;
  221       TMR6IE = FALSE;
  222       _PIN_C6 = FALSE;
  223       break;
  224 
  225       // -------------------------------------------------
  226     case DpaEvent_DpaRequest:
  227       // Called to interpret DPA request for peripherals
  228       // -------------------------------------------------
  229       // Peripheral enumeration
  230       if ( IsDpaEnumPeripheralsRequest() )
  231       {
  232         // We implement 1 user peripheral
  233         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  234         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  235         _DpaMessage.EnumPeripheralsAnswer.HWPID = 0x000F;
  236         _DpaMessage.EnumPeripheralsAnswer.HWPIDver = 0xFeFe;
  237 
  238 DpaHandleReturnTRUE:
  239         return TRUE;
  240       }
  241       // -------------------------------------------------
  242       // Get information about peripheral
  243       else if ( IsDpaPeripheralInfoRequest() )
  244       {
  245         if ( _PNUM == PNUM_USER + 0 )
  246         {
  247           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  248           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_WRITE;
  249           goto DpaHandleReturnTRUE;
  250         }
  251 
  252         break;
  253       }
  254       // -------------------------------------------------
  255       else
  256       {
  257         // Handle peripheral command
  258         if ( _PNUM == PNUM_USER + 0 )
  259         {
  260           // Check command
  261           if ( _PCMD != 0 )
  262             DpaApiReturnPeripheralError( ERROR_PCMD );
  263 
  264           // Check data length
  265           if ( _DpaDataLength > ( sizeof( Cmds ) - 1 ) )
  266             DpaApiReturnPeripheralError( ERROR_DATA_LEN );
  267 
  268           // Copy commands and always append CMD_STOP for sure
  269           GIE = FALSE;
  270           copyMemoryBlock( _DpaMessage.Request.PData, Cmds, sizeof( Cmds ) - 1 );
  271           writeToRAM( Cmds + _DpaDataLength, CMD_STOP );
  272           // Start new commands
  273           CmdsIndex = 0;
  274           ChangePWMloop = 0;
  275           GIE = TRUE;
  276 
  277           // Write no error
  278           _DpaDataLength = 0;
  279           goto DpaHandleReturnTRUE;
  280         }
  281       }
  282   }
  283 
  284   return FALSE;
  285 }
  286 
  287 //############################################################################################
  288 // 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) 
  289 #include "DPAcustomHandler.h"
  290 //############################################################################################