1 // *********************************************************************
    2 //   Custom DPA Handler code - Handling buttons                        *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-Buttons.c,v $
    7 // Version: $Revision: 1.22 $
    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 //   2022/02/24  Release for DPA 4.17
   14 //   2019/01/10  Release for DPA 4.00
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 
   20 // Default IQRF include (modify the path according to your setup)
   21 #include "IQRF.h"
   22 
   23 // Default DPA header (modify the path according to your setup)
   24 #include "DPA.h"
   25 // Default Custom DPA Handler header (modify the path according to your setup)
   26 #include "DPAcustomHandler.h"
   27 
   28 //############################################################################################
   29 
   30 // Button descriptor
   31 typedef struct
   32 {
   33   // Timer to measure debouncing, recommended to be the 1st field to save the code
   34   uns8  Timer;
   35   // Last button state [must be initialized to 0xFF if the initial state of the button is not sure to be HIGH]
   36   uns8  LastState;
   37   // Button's PIC port: 0 = PORTA, 1 = PORTB, ... [must be initialized]
   38   uns8  Port;
   39   // Button's bit mask (only one bit must be set) [must be initialized]
   40   uns8  ButtonMask;
   41   // Debounce interval measured in the timer ticks + 1 [must be initialized]
   42   uns8  DebounceDelay;
   43   // 0 if button is HIGH, 1 if button is LOW
   44   uns8  ButtonIsLow;
   45 } TButton;
   46 
   47 // Handles the button and sets ButtonIsLow appropriately
   48 void HandleButton( uns16 pButton @ FSR0 );
   49 
   50 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   51 //############################################################################################
   52 bit CustomDpaHandler()
   53 //############################################################################################
   54 {
   55   // Handler presence mark
   56   clrwdt();
   57 
   58   // We demonstrate 2 buttons by 1 physical button but with different debouncing value
   59   static TButton ButtonFast, ButtonSlow;
   60 
   61   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
   62   switch ( GetDpaEvent() )
   63   {
   64     // -------------------------------------------------
   65     case DpaEvent_Interrupt:
   66       // Do an extra quick background interrupt work
   67       // ! 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.
   68       // ! 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.
   69       // ! 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.
   70       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   71       // ! Make sure race condition does not occur when accessing those variables at other places.
   72       // ! 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.
   73       // ! Do not call any OS functions except setINDFx().
   74       // ! Do not use any OS variables especially for writing access.
   75       // ! 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.
   76 
   77       //  If TMR6 interrupt occurred
   78       if ( TMR6IF )
   79       {
   80         // Unmask interrupt
   81         TMR6IF = FALSE;
   82 
   83         // Handle buttons
   84         HandleButton( &ButtonFast );
   85         HandleButton( &ButtonSlow );
   86 
   87         // Demonstration of the fast button
   88         if ( ButtonFast.ButtonIsLow.0 )
   89           setLEDR();
   90         else
   91           stopLEDR();
   92 
   93         // Demonstration of the slow button
   94         if ( ButtonSlow.ButtonIsLow.0 )
   95           setLEDG();
   96         else
   97           stopLEDG();
   98       }
   99 
  100       return Carry;
  101 
  102       // -------------------------------------------------
  103     case DpaEvent_Init:
  104       // Do a one time initialization before main loop starts
  105 
  106       // Initialize buttons' descriptors (CC5X does not support initialization of the static variables)
  107 
  108       // Fast button @ IQRF standard button
  109       ButtonFast.LastState = 0xFF;
  110       ButtonFast.Port = 1;                  // PORTB
  111       ButtonFast.ButtonMask = 0b0001.0000;  // PORTB.4
  112       ButtonFast.DebounceDelay = 2 + 1 /* x64ms */;
  113 
  114       // Slow button @ IQRF standard button
  115       ButtonSlow.LastState = 0xFF;
  116       ButtonSlow.Port = 1;                  // PORTB
  117       ButtonSlow.ButtonMask = 0b0001.0000;  // PORTB.4
  118       ButtonSlow.DebounceDelay = 15 + 1 /* x64ms */;
  119 
  120 #if defined( TR7xG )
  121       TMR6MD = 0;
  122       T6CON = 0b1.110.1111;
  123       //  Timer2/4/6 Clock Select bits = FOSC/4
  124       T6CLKCON |= 0b0000.0001;
  125 #else
  126       T6CON = 0b0.1111.1.11;
  127 #endif
  128       // Setup TMR6 for 64 ms interval
  129       _PR6 = 250 - 1;
  130       TMR6IE = TRUE;
  131       break;
  132 
  133       // -------------------------------------------------
  134     case DpaEvent_AfterSleep:
  135       // Called on wake-up from sleep
  136       TMR6IE = TRUE;
  137       _TMR6ON = TRUE;
  138       break;
  139 
  140       // -------------------------------------------------
  141     case DpaEvent_BeforeSleep:
  142       // Called before going to sleep (the same handling as DpaEvent_DisableInterrupts event)
  143       // -------------------------------------------------
  144     case DpaEvent_DisableInterrupts:
  145       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  146       // Must not use TMR6 any more
  147       _TMR6ON = FALSE;
  148       TMR6IE = FALSE;
  149       break;
  150   }
  151 
  152   return FALSE;
  153 }
  154 
  155 //############################################################################################
  156 void HandleButton( uns16 pButton @ FSR0 )
  157 //############################################################################################
  158 {
  159   // FSR0 points to the button's descriptor
  160   // (Local variables must be static as they are used inside interrupt routine)
  161 
  162   // Timer is active (non zero)?
  163   W = FSR0[offsetof( TButton, Timer )];
  164   if ( W != 0 ) // Note: must not modify W
  165   {
  166     // Yes, decrement the timer
  167     FSR0 += offsetof( TButton, Timer ); // Note: Timer is the 1st field of TButton so this generates no code and must not modify W
  168     setINDF0( W - 1 );
  169     FSR0 += -(int8)offsetof( TButton, Timer ); // Note: Timer is the 1st field of TButton so this generates no code, casting fixes CC5X bug
  170   }
  171 
  172   // FSR1 will point to the button's port
  173   FSR1L = FSR0[offsetof( TButton, Port )] + ( &PORTA & 0xFF );
  174   FSR1H = &PORTA >> 8;
  175 
  176   // Current state of the button, only the button bit is used, other bits are masked to zero
  177   static uns8 state;
  178   state = *FSR1 & FSR0[offsetof( TButton, ButtonMask )];
  179 
  180   // Button state changed?
  181   if ( ( state ^ FSR0[offsetof( TButton, LastState )] ) != 0 )
  182   {
  183     FSR0 += offsetof( TButton, Timer ); // Note: Timer is the 1st field of TButton so this generates no code and must not modify W
  184     // Initialize timer for debouncing
  185     setINDF0( FSR0[offsetof( TButton, DebounceDelay ) - offsetof( TButton, Timer )] );
  186     // Adjust pointer to LastState field
  187     FSR0 += (int8)( offsetof( TButton, LastState ) - offsetof( TButton, Timer ) );  // Note: casting fixes CC5X bug
  188     // Store the current state as the last one
  189     setINDF0( state );
  190     return; // Note: saves Flash by direct GOTO setINDF0
  191   }
  192 
  193   // Timer is almost over?
  194   if ( FSR0[offsetof( TButton, Timer )] != 1 )
  195     // No
  196     return;
  197 
  198   // The button state was stable for the debounce interval, prepare the pointer to store the new state
  199   FSR0 += offsetof( TButton, ButtonIsLow );
  200   // Ready for new state "button is HIGH"
  201   W = FALSE;
  202   // Is button LOW?
  203   if ( state == 0 ) // Note: must not modify W
  204     // Ready for new state "button is LOW"
  205     W = TRUE;
  206 
  207   // Set the button external state
  208   setINDF0( W );
  209 }
  210 
  211 //############################################################################################
  212 // 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)
  213 #include "DPAcustomHandler.h"
  214 //############################################################################################
  215