Low Power Application on SAM D21 Using Harmony v3 Peripheral Libraries: Step 6
Add Application Code to the Project
The application is already partially developed and is available in the main_d21.c file under <your unzip folder>/samd21_low_power/dev_files/sam_d21_xpro. The main_d21.c file contains the application logic. It also contains placeholders that you will populate with the necessary code in the next step.
- Go to the samd21_low_power/dev_files/sam_d21_xpro folder and copy the pre-developed main_d21.c file.
- Replace (over-write) the main_d21.c file of your project available at <Your project folder>/samd21_low_power/firmware/src with the copied file.
- Open main_d21.c in MPLAB® X IDE and add the application code by following the steps below:
Under the main_d21.c file, in the main() function, notice the call to the SYS_Initialize function. The generated SYS_Initialize function initializes all the peripheral modules used in the application, configured through MPLAB Harmony Configurator (MHC).
Tip: Press the CTRL key and left-click on the SYS_Initialize function. The click will open the implementation for the SYS_Initialize function as shown in the following image.
In the int main (void) function, below the SYS_Initialize() function call, add the following lines of code to register callback event handlers, start the Real-Time Clock (RTC) timer, and display the demo title message:
EIC_CallbackRegister (EIC_PIN_15, EIC_User_Handler, 0);
/* Initialize the SERCOM 2 I²C callback register */
SERCOM2_I2C_CallbackRegister (i2cEventHandler, 0);
/* Initialize the AC callback register */
AC_CallbackRegister(acEventHandler, comparator_context);
/* Start RTC counter */
RTC_Timer32Start ();
/* Display example title */
printf ("\n\n\r-----------------------------------------------------------");
printf ("\n\r Low-power Application on SAM D21 Xpro ");
printf ("\n\r-----------------------------------------------------------");
After the code above, add the following lines of code to read the user input and run the application either in Power Measurement mode or Wake-Up Time Measurement mode.
{
display_menu();
if(cmd == 'a')
{
printf("\n\rEntering into Power Measurement Mode");
break;
}
else if(cmd == 'b')
{
printf("\n\rEntering into Wakeup Time Measurement Mode");
/* To run EIC using CPU clock through GCLK0, set the DFLLCTRL to run in standby mode and set the EIC peripheral clock source as GLCK2 */
/* Configure DFLL */
SYSCTRL_REGS->SYSCTRL_DFLLCTRL = SYSCTRL_DFLLCTRL_ENABLE_Msk | SYSCTRL_DFLLCTRL_MODE_Msk | SYSCTRL_DFLLCTRL_RUNSTDBY_Msk ;
/* Selection of the Generator and write Lock for EIC */
GCLK_REGS->GCLK_CLKCTRL = GCLK_CLKCTRL_ID(5) | GCLK_CLKCTRL_GEN(0x0) | GCLK_CLKCTRL_CLKEN_Msk;
break;
}
else
{
printf("\n\rInvalid choice");
}
}
After the code above, add the following lines of code to keep the device in Standby Sleep mode after power on reset.
/* Put device in Standby mode */
ac_comparison_done = false;
sleepMode = STANDBY_SLEEP_MODE;
PM_StandbyModeEnter ();
Implement the registered callback interrupt handlers for I²C, AC, and EIC PLIBs by adding the following code before the int main (void) function in main_d21.c.
static void i2cEventHandler (uintptr_t contextHandle)
{
/* Check that there were no error during SERCOM3_I2C read for temperature */
if(SERCOM2_I2C_ErrorGet() == SERCOM_I2C_ERROR_NONE)
{
isTemperatureRead = true;
}
else
{
/* Display message on terminal */
printf ("\n\rThere were an error during I2C Transmit. Please ensure that the I/O1 Xplained Pro is connected to the board.");
}
}
/* Handler for AC interrupt */
void acEventHandler(uint8_t int_flag, uintptr_t ac_context)
{
ac_comparison_done = true;
/* Indication that a comparison is done */
LED0_Toggle();
}
/* Handler for button switch interrupt using EIC peripheral */
static void EIC_User_Handler (uintptr_t context)
{
/* Set sleepmode flag after interrupt from button pressure */
if(cmd == 'a')
{
sleepMode = IDLE_SLEEP_MODE;
}
}
Inside the while loop, add the following lines of code to ensure that the selection is Power Measurement mode or Wake-up Time Measurement.
{
}
else /* Wakeup Time Measurement */
{
}
Inside the power measurement mode condition, add the following code to submit an I²C transfer to read the temperature sensor value when the AC triggers an interrupt (i.e., when the user covers the light sensor). The I²C PLIB calls back the callback event handler (registered in Step 2 above) when the submitted request is complete.
{
printf ("\n\n\n\rPA04 voltage is above detect level, Wake-up from sleep mode.");
ac_comparison_done = false;
/* Switch on LED0 */
LED0_Clear ();
/* Read the temperature sensor value from I/O1 Xplained Pro through SERCOM3 I²C */
SERCOM2_I2C_WriteRead (TEMP_SENSOR_SLAVE_ADDR, &i2cWrData, 1, i2cRdData, 2);
TemperatureReadInitiated = true;
}
else if (sleepMode == IDLE_SLEEP_MODE)
{
printf ("\n\n\n\rSW0 Pressed, Wake-up from sleep mode......");
}
After the code above, add the following lines of code to prepare the received temperature value from the sensor to be printed on the serial terminal and code to transfer the buffer containing the latest temperature value in the format Temperature = XX F\r\n.
{
isTemperatureRead = false;
/* Store the temperature sensor value on temperatureVal */
temperatureVal = getTemperature (i2cRdData);
/* Fill uartTxBuffer variable with message containing temperature value to display on terminal */
sprintf ((char*)uartTxBuffer, "\n\rTemperature = %02d F", temperatureVal);
/* Check that the SERCOM3 USART is ready for new data */
while (!SERCOM3_USART_TransmitterIsReady());
/* Transmit the uartTxBuffer variable content to terminal through SERCOM3 USART peripheral */
printf("%s", (char *)uartTxBuffer);
TemperatureReadInitiated = false;
}
After the code above, add the following code to enter into Standby mode once temperature data is printed on the serial terminal and add the code snippet to enter into Idle Sleep mode when the user presses switch SW0.
{
/* Switch off LED0 */
LED0_Set ();
if (sleepMode == IDLE_SLEEP_MODE)
{
sleepMode = STANDBY_SLEEP_MODE;
/* Display message on terminal */
printf ("\n\rEntering into Idle sleep mode.");
/* Put the device in Idle mode */
PM_IdleModeEnter ();
}
else
{
printf ("\n\rEntering into Standby sleep mode");
/* Put device in Standby mode */
PM_StandbyModeEnter ();
}
}
Inside the wake-up time measurement mode condition, add the following lines of code to put the device back into Sleep mode based on the wake-up input and sleepMode flag.
{
ac_comparison_done = false;
printf ("\n\n\n\rPA04 voltage is above detect level, Wake-up from sleep mode......");
}
else
{
printf ("\n\n\n\rSW0 Pressed, Wake-up from sleep mode......");
sleepMode = ((sleepMode == IDLE_SLEEP_MODE) ? STANDBY_SLEEP_MODE : IDLE_SLEEP_MODE);
}
/* Switch off LED0 */
LED0_Set ();
if (sleepMode == IDLE_SLEEP_MODE)
{
/* Display message on terminal */
printf ("\n\rEntering into Idle sleep mode.");
/* Put the device in Idle mode */
PM_IdleModeEnter ();
}
else
{
printf ("\n\rEntering into Standby sleep mode");
/* Put device in Standby mode */
PM_StandbyModeEnter ();
}
In the plib_eic.c file, add #include "definitions.h" at the top. Scroll down to the EIC_InterruptHandler function and add the following lines of code at the top. This GPIO is toggled in the Interrupt Service Routine (ISR) of the SW0 press interrupt to measure wake-up time. The time between the switch press and the GPIO toggle in the ISR is the wake-up time.
You are now ready to build the code and observe the results!