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.23 $
    8 // Date:    $Date: 2023/03/07 08:03:13 $
    9 //
   10 // Revision history:
   11 //   2023/03/07  Release for DPA 4.30
   12 //   2022/10/05  Release for DPA 4.18
   13 //   2017/03/13  Release for DPA 3.00
   14 //   2015/09/03  Release for DPA 2.21
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 
   20 // This example demonstrates usage of PWM and Timer at same time
   21 // It implements one user peripheral PNUM=0x20, PCMD=0x00
   22 // Data contains list of commands at the following format
   23 // 0x00, PWM
   24 //  Sets 8bit PWM duty cycle to value "PWM"
   25 // 0x01, loopLow8, loopHigh8, pwmAddlow8, pwmAddhigh8
   26 //  Adds 16bit "pwmAdd" value divided by 256 "loop" times. Time unit is approx 16 ms
   27 // 0x02
   28 //  Starts executing command from the beginning
   29 // 0x03
   30 //  Stops execution of commands
   31 
   32 // This example works only at STD mode, not at LP mode
   33 
   34 // Example data for PNUM=0x20, PCMD=0x00
   35 // 0x00, 0x00,
   36 //  - Sets PWM duty cycle to 0
   37 // 0x01, 0x00, 0x01, 0x40, 0x00,
   38 //  - Adds 0x0100 = 256 times every 16 ms value 0x0040 / 256 = 0.25 to the duty cycle
   39 // 0x01, 0x00, 0x01, 0xC0, 0xFF,
   40 //  - Adds 0x0100 = 256 times every 16 ms value 0xFFC0 / 256 = -0x0040 / 256 = -0.25 to the duty cycle
   41 // 0x01, 0x80, 0x00, 0x00, 0x00,
   42 //  - Adds 0x0800 = 128 times every 16 ms value 0 to the duty cycle (wait delay for approx 2 s)
   43 // 0x02
   44 //  - Starts again
   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 #if !defined( TR7xD )
   55 #error This example is intended for TR7xD
   56 #endif
   57 
   58 // PWM commands
   59 typedef enum
   60 {
   61   CMD_SET_PWM = 0,
   62   CMD_CHANGE_PWM = 1,
   63   CMD_LOOP = 2,
   64   CMD_STOP = 3
   65 };
   66 
   67 //############################################################################################
   68 
   69 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   70 //############################################################################################
   71 bit CustomDpaHandler()
   72 //############################################################################################
   73 {
   74   // Handler presence mark
   75   clrwdt();
   76 
   77   // List of commands
   78   static uns8 Cmds[40];
   79   // Index of the next command
   80   static uns8 CmdsIndex;
   81   // Loop for changing  PWM
   82   static uns16 ChangePWMloop;
   83   // Value to add to PWM during change
   84   static uns16 ChangePWMadd;
   85   // Full 16b PWM duty cycle value, only higher part is used at MCU
   86   static uns16 CCPR3Lshadow;
   87 
   88   // Detect DPA event to handle
   89   switch ( GetDpaEvent() )
   90   {
   91     // -------------------------------------------------
   92     case DpaEvent_Interrupt:
   93       // Do an extra quick background interrupt work
   94       // ! 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.
   95       // ! 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.
   96       // ! 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.
   97       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   98       // ! Make sure race condition does not occur when accessing those variables at other places.
   99       // ! 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.
  100       // ! Do not call any OS functions except setINDFx().
  101       // ! Do not use any OS variables especially for writing access.
  102       // ! 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.
  103 
  104       //  If TMR6 interrupt occurred
  105       if ( TMR6IF )
  106       {
  107         // Unmask interrupt
  108         TMR6IF = 0;
  109 
  110         // Changing PWM?
  111         if ( ChangePWMloop != 0 )
  112         {
  113           // Decrease loop
  114           ChangePWMloop--;
  115           // Add PWM
  116           CCPR3Lshadow += ChangePWMadd;
  117           // Copy to MCU
  118           CCPR3L = CCPR3Lshadow.high8;
  119         }
  120         else
  121         {
  122           // Get command pointer
  123           FSR0 = Cmds + CmdsIndex;
  124           switch ( FSR0[0] )
  125           {
  126             // Set PWM
  127             case CMD_SET_PWM:
  128               // Next cmd
  129               CmdsIndex += 1 + 1;
  130               // Get value
  131               CCPR3Lshadow.high8 = CCPR3L = FSR0[1];
  132               CCPR3Lshadow.low8 = 0;
  133               break;
  134 
  135               // Change PWM
  136             case CMD_CHANGE_PWM:
  137               // Next cmd
  138               CmdsIndex += 1 + 4;
  139               // Get loop count
  140               ChangePWMloop.low8 = FSR0[1];
  141               ChangePWMloop.high8 = FSR0[2];
  142               // Get added value
  143               ChangePWMadd.low8 = FSR0[3];
  144               ChangePWMadd.high8 = FSR0[4];
  145               break;
  146 
  147               // Loop commands
  148             case CMD_LOOP:
  149               // Start from the 1st command
  150               CmdsIndex = 0;
  151               break;
  152 
  153               // Stop commands
  154             case CMD_STOP:
  155             default:
  156               break;
  157           }
  158         }
  159       }
  160       return Carry;
  161 
  162       // -------------------------------------------------
  163     case DpaEvent_Init:
  164       // Do a one time initialization before main loop starts
  165 
  166       // Start with no commands
  167       Cmds[0] = CMD_STOP;
  168       // CmdsIndex = 0 // By C definition
  169 
  170       // Definitions used for TR72 having connected pins
  171 #define _OUT_A5 TRISA.5
  172 #define _OUT_B4 TRISB.4
  173 #define _OUT_C6 TRISC.6
  174 #define _PIN_C6 LATC.6
  175 
  176       // Read module info into bufferINFO
  177       moduleInfo();
  178       // TR module with connected pins?
  179       if ( bufferINFO[5].7 == 0 )
  180       {
  181         _OUT_A5 = 1;
  182         _OUT_B4 = 1;
  183       }
  184 
  185       // Single output; PxA modulated; PxB, PxC, PxD assigned as port pins
  186       // PWM mode: PxA, PxC active-high; PxB, PxD active-high
  187       CCP3CON = 0b00.00.1100;
  188       // PWM duty cycle
  189       CCPR3L = 0;
  190       // Period
  191       PR6 = 0xff;
  192       // CCP3 is based off Timer6 in PWM mode
  193       CCPTMRS0 = 0b00100000;
  194       // CCP3/P3A function is CCP3
  195       CCP3SEL = 0;
  196 #if 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, LoadCode, Remove bond, and Run 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 implementing a Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup)
  295 #include "DPAcustomHandler.h"
  296 //############################################################################################