Arm® TrustZone® Getting Started Application on SAM L11: Step 5

Step 5: Add Application Code to the Secure Project

The application is already partially developed and is available in the main_l11.c file under <your unzip folder>/saml11_getting_started/dev_files/Secure. The main_l11.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 saml11_getting_started/dev_files/Secure folder and copy the pre-developed main_l11.c file.
  • Replace the main_l11.c file of your project available at <Your project folder>/saml11_getting_started/firmware/Secure/firmware/src by over-writing it with the copied file.
  • Go to the saml11_getting_started/dev_files/Secure/trustZone folder and copy the pre-developed nonsecure_entry.c file.
  • Replace the nonsecure_entry.c file of your project available at <Your project folder>/saml11_getting_started/firmware/Secure/firmware/src/trustZone by over-writing it with the copied file.
  • Open the trustzone_sam_l11_xpro_Secure project main_l11.c in MPLAB® X IDE and add the application code by following the steps below:

1

Under the main_l11.c file, in function main, notice the call to the SYS_Initialize function. The generated SYS_Initialize function initializes all the peripheral modules used in the Secure application, which is 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.

secure_app_code1.png

Note: NVMCTRL_Initialize, PM_Initialize, and EVSYS_Initialize are system-specific initialization functions necessary to run the device. MHC adds these modules by default to the project graph and generates code. These modules will be initialized to user configurations if the user configures them explicitly.

2

In the main() function, below SYS_Initialize(), add the following code to register callback event handlers.

SERCOM1_I2C_CallbackRegister(i2cEventHandler, 0);
RTC_Timer32CallbackRegister(rtcEventHandler, 0);
EIC_CallbackRegister(EIC_PIN_5,EIC_User_Handler, 0);

Following the addition of the code above, add the function call.

RTC_Timer32Start();
secure_app_code2.png

Note:

a

The SERCOM1_I2C_CallbackRegister function call registers a callback event handler with the Secure I²C Peripheral Library (PLIB). The event handler is called by the I²C PLIB when the I²C transfer is complete.

b

The RTC_Timer32CallbackRegister function call registers a Secure Real-Time Clock (RTC) callback event handler with the RTC PLIB. The callback event handler is called by the RTC PLIB when the configured time period has elapsed.

c

The EIC_CallbackRegister function call registers a Secure External Interrupt Controller (EIC) callback event handler with the EIC PLIB. The callback event handler is called by EIC PLIB when the user presses the SW0 switch.

3

Implement the registered callback event handlers for Secure RTC, Secure I²C, and Secure EIC PLIBs by adding the following code before the main() function in main_l11.c.

static void EIC_User_Handler(uintptr_t context)
{
    changeTempSamplingRate = true;      
}

static void rtcEventHandler (RTC_TIMER32_INT_MASK intCause, uintptr_t context)
{
    if (intCause & RTC_TIMER32_INT_MASK_CMP0)
    {            
        isRTCTimerExpired = true;                              
    }
}

static void i2cEventHandler(uintptr_t contextHandle)
{
    if (SERCOM1_I2C_ErrorGet() == SERCOM_I2C_ERROR_NONE)
    {
        if(i2cAddress == TEMP_SENSOR_SLAVE_ADDR)
        {
            isTemperatureRead       = true;
        }
        else if(isEEPROMReadReq == true)
        {
            isEEPROMReadReq         = false;
            eepromTemperatureDataReadStatus  = true;
        }
        isI2CFree                   = true;
    }
}
secure_app_code3.png

4

Implement the temperature conversion function to convert the temperature value read from sensor to a readable format (Degree Celsius) by adding the following code before the main() function in main_l11.c.

static uint8_t getTemperature(uint8_t* rawTempValue)
{
    int16_t temp;
    // Convert the temperature value read from sensor to readable format (Degree Celsius)
    // For demonstration purpose, temperature value is assumed to be positive.
    // The maximum positive temperature measured by sensor is +125 C
    temp = (rawTempValue[0] << 8) | rawTempValue[1];
    temp = (temp >> 7) * 0.5;
    temp = (temp * 9/5) + 32;
    return (uint8_t)temp;
}
secure_app_code4.png

5

Implement the EEPROM write function to write the temperature value read from the sensor into EEPROM by adding the following code before the main() function in main_l11.c.

static void EEPROM_Write(int8_t temperature)
{
    eepromTxBuffer[0] = EEPROM_LOG_MEMORY_ADDR + wrIndex;
    eepromTxBuffer[1] = temperature;

    /* Submit I2C transfer to store the temperature value in EEPROM */
    SERCOM1_I2C_Write(EEPROM_I2C_SLAVE_ADDR, (void *)eepromTxBuffer, 2);

    /* Increment the EEPROM memory index */
    if (++wrIndex >= EEPROM_MAX_LOG_VALUES)
    {
        wrIndex = 0;
    }
}
secure_app_code5.png

6

Implement the secure application function by adding the following code before the main() function in main_l11.c.

