/*********************************************************************
* Filename: LPMwakeup.c                                                
*                                                                    
* Description: This example illustrates the ability of the CAN module to enter
  and exit low-power mode  (automatically).

  The PDR bit of node A is set to force it into low-power mode (LPM). The PDA
  bit is read back to ensure that it is indeed in low-power mode. 
  An attempt is made to write to mailbox 9 RAM while the CAN module
  is in low-power mode, to verify that the write does not go through.
  After a while, node B transmits a packet to "wake-up" node A. The PDA bit
  is again read back to ensure that node A is indeed out of low-power mode.
  After wake up, WUIFn bit must be set as appropriate
  
*********************************************************************/

//
// Included Files
//
#include "DSP28x_Project.h"     // Device Header file and Examples Include File

// Variable declarations

int PDA_STATUS = 0;
int int0count = 0;		// Counter to track the # of level 0 interrupts
int int1count = 0;	    // Counter to track the # of level 1 interrupts
long      i;

// Prototype statements for functions found within this file.

interrupt void eCAN0INT_ISR(void);
interrupt void eCAN1INT_ISR(void);

/* Create a shadow register structure for the CAN control registers. This is
 needed, since, only 32-bit access is allowed to these registers. 16-bit access
 to these registers could potentially corrupt the register contents. This is
 especially true while writing to a bit (or group of bits) among bits 16 - 31 */

struct ECAN_REGS ECanaShadow;
struct ECAN_REGS ECanbShadow;

