Harmony v3 Drivers and Middleware on PIC32MZ EF MCUs Using FreeRTOS: Step 6

Step 6: Add Application Code to the Project

The application is already developed and is available in the files:

  • app_sensor_thread.c,
  • app_sensor_thread.h,
  • app_eeprom_thread.c,
  • app_eeprom_thread.h,
  • app_usb_thread.c,
  • app_usb_thread.h,
  • app_user_input_thread.c, and
  • app_user_input_thread.h.

They are available under
<your unzip folder>/getting_started_middleware/dev_files/middleware_drivers_freertos_pic32mz_ef_curiosity_v2.

The application files app_sensor_thread.c, app_eeprom_thread.c, app_usb_thread.c, and app_user_input_thread.c contain the application logic. They also contain placeholders that you will populate with the necessary code in the next step.

Go to the
getting_started_middleware/dev_files/middleware_drivers_freertos_pic32mz_ef_curiosity_v2 folder and copy the pre-developed files:

  • app_sensor_thread.c,
  • app_sensor_thread.h,
  • app_eeprom_thread.c,
  • app_eeprom_thread.h,
  • app_usb_thread.c,
  • app_usb_thread.h,
  • app_user_input_thread.c, and
  • app_user_input_thread.h.

Paste and replace (over-write) the files of your project available at <Your project folder>/getting_started_middleware/firmware/src with the copied files.

1

The main_pic32mz.c file calls the SYS_Tasks() routine, which creates threads to run the System Service and Middleware tasks. It also creates the threads to run Sensor, EEPROM, USB, and User Input applications and makes a call to FreeRTOS Application Programming Interface (API) vTaskStartScheduler() to start the scheduler.

main_thread_calls.png

Observe that each application task runs in its individual RTOS thread.

2

The app_sensor_thread.h, app_eeprom_thread.h, app_usb_thread.h, and app_user_input_thread.h files define the states and data structures of these application threads.

thread_ds.png

3

The APP_SENSOR_THREAD_Tasks() function in app_sensor_thread.c, the APP_EEPROM_THREAD_Tasks() function in app_eeprom_thread.c, the APP_USB_THREAD_Tasks() function in app_usb_thread.c, and the APP_USER_INPUT_THREAD_Tasks() function in app_user_input_thread.c implement the application thread functionality.

4

The sensor application thread in app_sensor_thread.c and the EEPROM application thread in app_eeprom_thread.c act as the two clients to the Synchronous I²C driver instance 0.

5

The sensor application thread in app_sensor_thread.c, the EEPROM application thread in app_eeprom_thread.c, and the User Input application thread in app_user_input_thread.c act as the three clients to the synchronous Universal Synchronous Asynchronous Receiver Transmitter (USART) driver instance 0.

In the application source files, the code for the application threads is already developed. In the following steps, you will add the missing code snippets.

6

Open app_sensor_thread.c and add application code referring to the comments in the screenshots as shown in the following steps:

a

Open the I²C driver instance 0 (which is associated with TWIHS0). The call to DRV_I2C_Open() API will associate the sensor client with the I²C driver instance 0. The returned handle will be used by the application in all the subsequent calls to the driver to read temperature values from the sensor.

app_sensorData.i2cHandle = DRV_I2C_Open( DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE );
open_i2c_sensor.png

b

Set the transfer parameters for the sensor application thread client after a valid handle to the driver is obtained. The transfer parameter sets the I²C clock speed to 100 kHz for this client.

DRV_I2C_TransferSetup(app_sensorData.i2cHandle, &app_sensorData.i2cSetup);
set_sensor_i2c_baud.png

c

Open the USART synchronous driver instance 0, which is associated with UART6. The call to DRV_USART_Open() API will return a handle to the USART driver instance 0. The returned handle will be used by the sensor application thread in all the subsequent calls to the driver to print the temperature values on the serial terminal for this client.

app_sensorData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, 0);
sensor_open_usart.png

d

Submit a call to the FreeRTOS API vTaskDelay() to yield CPU control to other threads in the application for a duration equal to the temperature sampling time of the application (one second).

