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