Arm® TrustZone® Technology Getting Started Application on PIC32CM LS60: Step 5

Last modified by Microchip on 2024/06/26 15:07

  

Add application code to the secure project.

The application is already partially developed and is available in the main.c file under <your unzip folder>/pic32cm_ls60_cpro_tz_getting_started/dev_files/Secure. The main.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 pic32cm_ls60_cpro_tz_getting_started/dev_files/Secure folder and copy the pre-developed main.c file.
Replace the main.c file of your project available at <Your project folder>/pic32cm_ls60_cpro_tz_getting_started/firmware_secure/src by over-writing it with the copied file.
Go to the pic32cm_ls60_cpro_tz_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>/pic32cm_ls60_cpro_tz_getting_started/firmware_secure/src/trustZone by over-writing it with the copied file.

Open the tz_pic32cm_ls60_cpro_secure project main.c in MPLAB® X IDE and add the application code by following the steps:

Under the main.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® Code Configurator (MCC).

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 Figure 1.

implementation for the SYS_Initialize function

Figure 1

Note: PM_Initialize, EVSYS_Initialize and NVMCTRL_Initialize are system-specific initialization functions necessary to run the device. MCC 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.

Back to Top


In the main() function, below SYS_Initialize(), add the following code to register callback event handlers, flag and a sprintf to indicate toggling LED rate is displayed on the terminal.

    SERCOM5_I2C_CallbackRegister(i2cEventHandler, 0);
    RTC_Timer32CallbackRegister(rtcEventHandler, 0);
    EIC_CallbackRegister(EIC_PIN_12, sw0_eventHandler, 0);
    EIC_CallbackRegister(EIC_PIN_4, sw1_eventHandler, 0);

    sprintf((char*)uartTxTempBuffer, "************* Printing Toggling LED rate *************\r\n");
    readUartTxStatus = true;

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

RTC_Timer32Start();
secure app code to indicate toggling LED rate is displayed on the terminal

Figure 2

Note:

  • The SERCOM5_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.
  • 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.
  • 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 and SW1 switch.
  • The readUartTxStatus flag indicates whether the transfer has taken place.

Back to Top


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.c.

static void sw0_eventHandler(uintptr_t context)
{
    changeSamplingRate = true;
}