vTaskDelay(APP_SENSOR_SAMPLING_RATE_IN_MSEC/portTICK_PERIOD_MS);
vtask_delay_sensor.png

e

Submit a blocking I²C request to read temperature from the sensor. The calling thread will be blocked until the request is completed, thereby allowing other threads to run.

if (true == DRV_I2C_WriteReadTransfer(app_sensorData.i2cHandle, APP_SENSOR_I2C_SLAVE_ADDR, (void*)&registerAddr, 1, (void*)app_sensorData.i2cRxBuffer, 2))
submit_i2c_rd_sensor.png

f

Communicate the temperature ready event and the temperature value to the EEPROM thread using the RTOS queue. The EEPROM task unblocks when an event/data is available in the RTOS queue.

xQueueSend( eventQueue, (void*)&app_sensorData.eventInfo, portMAX_DELAY);
queuesend_sensor.png

g

Print the latest temperature value by making a call to DRV_USART_WriteBuffer() API, which submits a blocking USART write request.

DRV_USART_WriteBuffer(app_sensorData.usartHandle, app_sensorData.usartTxBuffer, strlen );
write_usart_sensor.png

7

Open app_eeprom_thread.c and add application code referring to the comments in the screenshots as shown in the following steps:

a

Associate the second client (EEPROM application thread client) with the I²C driver instance 0. This is done by opening the I²C driver instance 0 again. The call to DRV_I2C_Open () API will now associate the EEPROM application thread client with the I²C driver instance 0. The returned handle will be used by the application in all the subsequent calls (related to the EEPROM application thread client) to the driver to write/read temperature values to/from EEPROM.

app_eepromData.i2cHandle = DRV_I2C_Open( DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE );
eeprom_i2c_open.png

b

Like the sensor application thread client, setup the transfer parameters for the EEPROM application thread client after a valid handle to the driver is obtained. The transfer parameters set the I²C clock speed to 400 kHz for this client.

DRV_I2C_TransferSetup(app_eepromData.i2cHandle, &app_eepromData.i2cSetup);
eeprom_set_i2c_baud.png
  • The call to DRV_I2C_TransferSetup overrides the baud rate set in the I²C driver configuration using MPLAB® Harmony Configurator (MHC).
  • I²C was configured to run at 400 kHz using MHC. While in the application, the sensor thread has reconfigured it to run at 100 kHz and EEPROM thread configured it to run at 400 kHz. This illustrates how the Harmony I²C driver handles the peripheral module-specific configuration depending on the client accessing the peripheral.

c

Associate the second client (EEPROM application thread client) to USART synchronous driver instance 0 (which is associated with USART1). The call to DRV_USART_Open() API will return a handle to the USART driver instance 0. The returned handle will be used by the EEPROM application thread in all the subsequent calls to the driver to print the temperature values from the EEPROM on the serial terminal.

app_eepromData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, 0);
eeprom_usart_open.png

d

Block the EEPROM application thread until a request is available in the RTOS queue. The scheduler will unblock the EEPROM thread when an event/data is available in the RTOS queue. Depending on the event received in the RTOS queue, the EEPROM task either writes or reads temperature value from the EEPROM.

xQueueReceive( eventQueue, &app_eepromData.eventInfo, portMAX_DELAY );
eeprom_xqueue_receive.png

e

Submit blocking I²C requests to write temperature from the sensor to EEPROM. The calling thread will be blocked until the request is completed thereby allowing other threads to run.

        if (true == DRV_I2C_WriteTransfer(app_eepromData.i2cHandle, APP_EEPROM_I2C_SLAVE_ADDR, (void *)app_eepromData.i2cTxBuffer, 2))
        {                
            /* Check if EEPROM has completed the write operation */
            while (false == DRV_I2C_WriteTransfer(app_eepromData.i2cHandle, APP_EEPROM_I2C_SLAVE_ADDR, (void *)&dummyData, 1));
        }
eeprom_i2c_blocking_write.png

f

Submit blocking I²C request to read the temperature from the EEPROM to submit for printing on the serial terminal. The calling thread will be blocked until the request is completed, thereby allowing other threads to run.

