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