static void sw1_eventHandler(uintptr_t context)
{
   if(false == startTemperatureReading)
    {
        startTemperatureReading = true;
        printTempSampleRate = true;
    }
   else
    {
        startTemperatureReading = false;
        printLedToggleRate = 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 (SERCOM5_I2C_ErrorGet() == SERCOM_I2C_ERROR_NONE)
    {
       if(i2cAddress == TEMP_SENSOR_SLAVE_ADDR)
        {
            isTemperatureRead = true;
        }
       else if(isEEPROMReadReq == true)
        {
            isEEPROMReadReq = false;
            eepromTemperatureDataReadStatus  = true;
        }
        isI2CFree = true;
    }
   else if (SERCOM5_I2C_ErrorGet() == SERCOM_I2C_ERROR_NAK ||
             SERCOM5_I2C_ErrorGet() == SERCOM_I2C_ERROR_BUS)
    {
        showErrorMsg = true;
    }
}
secure app code to implement the registered callback event handlers

Figure 3

Back to Top


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

static uint8_t getTemperature(uint8_t* rawTempValue)
{
   int16_t temp;
    temp = (rawTempValue[0] << 8) | rawTempValue[1];
    temp = (temp >> 7) * 0.5;
    temp = (temp * 9/5) + 32;
   return (uint8_t)temp;
}
secure app code to implement the temperature conversion function

Figure 4

Back to Top


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.c.

static void eepromWrite(int8_t temperature)
{
   eepromTxBuffer[0] = EEPROM_LOG_MEMORY_ADDR + wrIndex;
   eepromTxBuffer[1] = temperature;
   SERCOM5_I2C_Write(EEPROM_I2C_SLAVE_ADDR, (void *)eepromTxBuffer, 2);
  if (++wrIndex >= EEPROM_MAX_LOG_VALUES)
   {
       wrIndex = 0;
   }
}
secure app code to implement the EEPROM write function

Figure 5

Back to Top


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

void secureApp(void)
{
   /* Basic Functionality: Demonstrates an LED toggle, i.e. LED0 toggles when
     * the switch SW0 is pressed on a timeout basis and prints the LED toggling
     * rate on the serial terminal.*/

   if (startTemperatureReading == false)
    {
       if (printLedToggleRate == true)
        {
           memset((char*)uartTxTempBuffer, 0x00, 100);
           sprintf((char*)uartTxTempBuffer, "************* Printing Toggling LED rate *************\r\n");
           printLedToggleRate = false;
           readUartTxStatus = true;
        }
       if (isRTCTimerExpired == true)
        {
           isRTCTimerExpired = false;
           memset((char*)uartTxTempBuffer, 0x00, 100);
           sprintf((char*)uartTxTempBuffer, "Toggling LED at %s rate \r\n", &timeouts[(uint8_t)tempSampleRate][0]);
           LED0_Toggle();
           readUartTxStatus = true;
        }
       if(changeSamplingRate == true)
        {
           changeSamplingRate = false;
           if(tempSampleRate == SAMPLING_RATE_500MS)
            {
               tempSampleRate = SAMPLING_RATE_1S;
               RTC_Timer32CompareSet(PERIOD_1S);
            }
           else if(tempSampleRate == SAMPLING_RATE_1S)
            {
               tempSampleRate = SAMPLING_RATE_2S;
               RTC_Timer32CompareSet(PERIOD_2S);
            }
           else if(tempSampleRate == SAMPLING_RATE_2S)
            {
               tempSampleRate = SAMPLING_RATE_4S;
               RTC_Timer32CompareSet(PERIOD_4S);
            }
           else if(tempSampleRate == SAMPLING_RATE_4S)
            {
              tempSampleRate = SAMPLING_RATE_500MS;
              RTC_Timer32CompareSet(PERIOD_500MS);
            }
           else
            {
               ;
            }
           RTC_Timer32CounterSet(0);
           sprintf((char*)uartTxTempBuffer, "LED Toggling rate is changed to %s\r\n", &timeouts[(uint8_t)tempSampleRate][0]);
           readUartTxStatus = true;
        }
    }
   else
    {
       /* Extended Functionality: Reads and prints the current room temperature
         * periodically when the extension header (EXT1) is plugged with the
         * I/O1 Xplained Pro Extension Kit. The temperature reading is displayed on
         * a serial console periodically every second.*/

       if (printTempSampleRate == true)
        {
           memset((char*)uartTxTempBuffer, 0x00, 100);
           sprintf((char*)uartTxTempBuffer, "************* Printing Temperature *************\r\n");
           printTempSampleRate = false;
           readUartTxStatus = true;
        }
       if ((isI2CFree == true) &&
           (isRTCTimerExpired == true))  //Temperature Reading from Sensor
        {
           isRTCTimerExpired = false;
           isI2CFree = false;
           i2cAddress = TEMP_SENSOR_SLAVE_ADDR;
           SERCOM5_I2C_WriteRead(TEMP_SENSOR_SLAVE_ADDR, &i2cWrData, 1, i2cRdData, 2);
        }
       if ((isI2CFree == true) &&
           (isTemperatureRead == true))
        {
           isTemperatureRead = false;
           if(changeSamplingRate == false)
            {
               temperatureVal = getTemperature(i2cRdData);
               memset((char*)uartTxTempBuffer, 0x00, 100);
               sprintf((char*)uartTxTempBuffer, "Temperature = %02d F\r\n", temperatureVal);
               LED0_Toggle();
               i2cAddress = EEPROM_I2C_SLAVE_ADDR;
               isI2CFree = false;
               eepromWrite(temperatureVal);
            }
           else
            {
               changeSamplingRate = false;
               RTC_Timer32CounterSet(0);
               if(tempSampleRate == SAMPLING_RATE_500MS)
                {
                   tempSampleRate = SAMPLING_RATE_1S;
                   sprintf((char*)uartTxTempBuffer, "Sampling Temperature every 1 second \r\n");
                   RTC_Timer32CompareSet(PERIOD_1S);
                }
               else if(tempSampleRate == SAMPLING_RATE_1S)
                {
                   tempSampleRate = SAMPLING_RATE_2S;
                   sprintf((char*)uartTxTempBuffer, "Sampling Temperature every 2 seconds \r\n");
                   RTC_Timer32CompareSet(PERIOD_2S);
                }
               else if(tempSampleRate == SAMPLING_RATE_2S)
                {
                   tempSampleRate = SAMPLING_RATE_4S;
                   sprintf((char*)uartTxTempBuffer, "Sampling Temperature every 4 seconds \r\n");
                   RTC_Timer32CompareSet(PERIOD_4S);
                }
               else if(tempSampleRate == SAMPLING_RATE_4S)
                {
                   tempSampleRate = SAMPLING_RATE_500MS;
                   sprintf((char*)uartTxTempBuffer, "Sampling Temperature every 500 ms \r\n");
                   RTC_Timer32CompareSet(PERIOD_500MS);
                }
               else
                {
                   ;
                }
            }
           readUartTxStatus = true;
        }
       else if (showErrorMsg == true)
        {
           memset((char*)uartTxTempBuffer, 0x00, 100);
           sprintf((char*)uartTxTempBuffer, "Error.! Connect IO1 Xplained Pro board and Restart the device.\r\n");
           readUartTxStatus = true;
           showErrorMsg = false;
        }
       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;
           SERCOM5_I2C_WriteRead(EEPROM_I2C_SLAVE_ADDR, eepromTxBuffer, 1, eepromRxBuffer, 6);
        }
    }
}
secure app code to implement the secure application function