void secureApp(void)
{
    if ((isI2CFree == true) && 
        (isRTCTimerExpired == true))  //Temperature Reading from Sensor
    {
        isRTCTimerExpired = false;
        isI2CFree = false;
        i2cAddress = TEMP_SENSOR_SLAVE_ADDR;
        SERCOM1_I2C_WriteRead(TEMP_SENSOR_SLAVE_ADDR, &i2cWrData, 1, i2cRdData, 2);
    }
    if ((isI2CFree == true) && 
        (isTemperatureRead == true))
    {
        isTemperatureRead = false;
        if(changeTempSamplingRate == false)    
        {
            temperatureVal = getTemperature(i2cRdData);
            memset((char*)secureUartTxBuffer, 0x00, 100);
            sprintf((char*)secureUartTxBuffer, "Temperature = %02d F\r\n", temperatureVal);
            LED_Toggle();
        // Temperature Writing to EEPROM
            i2cAddress = EEPROM_I2C_SLAVE_ADDR;
            isI2CFree = false;
            EEPROM_Write(temperatureVal);
        }
        else
        {
            changeTempSamplingRate = false;
            if(tempSampleRate == TEMP_SAMPLING_RATE_500MS)
            {
                tempSampleRate = TEMP_SAMPLING_RATE_1S;
                sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 1 second \r\n");
                RTC_Timer32CompareSet(PERIOD_1S);
            }
            else if(tempSampleRate == TEMP_SAMPLING_RATE_1S)
            {
                tempSampleRate = TEMP_SAMPLING_RATE_2S;
                sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 2 seconds \r\n");        
                RTC_Timer32CompareSet(PERIOD_2S);                        
            }
            else if(tempSampleRate == TEMP_SAMPLING_RATE_2S)
            {
                tempSampleRate = TEMP_SAMPLING_RATE_4S;
                sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 4 seconds \r\n");        
                RTC_Timer32CompareSet(PERIOD_4S);                                        
            }    
            else if(tempSampleRate == TEMP_SAMPLING_RATE_4S)
            {
               tempSampleRate = TEMP_SAMPLING_RATE_500MS;
               sprintf((char*)secureUartTxBuffer, "Sampling Temperature every 500 ms \r\n");        
               RTC_Timer32CompareSet(PERIOD_500MS);
            }
            else
            {
                ;
            }
        }
        temperatureStatus = true;
    }
    if ((isI2CFree == true) && 
        (isEEPROMRead == true))       //Temperature Reading from EEPROM
    {
        isI2CFree           = false;
        isEEPROMRead        = false;
        isEEPROMReadReq     = true;
        i2cAddress          = EEPROM_I2C_SLAVE_ADDR;
        eepromTxBuffer[0]   = EEPROM_LOG_MEMORY_ADDR;
        SERCOM1_I2C_WriteRead(EEPROM_I2C_SLAVE_ADDR, eepromTxBuffer, 1, eepromRxBuffer, 6);
    }
}
secure_app_code6.png

Note:

a

The code snippet (shown in the following image) to submit an I²C transfer to read the temperature sensor value when the configured time period (default 500 milliseconds) has elapsed and I²C PLIB is free. The I²C PLIB calls back the callback event handler (registered in Step 2 above) when the submitted request is complete.

secure_app_code7.png

b

The code snippet (shown in the following image) to prepare the received temperature value from the sensor to be displayed on the serial terminal, to store the temperature value in the EEPROM memory. This complete action will happen only if the temperature sensor value is ready and I²C PLIB is free.

secure_app_code8.png

c

The code snippet (shown in the following image) to implement the change of sampling rate and prepare a message for the change on the serial terminal when the user presses the SW0 switch.

secure_app_code9.png

d

The code snippet (shown in the following image) to submit an I²C transfer to read the last five temperature values stored in the EEPROM memory when a read request is received from the non-secure application and I²C PLIB is free.

secure_app_code10.png

The complete secure functionality must run in the while(1) super loop of the secure main() function in main_l11.c.

secureApp();
secure_app_code11.png

Note:

  • The non-secure application can access the secure applications by calling Non-Secure Callable (NSCs) functions. So, you need to create an NSC in the non-secure entry region to a Secure function by using the cmse_nonsecure_entry attribute when a secure developer wants to give them access to a secure function to a non-secure developer.
  • When both secure and non-secure applications are running on the Arm® TrustZone® device, the secure main stack will be redirected to the non-secure main stack once after the secure application initialization completes. So, you need to create an NSC to run the complete secure application from the non-secure application.

7

Under Source Files > trustZone, in the nonsecure_entry.c file, implement the non-secure callables below to access and request the secure application from the non-secure application.

secure_app_code12.png

a

The readTemperatureData() function copies the current temperature sensor value into requested non-secure application buffer.

b

The readEEPROMTemperatureDataReq() function registers a read request to read last five temperature values stored in EEPROM from the non-secure application.

c

The getEEPROMTemperatureDataReadStatus() function checks the completion status of the last five stored temperature values read from the EEPROM.

d

The secureAppEntry() function runs the complete secure functionality from the non-secure application.



Next Step >

© 2022 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.