if (true == DRV_I2C_WriteReadTransfer(app_eepromData.i2cHandle, \
                APP_EEPROM_I2C_SLAVE_ADDR, app_eepromData.i2cTxBuffer, 1,\
                app_eepromData.i2cRxBuffer, 5))
eeprom_i2c_blocking_read.png

g

Print the logged temperature value from EEPROM by making a call to the DRV_USART_WriteBuffer() API by calling the EPPROM application thread function APP_EEPROM_PrintTemperature() which submits a blocking USART write request.

APP_EEPROM_PrintTemperature(app_eepromData.i2cRxBuffer, app_eepromData.wrIndex);
eeprom_usart_write.png

8

Open app_usb_thread.c and add application code referring to the comments in the screenshots as shown in the following steps:

a

Register an event handler (callback) with the File System Service by calling API SYS_FS_EventHandlerSet(). The event handler is called by the File System Service when mount/unmount operation has completed.
Register an event handler (callback) with the USB Host stack layer by calling API USB_HOST_EventHandlerSet(). The event handler is called by the USB Host stack layer when several USB host events occur.
Call API USB_HOST_BusEnable () to enable operation of the USB Host Bus.

SYS_FS_EventHandlerSet((void *)APP_SYSFSEventHandler, (uintptr_t)NULL);
USB_HOST_EventHandlerSet(APP_USBHostEventHandler, 0);
USB_HOST_BusEnable(0);
usb_host_bus_enable.png

b

Call API USB_HOST_BusIsEnabled() to wait in the USB application thread state for the USB host bus to be enabled.

if(USB_HOST_BusIsEnabled(0))
wait_usb_host_bus_enable.png

c

Call the File System API SYS_FS_FileOpen() to create a new text file Temperature_Sensor_Data.txt to write temperature sensor data to the connected USB thumb drive. The text file will be created only if there is no existing file of the same name in the thumb drive.

app_usb_threadData.fileHandle = SYS_FS_FileOpen("/mnt/myDrive1/Temperature_Sensor_Data.txt", (SYS_FS_FILE_OPEN_APPEND_PLUS));
open_temp_file.png

d

Submit a request to write temperature sensor data to the USB thumb drive by calling the File System API SYS_FS_FileWrite(). The API would then return when the data is successfully written to the thumb drive, otherwise it would return an error.

if (SYS_FS_FileWrite( app_usb_threadData.fileHandle, (const void *) writeData, strlen) == -1)
write_temp_sensor_data.png

e

Call the File System API SYS_FS_FileClose () to close the text file Temperature_Sensor_Data.txt once the temperature sensor data is written.

SYS_FS_FileClose(app_usb_threadData.fileHandle);
close_temp_file.png

f

Block the USB application thread until a request is available in the RTOS queue. The scheduler will unblock the USB thread when an event/data is available in the RTOS queue. Depending on the event received in the RTOS queue, the USB task writes temperature value to the USB thumb drive.

xQueueReceive( eventQueue, &app_usb_threadData.eventInfo, portMAX_DELAY );
usb_xqueue_receive.png

9

Open app_user_input_thread.c and add application code referring to the comments in the screenshots as shown in the following steps:

a

Associate the third client (user input application thread client) to USART synchronous driver instance 0 (which is associated with USART1). The call to DRV_USART_Open() API will return a handle to the USART driver instance 0. The returned handle will be used by the User Input application thread in all the subsequent calls to the driver to read a user key press on the serial terminal.

app_user_inputData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, 0);
user_input_usart_open.png

b

Submit a blocking USART read request. The user input thread will be unblocked when a character is received on the USART. After unblocking, it communicates the request to read data from EEPROM to the EEPROM thread via the RTOS queue.

if (DRV_USART_ReadBuffer(app_user_inputData.usartHandle, &usartData, 1 ) == true)
user_input_usart_read.png
xQueueSend( eventQueue, &app_user_inputData.eventInfo, portMAX_DELAY );
user_input_usart_event.png

You have used FreeRTOS to implement your application. You are now ready to build and run your RTOS based application!


Next Step >

© 2024 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.