main() 
{

    /* Kill Watchdog, Init PLL, Enable peripheral clocks */

        InitSysCtrl();

    /* Initialize the CAN module */

        InitECan();
        InitECanGpio();

        EALLOW;
	
/* Initialize PIE vector table To a Known State: */
	// The PIE vector table is initialized with pointers to shell "Interrupt 
    // Service Routines (ISR)".  The shell routines are found in DSP28_DefaultIsr.c.
	// Insert user specific ISR code in the appropriate shell ISR routine in 
    // the DSP28_DefaultIsr.c file.
    
    // InitPieVectTable();	 // uncomment this line if the shell ISR routines are needed
    
    // This function is found in DSP28_PieVect.c. It populates the PIE vector table
    // with pointers to the shell ISR functions found in DSP28_DefaultIsr.c. This 
    // function is not useful in this code because the user-specific ISR is present
    // in this file itself. The shell ISR routine in the DSP28_DefaultIsr.c file is
    // not used. If the shell ISR routines are needed, uncomment this line and add 
    // DSP28_PieVect.c & DSP28_DefaultIsr.c files to the project

/* Disable and clear all CPU interrupts: */

	DINT;
	IER = 0x0000;
	IFR = 0x0000;

/* Initialize Pie Control Registers To Default State */
        
	InitPieCtrl(); // This function is found in the DSP28_PieCtrl.c file. 
    
/* Write to the MSGID field  */    

    ECanaMboxes.MBOX0.MSGID.all  = 0x9FFFFF00; // MBX0 is a Tx MBX
    ECanaMboxes.MBOX1.MSGID.all  = 0x9FFFFF01; // MBX1 is a Tx MBX
    ECanaMboxes.MBOX2.MSGID.all  = 0x9FFFFF02; // MBX2 is a Tx MBX
    ECanaMboxes.MBOX3.MSGID.all  = 0x9FFFFF03; // MBX3 is a Tx MBX
    ECanaMboxes.MBOX4.MSGID.all  = 0x9FFFFF04; // MBX9 is a Tx MBX
    ECanaMboxes.MBOX5.MSGID.all  = 0x9FFFFF05; // MBX5 is a Tx MBX
    ECanaMboxes.MBOX6.MSGID.all  = 0x9FFFFF06; // MBX6 is a Tx MBX
    ECanaMboxes.MBOX7.MSGID.all  = 0x9FFFFF07; // MBX7 is a Tx MBX
    ECanaMboxes.MBOX8.MSGID.all  = 0x9FFFFF08; // MBX8 is the RCV MBX
    ECanaMboxes.MBOX9.MSGID.all  = 0x9FFFFF09; // MBX9 is a Tx MBX

/* Enable Mailboxes under test */
	
	ECanaShadow.CANME.all = 0;	
	ECanaShadow.CANME.bit.ME0 = 1;
	ECanaShadow.CANME.bit.ME1 = 1;
	ECanaShadow.CANME.bit.ME2 = 1;
	ECanaShadow.CANME.bit.ME3 = 1;
	ECanaShadow.CANME.bit.ME4 = 1;
	ECanaShadow.CANME.bit.ME5 = 1;
	ECanaShadow.CANME.bit.ME6 = 1;
	ECanaShadow.CANME.bit.ME7 = 1;
	ECanaShadow.CANME.bit.ME8 = 1;
	ECanaShadow.CANME.bit.ME9 = 1;	
	ECanaRegs.CANME.all = ECanaShadow.CANME.all; 

/* Write to the mailbox RAM a known data */

	ECanaMboxes.MBOX0.MDH.all = 0x00000000;
	ECanaMboxes.MBOX0.MDL.all = 0x00000000;
	ECanaMboxes.MBOX1.MDH.all = 0x11111111;
	ECanaMboxes.MBOX1.MDL.all = 0x11111111;
	ECanaMboxes.MBOX2.MDH.all = 0x22222222;
	ECanaMboxes.MBOX2.MDL.all = 0x22222222;
	ECanaMboxes.MBOX3.MDH.all = 0x33333333;
	ECanaMboxes.MBOX3.MDL.all = 0x33333333;
	ECanaMboxes.MBOX4.MDH.all = 0x44444444;
	ECanaMboxes.MBOX4.MDL.all = 0x44444444;
	ECanaMboxes.MBOX5.MDH.all = 0x55555555;
	ECanaMboxes.MBOX5.MDL.all = 0x55555555;
	ECanaMboxes.MBOX6.MDH.all = 0x66666666;
	ECanaMboxes.MBOX6.MDL.all = 0x66666666;
	ECanaMboxes.MBOX7.MDH.all = 0x77777777;
	ECanaMboxes.MBOX7.MDL.all = 0x77777777;
	
	ECanaMboxes.MBOX8.MDH.all = 0x88888888; // MBOX8 = 18181818 after wakeup.
	ECanaMboxes.MBOX8.MDL.all = 0x88888888;
	ECanaMboxes.MBOX9.MDH.all = 0x99999999;
	ECanaMboxes.MBOX9.MDL.all = 0x99999999;
		           
/* Configure Receive mailbox  */

	ECanaShadow.CANMD.all = 0x0;
	ECanaShadow.CANMD.bit.MD8 = 1;
	ECanaRegs.CANMD.all = ECanaShadow.CANMD.all;	

/* Write to MSGCTRL of Transmit  mailboxes */ // Write 00000000 to MSGCTRL

	ECanaMboxes.MBOX0.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX1.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX2.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX3.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX4.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX5.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX6.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX7.MSGCTRL.bit.DLC = 8;
    ECanaMboxes.MBOX9.MSGCTRL.bit.DLC = 8;	

/* Configure CAN-B for transmission */

    ECanbMboxes.MBOX0.MSGID.all  = 0x9FFFFF08; // MBX0 is the TX MBX
    ECanbMboxes.MBOX0.MSGCTRL.bit.DLC = 8;
    ECanbMboxes.MBOX0.MDH.all = 0x18181818; // MBOX8 = 18181818 after wakeup.
    ECanbMboxes.MBOX0.MDL.all = 0x18181818;
    ECanbRegs.CANME.all = 0x00000001;
	    
/* Configure CAN interrupts */ 
	
 	ECanaShadow.CANGIM.all = 0;	
    ECanaShadow.CANGIM.bit.WUIM = 1;  // Enable "Wakeup" int 
    ECanaShadow.CANGIM.bit.GIL = 1;	 // SIL value determines HECC(0/1)INT
    ECanaShadow.CANGIM.bit.I0EN = 1;   // Enable the int line chosen by SIL
    ECanaShadow.CANGIM.bit.I1EN = 1;   // Enable the int line chosen by SIL
    ECanaRegs.CANGIM.all = ECanaShadow.CANGIM.all;
    
/* Reassign ISRs. i.e. reassign the PIE vector for ECAN0INTA_ISR and ECAN0INTA_ISR 
   to point to a different ISR than the shell routine found in DSP28_DefaultIsr.c.
   This is done if the user does not want to use the shell ISR routine but instead
   wants to embed the ISR in this file itself. */
	
	PieVectTable.ECAN0INTA = &eCAN0INT_ISR;
	PieVectTable.ECAN1INTA = &eCAN1INT_ISR;	
    
/* Configure PIE interrupts */    
  
	PieCtrlRegs.PIECTRL.bit.ENPIE = 1;  // Enable vector fetching from PIE block	
	
	PieCtrlRegs.PIEACK.bit.ACK9 = 1;    // Enables PIE to drive a pulse into the CPU

// The 'Wakeup' interrupt can be asserted in either of the eCAN interrupt lines
// Comment out the unwanted line...

	PieCtrlRegs.PIEIER9.bit.INTx5 = 1;  // Enable INTx.5 of INT9 (eCAN0INT)
	PieCtrlRegs.PIEIER9.bit.INTx6 = 1;  // Enable INTx.6 of INT9 (eCAN1INT)	
	
/* Configure system interrupts */
	
	IER |= 0x0100;					// Enable INT9 of CPU
	EINT;							// Global enable of interrupts   	               
 	
/* Configure WUBA */	
	
	ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
	ECanaShadow.CANMC.bit.WUBA = 1; // Set WUBA = 1 so that node can "wakeup"
	ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;

/* Transmit */

    ECanaShadow.CANTRS.all = 0x000002FF;
    ECanaRegs.CANTRS.all = ECanaShadow.CANTRS.all; 	// Initiate a transmit for
    										// all Tx mailboxes
    while(ECanaRegs.CANTRS.all !=0) {}    	// Loop here until all mailboxes are 
    										// transmitted    	
    										 
/* Drive the CAN module into low power mode by setting the PDR bit */   
   
    ECanaShadow.CANMC.all = ECanaRegs.CANMC.all;
    ECanaShadow.CANMC.bit.PDR = 1 ;         // Set PDR = 1
    ECanaRegs.CANMC.all = ECanaShadow.CANMC.all;
     
    ECanaShadow.CANES.all = ECanaRegs.CANES.all;
       do {ECanaShadow.CANES.all = ECanaRegs.CANES.all;}   // Loop here if PDA != 1
        while(ECanaShadow.CANES.bit.PDA != 1 );

	PDA_STATUS = ECanaRegs.CANES.bit.PDA;
	
/* Attempt to write to MBX9 RAM to verify if LPM has indeed been entered */

	ECanaMboxes.MBOX9.MDH.all = 0x01234567;  // This data should not be
	ECanaMboxes.MBOX9.MDL.all = 0x89ABCDEF;  // written into the MBX RAM
	
/* Transmit data from CAN-B to wakeup CAN-A
Node B transmits the same data twice; the first data "wakes up"
the 28x , the second data is stored in MBX8. It can be verified whether
the second data is received and stored in MBX8 */

	for(i=0; i<9999999; i++){}   // Provide a long delay

	ECanbRegs.CANTRS.all = 0x00000001;
	while(ECanbRegs.CANTRS.all !=0) {}
	ECanbRegs.CANTA.all = 0x00000001;

	ECanbRegs.CANTRS.all = 0x000000001;
	while(ECanbRegs.CANTRS.all !=0) {}
	ECanbRegs.CANTA.all = 0x00000001;

    ECanaShadow.CANRMP.all = ECanaRegs.CANRMP.all;
       do {ECanaShadow.CANRMP.all = ECanaRegs.CANRMP.all;}   // Wait for data to wakeup...
        while(ECanaShadow.CANRMP.bit.RMP8 == 0  );

/* Read PDA bit to make sure it is 0 */
	
       ECanaShadow.CANES.all = ECanaRegs.CANES.all;
       PDA_STATUS = ECanaShadow.CANES.bit.PDA;
	
       asm (" ESTOP0");

}