Figure 6

Figure 7 shows the code snippet 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 ) when the submitted request is complete.

The code snippet to submit an I²C transfer to read the temperature sensor value is displayed

Figure 7

Figure 8 shows the code snippet 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.

The code snippet to prepare the received temperature value from the sensor to be displayed on the serial terminal is displayed

Figure 8

Figure 9 shows the code snippet to implement the change of periodicity of the LED Toggling rate and Temperature Sampling rate. Later, prepare a message for the change on the serial terminal when the user presses the SW0 switch.

Figure 9 also shows the code snippet for changing the LED toggling rate.

code snippet for changing LED toggling rate

Figure 9

Figure 10 shows the code snippet to change the temperature sampling rate.

code snippet to change the temperature sampling rate

Figure 10

By default, the LED sampling rate (Basic Functionality) is displayed on the terminal. By pressing the switch SW1, the temperature sampling rate (Extended Functionality) is displayed. Switch SW1 press is used toggle from Basic Functionality to Extended Functionality and vice-versa.

Figure 11 shows the code snippet 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.

code snippet to submit an I²C transfer to read the last five temperature values

Figure 11

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® technology 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.

Back to Top


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

/* Non-secure callable (entry) function */
bool __attribute__((cmse_nonsecure_entry)) readUartTxData(uint8_t *lcluartTxBuffer)
{
   bool localSecureUartStatus = readUartTxStatus;
   if(localSecureUartStatus == true)
    {
        memset((char*)lcluartTxBuffer, 0x00, 100);
        memcpy(lcluartTxBuffer, uartTxTempBuffer, strlen((const char *)&uartTxTempBuffer[0]));
        readUartTxStatus   = false;
    }
   return (localSecureUartStatus);
}

void __attribute__((cmse_nonsecure_entry)) readEEPROMTemperatureDataReq(uint8_t *temperatureBuf)
{
    isEEPROMRead   = true;
    eepromRxBuffer = temperatureBuf;
   return ;
}

bool __attribute__((cmse_nonsecure_entry)) getEEPROMTemperatureDataReadStatus(uint8_t *LclWrIndex)
{
   bool lclEEPROMTempDataReadStatus    = eepromTemperatureDataReadStatus;
    eepromTemperatureDataReadStatus     = false;
   *LclWrIndex                         = wrIndex;
   return (lclEEPROMTempDataReadStatus);
}

void __attribute__((cmse_nonsecure_entry)) secureAppEntry(void)
{
    secureApp();
}
variables and non-secure callables to access and request the secure application from the non-secure application

Figure 12

Note:

  • The readUartTxData() function copies the current temperature sensor value into the requested non-secure application buffer.
  • The readEEPROMTemperatureDataReq() function registers a read request to read the last five temperature values stored in EEPROM from the non-secure application.
  • The getEEPROMTemperatureDataReadStatus() function checks the completion status of the last five stored temperature values read from the EEPROM.
  • The secureAppEntry() is an NSC that helps to jump to the secureApp() function.

Following the addition of the code above, add the header:

#include <stdint.h>
#include
<stddef.h>                     // Defines NULL
Code example adding header

Figure 13

Back to Top