/* --------------------------------------------------- */
/* ISR for PIE INT9.5                          */
/* Connected to HECC0-INTA  eCAN                       */
/* ----------------------------------------------------*/

interrupt void eCAN0INT_ISR(void)  // eCAN
{
   ECanaShadow.CANTA.all  = 0x000002FF;
   // Clear WUIF0 flag bit..
   ECanaShadow.CANGIF0.all = ECanaRegs.CANGIF0.all;
   ECanaShadow.CANGIF0.bit.WUIF0 = 1;
   ECanaRegs.CANGIF0.all = ECanaShadow.CANGIF0.all;
   int0count++;						// Interrupt counter
   
   // Re-enable core interrupts and CAN int from PIE module
   PieCtrlRegs.PIEACK.bit.ACK9 = 1;    // Enables PIE to drive a pulse into the CPU
   IER |= 0x0100;					 // Enable INT9 
   EINT;
   return;
}

/* --------------------------------------------------- */
/* ISR for PIE INT9.6                           */
/* Connected to HECC1-INTA  eCAN                       */
/* ----------------------------------------------------*/

interrupt void eCAN1INT_ISR(void)  // eCAN
{
   ECanaShadow.CANTA.all  = 0x000002FF;
   // Clear WUIF1 flag bit..
   ECanaShadow.CANGIF1.all = ECanaRegs.CANGIF1.all;
   ECanaShadow.CANGIF1.bit.WUIF1 = 1;
   ECanaRegs.CANGIF1.all = ECanaShadow.CANGIF1.all;
   int1count++;						// Interrupt counter
   
   // Re-enable core interrupts and CAN int from PIE module
   PieCtrlRegs.PIEACK.bit.ACK9 = 1;    // Enables PIE to drive a pulse into the CPU
   IER |= 0x0100;					 // Enable INT9 
   EINT;
   return;
}


/* 

OBSERVATIONS:

The following can be observed while executing this example:
 1. Setting PDR = 1 puts CAN-A in LPM
 2. A write to mailbox RAM fails when CAN is in LPM.
 3. CAN module wakes up, when node B transmits a frame (twice). The first
 frame wakes up the CAN. The second frame is correctly received by the
 configured mailbox. The first dataframe may generate an error if,
 there are only 2 nodes in the network (28x MCU and a CAN bus analyzer, for example).
 Reason being, apart from the CAN bus analyzer, the only other node is in LPM and
 there is no node to generate an ACK for the frame the CAN bus analyzer transmits to wakeup.
 4. The WUBA bit works as intended.
 5. The WUIFn bit is correctly set upon wakeup.
 6. The CAN module exits LPM when PDR bit is cleared to zero.

 At least 80 NOPs are needed after the statement that sets the
 TRS bit before the statement that sets the PDR bit = 1. Only then is the
 data in mailbox RAM is transmitted BEFORE the CAN module goes into LPM mode
 Otherwise, the data is transmitted AFTER the CAN module wakes up.
 To ensure all mailboxes have transmitted before putting the CAN module in
 LPM, the CANTRS register may be checked.


*/ 
