1 Generic Driver Interfaces

1.1 Introduction

The MPLAB® Code Configurator (MCC) Melody Generic Driver Interfaces library contains sets of portable, abstract, and generic APIs for commonly used drivers.

Currently supported drivers:

  • I2C
  • SPI
  • UART

1.2 Required header file:

#include "mcc_generated_files/generic_driver_interfaces/generic_driver_interfaces.h"

1.3 How to use the Generic Driver Interfaces

The Generic Driver Interfaces header file contains generic APIs for the selected drivers.

When to use the interfaces:

The Generic Driver Interfaces can be used when a driver or library requires full abstraction and portability. These APIs use function pointers to assign specific hardware APIs to the required driver APIs. The examples listed in the next section describe how drivers implement the generic APIs for I2C, SPI, and UART.

How to use the interfaces:

Examples on how to use the interfaces with a middleware library or off-chip device drivers are provided in the links below:
  1. Generic Driver Interfaces Use Case 1: I2C APIs with the I2C Temp Sensor MCP98XX Driver - This example uses the Generic Driver Interfaces to communicate with the MCP9808 Temperature Sensor via I2C .
  2. Generic Driver Interfaces Use Case 2: SPI APIs with the SPI Digipot MCP41HV51 Driver - This example uses the Generic Driver Interfaces to communicate with the MCP41HV51 Digital Potentiometer via SPI.
  3. Generic Driver Interfaces Use Case 3: UART APIs with the Data Streamer Driver - This example uses the Generic Driver Interfaces with the Data Streamer Driver to send data frames to the Data Visualizer via UART.

1.4 Generic Driver Interfaces Documentation

1.4.1 Module Documentation

1.4.1.1 Generic Driver Interfaces Use Cases

This contains the basic use cases and implementation of the Generic Driver Interfaces.

1.4.1.1.1 Generic Driver Interfaces Use Case Instructions
The use cases demonstrate how the Generic Driver Interfaces are used within a MCC Melody project. These use cases provide examples guided by the following steps:
  • Add an off-chip device driver or middleware library that requires a driver supported by the Generic Driver Interfaces;

  • Add the Generic Driver Interfaces to the project (if not already added by the driver or library in the previous step);

  • Configure as described in the examples:
    1. The off-chip device driver or middleware library.

    2. The Generic Driver Interfaces.

  • Generate the code;

  • Add the code snippet(s) to the application code;

  • Program the board.

1.4.1.1.2 Generic Driver Interfaces Use Case 1: I2C APIs with the I2C Temp Sensor MCP98XX Driver
This example uses the Generic Driver Interfaces to communicate with the MCP9808 Temperature Sensor via I2C.
  • The use case reads the current ambient temperature and stores it in a variable: 'ambientTempValue'.

Device Resources:
  1. I2C Temp Sensor MCP98XX;

  2. Generic Driver Interfaces;

  3. Delay Driver.

I2C Temp Sensor MCP98xx Driver:
  • Hardware Settings:
    1. Select Device: MCP9808.

  • Communication Settings:
    1. I2C Host Dependency: Select an instance of the I2C.

    2. Address Pin Configuration: Configure the address pins based on the hardware schematic.

Generic Driver Interfaces:
  • Interface Settings:
    1. Add I2C Interface: Yes.

I2CX:
  • Interrupt Settings:
    1. Interrupt Driven: No.

System:
  • Pins: (for all pins check the hardware schematics)
    1. Pin Grid View: Select pins for the I2C bus - SDA, SCL.

After configuring the components as described above, click 'Generate' to generate the code. The following files show how the generated MCP9808 driver should implement the generic APIs for its I2C operations:

  • mcp9808_comms.h - contains the declarations for all the I2C-related APIs;
    • #include <stdint.h>
      #include <stddef.h>
      #include <stdbool.h>
      #include "../generic_driver_interfaces/generic_driver_interfaces.h"
      
      // Creates an instance of the generic I2C APIs
      extern comms_i2c_interface_t MCP9808_Comms;
      
      // Writes data to the MCP9808 using the I2C protocol.
      uint8_t MCP9808_Write(uint16_t address, uint8_t *data, size_t dataLength);
      
      // Reads the data from a MCP9808 register address using the I2C protocol, and then stores the received data in a buffer.
      uint8_t MCP9808_Read(uint16_t address, uint8_t *registerAddress, size_t addressLength, uint8_t *readData, size_t readLength);
      
  • mcp9808_comms.c - contains the implementation of all the I2C-related APIs;
    • #include "../../temperature-mcp98xx/mcp9808_comms.h"
      
      uint8_t MCP9808_Write(uint16_t address, uint8_t *data, size_t dataLength){
          uint8_t returnStat = MCP9808_I2C_ERROR;
          if (MCP9808_Comms.Write(address, data, dataLength)){
              while (MCP9808_Comms.IsBusy()){
                  if (MCP9808_Comms.isInterruptDriven != true){
                      MCP9808_Comms.Tasks();
                  }
              }
              returnStat = MCP9808_I2C_NO_ERROR;
          }
          return returnStat;
      }
      
      uint8_t MCP9808_Read(uint16_t address, uint8_t *registerAddress, size_t addressLength, uint8_t *readData, size_t readLength)
      {
          uint8_t returnStat = MCP9808_I2C_ERROR;
          if (MCP9808_Comms.Write(address, registerAddress, addressLength)){
              while (MCP9808_Comms.IsBusy()){
                  if (MCP9808_Comms.isInterruptDriven == false){
                      MCP9808_Comms.Tasks();
                  }
              }
          }
          if (MCP9808_Comms.Read(address, readData, readLength)){
              while (MCP9808_Comms.IsBusy()){
                  if (MCP9808_Comms.isInterruptDriven == false){
                      MCP9808_Comms.Tasks();
                  }
              }
              returnStat = MCP9808_I2C_NO_ERROR;
          }
          return returnStat;
      }
      
  • mcp9808.h - contains the declarations for the main driver APIs;
    • #include <stdint.h>
      #include "../temperature-mcp98xx/mcp9808_comms.h"
      #include "../temperature-mcp98xx/mcp98xx_interface.h"
      
      // Initializes the MCP9808 Temperature Sensor driver.
      MCP9808_STATUS_t MCP9808_Initialize(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_DEVICE_INIT_t deviceInit);
      
      // Sets the configuration of the MCP9808 Temperature Sensor.
      MCP9808_STATUS_t MCP9808_SensorConfigSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_CONFIG_REG_t sensorConfigReg);
      
      // Sets the temperature conversion resolution of the MCP9808 Temperature Sensor.
      MCP9808_STATUS_t MCP9808_TempResolutionSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_RESOLUTION_VALUE_t tempResolution);
      
      // Reads the temperature sensor value in Celsius from the MCP9808 Temperature Sensor.
      MCP9808_STATUS_t MCP9808_AmbientTemperatureCelsiusRead(MCP9808_DEVICE_CONTEXT_PTR devicePtr, float* ambientTemp);
      
      // MCP9808_STATUS_t MCP9808_TemperatureRawValueRead(MCP9808_DEVICE_CONTEXT_PTR devicePtr, uint16_t* tempRawVal);
      MCP9808_STATUS_t MCP9808_TemperatureRawValueRead(MCP9808_DEVICE_CONTEXT_PTR devicePtr, uint16_t* tempRawVal);
      
      // Converts the temperature raw value to Celsius.
      float MCP9808_TemperatureRawToCelsius(uint16_t rawVal);
      
  • mcp9808_comms.c - contains the implementation of the main driver APIs;
    • #include "../../temperature-mcp98xx/mcp9808.h"
      #include "../../temperature-mcp98xx/mcp9808_config.h"
      
      // Performs byte arrangements for the Sensor Configuration register of the MCP9808 Temperature Sensor.
      void MCP9808_SensorConfigBufferSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr);
      
      // Global API Implementation 
      MCP9808_STATUS_t MCP9808_Initialize(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_DEVICE_INIT_t deviceInit)
      {
          MCP9808_STATUS_t errorCode = MCP98XX_NO_ERROR;
          if (devicePtr == NULL){
              errorCode = MCP98XX_NULL_POINTER;
          } else {
              devicePtr->i2cAddress = deviceInit.i2cAddress;
              devicePtr->sensorConfigReg.shutdownMode = deviceInit.shutdownMode;
              devicePtr->sensorConfigReg.tempHysteresis = deviceInit.tempHysteresis;
              devicePtr->sensorConfigReg.critLock = deviceInit.critLock;
              devicePtr->sensorConfigReg.winLock = deviceInit.winLock;
              devicePtr->sensorConfigReg.alertClear = deviceInit.alertClear;
              devicePtr->sensorConfigReg.alertControl = deviceInit.alertControl;
              devicePtr->sensorConfigReg.alertSelect = deviceInit.alertSelect;
              devicePtr->sensorConfigReg.alertMode = deviceInit.alertMode;
              devicePtr->sensorConfigReg.alertPolarity = deviceInit.alertPolarity;
              devicePtr->tempResolution = deviceInit.tempResolution;
              (void) MCP9808_SensorConfigSet(devicePtr, devicePtr->sensorConfigReg);
              errorCode = MCP9808_TempResolutionSet(devicePtr, devicePtr->tempResolution);
          }
          return errorCode;
      }
      
      MCP9808_STATUS_t MCP9808_SensorConfigSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_CONFIG_REG_t sensorConfigReg)
      {
          MCP9808_STATUS_t errorCode = MCP98XX_NO_ERROR;
          if (devicePtr == NULL){
              errorCode = MCP98XX_NULL_POINTER;
          } else {
              devicePtr->sensorConfigReg = sensorConfigReg;
              devicePtr->txBuffer[0] = MCP9808_SENSOR_CONFIG_REG_PTR;
              MCP9808_SensorConfigBufferSet(devicePtr);
              errorCode = MCP9808_Write(devicePtr->i2cAddress, devicePtr->txBuffer, 3);
          }
          return errorCode;
      }
      
      MCP9808_STATUS_t MCP9808_TempResolutionSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr, MCP9808_RESOLUTION_VALUE_t tempResolution)
      {
          MCP9808_STATUS_t errorCode = MCP98XX_NO_ERROR;
          if (devicePtr == NULL){
              errorCode = MCP98XX_NULL_POINTER;
          } else {
              devicePtr->txBuffer[0] = MCP9808_RESOLUTION_REG_PTR;
              devicePtr->txBuffer[1] = tempResolution;
              errorCode = MCP9808_Write(devicePtr->i2cAddress, devicePtr->txBuffer, 2);
          }
          return errorCode;
      }
      
      MCP9808_STATUS_t MCP9808_AmbientTemperatureCelsiusRead(MCP9808_DEVICE_CONTEXT_PTR devicePtr, float* ambientTemp)
      {
          MCP9808_STATUS_t errorCode = MCP98XX_NO_ERROR;
          uint16_t tempRawVal;
          errorCode = MCP9808_TemperatureRawValueRead(devicePtr, &tempRawVal);
          if (errorCode == MCP98XX_NO_ERROR){
              *ambientTemp = MCP9808_TemperatureRawToCelsius(tempRawVal);
              if ((tempRawVal & MCP9808_CRIT_TEMP_VS_AMBIENT_TEMP) == MCP9808_CRIT_TEMP_VS_AMBIENT_TEMP){
                  errorCode = MCP98XX_CRIT_TEMP_ASSERTED;
              } else if ((tempRawVal & MCP9808_UPPER_TEMP_VS_AMBIENT_TEMP) == MCP9808_UPPER_TEMP_VS_AMBIENT_TEMP){
                  errorCode = MCP98XX_UPPER_TEMP_ASSERTED;
              } else if ((tempRawVal & MCP9808_LOWER_TEMP_VS_AMBIENT_TEMP) == MCP9808_LOWER_TEMP_VS_AMBIENT_TEMP){
                  errorCode = MCP98XX_LOWER_TEMP_ASSERTED;
              } else {
                  errorCode = MCP98XX_NOT_ASSERTED;
              }
          }
          return errorCode;
      }
      
      MCP9808_STATUS_t MCP9808_TemperatureRawValueRead(MCP9808_DEVICE_CONTEXT_PTR devicePtr, uint16_t* tempRawVal)
      {
          MCP9808_STATUS_t errorCode = MCP98XX_NO_ERROR;
          uint8_t tempReg = MCP9808_AMBIENT_TEMP_REG_PTR;
          if (devicePtr == NULL){
              errorCode = MCP98XX_NULL_POINTER;
          } else {
              errorCode = MCP9808_Read(devicePtr->i2cAddress, &tempReg, MCP9808_REGISTER_ADDRESS_LENGTH, devicePtr->rxBuffer, 2);
              if (errorCode == (MCP9808_STATUS_t) MCP9808_I2C_ERROR){
                  errorCode = MCP98XX_COMMUNICATION_ERROR;
              } else {
                  *tempRawVal = (uint16_t) ((devicePtr->rxBuffer[0] << 8) + devicePtr->rxBuffer[1]);
              }
          }
          return errorCode;
      }
      
      float MCP9808_TemperatureRawToCelsius(uint16_t rawVal)
      {
          uint16_t regVal = rawVal;
          if ((regVal & MCP9808_AMBIENT_TEMP_BIT_12) == MCP9808_AMBIENT_TEMP_BIT_12){
              regVal = regVal | MCP9808_BOUNDARY_MASK;
          } else {
              regVal = regVal & ~MCP9808_BOUNDARY_MASK;
          }
          int16_t signedVal = (int16_t) regVal;
          float tempCelsius = (float) signedVal / (float) 16;
          return tempCelsius;
      }
      
      // Local API Implementation 
      void MCP9808_SensorConfigBufferSet(MCP9808_DEVICE_CONTEXT_PTR devicePtr)
      {
          devicePtr->txBuffer[1] = (uint8_t) ((devicePtr->sensorConfigReg.tempHysteresis << 1) |
                  (devicePtr->sensorConfigReg.shutdownMode));
          devicePtr->txBuffer[2] = (uint8_t) ((devicePtr->sensorConfigReg.critLock << 7) |
                  (devicePtr->sensorConfigReg.winLock << 6) |
                  (devicePtr->sensorConfigReg.alertClear << 5) |
                  (devicePtr->sensorConfigReg.alertControl << 3) |
                  (devicePtr->sensorConfigReg.alertSelect << 2) |
                  (devicePtr->sensorConfigReg.alertPolarity << 1) |
                  (devicePtr->sensorConfigReg.alertMode));
      }
      
  • For the example use case, add the following code snippets to the application main.c.
    • #include "mcc_generated_files/system/system.h"
      #include "mcc_generated_files/temperature-mcp98xx/mcp9808.h" 
      #include "mcc_generated_files/timer/delay.h"
      
      // Identify the required I2C functions for the MCP9808 driver and assign these functions to the generic APIs
      comms_i2c_interface_t MCP9808_Comms = {
          .Write = I2C1_Write,
          .Read = I2C1_Read,
          .IsBusy = I2C1_IsBusy,
          .Tasks = I2C1_Tasks,
          .isInterruptDriven = false
      };
      
      void main(void)
      {
          SYSTEM_Initialize();
          float ambientTempValue = 0.0;
          
          // Use Case 1 routine - Initialize the device and read the ambient temperature in degrees Celsius
          MCP9808_DEVICE_CONTEXT_t MCP9808Driver;
          MCP9808_DEVICE_CONTEXT_PTR MCP9808DriverPtr = &MCP9808Driver;
          (void) MCP9808_Initialize(MCP9808DriverPtr, MCP9808_INITIAL_CONFIG);
          DELAY_milliseconds(10);
          (void) MCP9808_AmbientTemperatureCelsiusRead(MCP9808DriverPtr, &ambientTempValue);
          DELAY_milliseconds(10);
          
          while(1)
          {
              // Optional: Continuously read the ambient temperature for a constant interval
              (void) MCP9808_AmbientTemperatureCelsiusRead(MCP9808DriverPtr, &ambientTempValue);          
              DELAY_milliseconds(1000);
          }    
      }
      
1.4.1.1.3 Generic Driver Interfaces Use Case 2: SPI APIs with the SPI Digipot MCP41HV51 Driver
This example uses the Generic Driver Interfaces to communicate with the MCP41HV51 Digital Potentiometer via SPI.
  • The use case sets the device output to 5000 Ohms, and reads the output back to a variable: 'digitalPotValue'.

Device Resources:
  1. SPI Digipot MCP41HV51;

  2. Generic Driver Interfaces;

  3. Delay Driver.

SPI Digipot MCP41HV51 Driver:
  • Communication Settings:
    1. SPI Host Dependency: Select an instance of the SPI.

  • General Settings:
    1. Package Code: Select the code based on the hardware schematic.

    2. Digital POT Output (Ohms): Set the output value to 5000 Ohms.

Generic Driver Interfaces:
  • Interface Settings:
    1. Add SPI Interface: Yes.

SPIX:
  • Interrupt Settings:
    1. Interrupt Driven: No.

System:
  • Pins: (for all pins check the hardware schematics)
    1. Pin Grid View: Select pins for the SPI bus - SCK, SDI, SDO, and MCP41HV51 - CS.

After configuring the components as described above, click 'Generate' to generate the code. The following files show how the generated MCP41HV51 driver should implement the generic APIs for its SPI operations:

  • mcp41hv51_comms.h - contains the declarations for all the SPI-related APIs;
    • #include <stdint.h>
      #include "../generic_driver_interfaces/generic_driver_interfaces.h"
      
      // Creates an instance of the generic SPI APIs
      extern comms_spi_interface_t MCP41HV51_Comms;
      
      // Exchanges the MCP41HV51 buffer data.
      mcp41hv51_comms_status_t MCP41HV51_BufferExchange(uint8_t *buffer, uint8_t bufferSize);
      
      // Enables the SPI configurations.
      mcp41hv51_comms_status_t MCP41HV51_SpiOpen(void);
      
  • mcp41hv51_comms.c - contains the implementation of all the SPI-related APIs;
    • #include "../../mcp41hv51-digipot/mcp41hv51_comms.h"
       
       mcp41hv51_comms_status_t MCP41HV51_BufferExchange(uint8_t *buffer, uint8_t bufferSize)
      {
          mcp41hv51_comms_status_t spiStatus = MCP41HV51_SPI_ERROR;
          if ((NULL != buffer) && (bufferSize > 0U)){
              MCP41HV51_Comms.CsEnable();
              MCP41HV51_Comms.BufferExchange(buffer, bufferSize);
              MCP41HV51_Comms.CsDisable();
              spiStatus = MCP41HV51_SPI_OK;
          }
          return spiStatus;
      }
      
      mcp41hv51_comms_status_t MCP41HV51_SpiOpen(void)
      {
          mcp41hv51_comms_status_t spiStatus = MCP41HV51_SPI_ERROR;
          if (MCP41HV51_Comms.Open(MCP41HV51_Comms.configIndex) == true){
              spiStatus = MCP41HV51_SPI_OK;
          }
          return spiStatus;
      };
      
  • mcp41hv51.h - contains the declarations for the main driver APIs;
    • #include <stdint.h>
      #include "../mcp41hv51-digipot/mcp41hv51_comms.h"
      
      // Initializes the SPI Digi POT MCP41HV51.
      void MCP41HV51_Initialize(void);
      
      // Writes to the SPI Digi POT MCP41HV51 Wiper0 register.
      void MCP41HV51_Wiper0Write(uint8_t data);
      
      // Reads from the SPI Digi POT MCP41HV51 Wiper0 register.
      uint8_t MCP41HV51_Wiper0Read(void);
      
      // Writes to the SPI Digi POT MCP41HV51 TCON register.
      void MCP41HV51_TconRegWrite(uint8_t data);
      
  • mcp41hv51_comms.c - contains the implementation of the main driver APIs;
    • #include "../../mcp41hv51-digipot/mcp41hv51.h"
      #include "../../mcp41hv51-digipot/mcp41hv51_config.h"
      
      uint8_t tconReg = MCP41HV51_TCON_POR;
      uint8_t wip0Reg = MCP41HV51_WIP0_POR;
      uint8_t cmdByte = 0x00U;
      
      // Global API Implementation 
      void MCP41HV51_Initialize(void)
      {
          tconReg = MCP41HV51_TCON_POR;
          wip0Reg = 127;
          if (MCP41HV51_SpiOpen() == MCP41HV51_SPI_OK){
              MCP41HV51_TconRegWrite(tconReg);
              MCP41HV51_Wiper0Write(wip0Reg);
          }
      }
      
      void MCP41HV51_Wiper0Write(uint8_t data)
      {
          cmdByte = (MCP41HV51_WIP0_ADDRESS << 4U) | (cmdByte & ~MCP41HV51_ADDR_MASK);
          cmdByte = ((uint8_t) WRITE_DATA << 2U) | (cmdByte & ~MCP41HV51_CMD_MASK);
          uint8_t buffer[2] = {cmdByte, data};
          (void) MCP41HV51_BufferExchange(buffer, 2);
      };
      
      uint8_t MCP41HV51_Wiper0Read(void)
      {
          cmdByte = (MCP41HV51_WIP0_ADDRESS << 4U) | (cmdByte & ~MCP41HV51_ADDR_MASK);
          cmdByte = ((uint8_t) READ_DATA << 2U) | (cmdByte & ~MCP41HV51_CMD_MASK);
          uint8_t buffer[2] = {cmdByte, 0};
          (void) MCP41HV51_BufferExchange(buffer, 2);
          wip0Reg = buffer[1];
          return wip0Reg;
      };
      
      void MCP41HV51_TconRegWrite(uint8_t data)
      {
          cmdByte = (MCP41HV51_TCON_ADDRESS << 4U) | (cmdByte & ~MCP41HV51_ADDR_MASK);
          cmdByte = ((uint8_t) WRITE_DATA << 2U) | (cmdByte & ~MCP41HV51_CMD_MASK);
          uint8_t buffer[2] = {cmdByte, data};
          (void) MCP41HV51_BufferExchange(buffer, 2);
      };
      
      
  • For the example use case, add the following code snippets to the application main.c.
    • #include "mcc_generated_files/system/system.h"  
      #include "mcc_generated_files/mcp41hv51-digipot/mcp41hv51.h" 
      #include "mcc_generated_files/timer/delay.h"
      
      void CSPIN_SetHigh(void);
      void CSPIN_SetLow(void);
      
      // Identify the required SPI functions for the MCP41HV51 driver and assign these functions to the generic APIs
      comms_spi_interface_t MCP41HV51_Comms = {
          .configIndex = SPI_MCP41HV51,
          .Open = SPI1_Open,
          .BufferExchange = SPI1_BufferExchange,
          .CsEnable = CSPIN_SetLow,
          .CsDisable = CSPIN_SetHigh,
      };
      
      void main(void)
      {
          SYSTEM_Initialize();
          uint8_t digitalPotValue = 0;    
          
          // Use Case 2 routine - Initialize the device and read the WIP0 value
          MCP41HV51_Comms.CsDisable();
          DELAY_milliseconds(10);
          MCP41HV51_Initialize();
          DELAY_milliseconds(10);
          digitalPotValue = MCP41HV51_Wiper0Read();
      
          while(1){}    
      }
      
      void CSPIN_SetLow(void)
      {
          MCP41HV51_CS_SetLow();
      }
      
      void CSPIN_SetHigh(void)
      {
          MCP41HV51_CS_SetHigh();
      }
      
1.4.1.1.4 Generic Driver Interfaces Use Case 3: UART APIs with the Data Streamer Driver
This example uses the Generic Driver Interfaces and the Data Streamer driver to send data frames to the Data Visualizer via UART.
  • The use case displays both the 'ambientTempValue' and 'digitalPotValue' values on the Data Visualizer.

  • Use Cases 1 and 2 are required and must be configured before following this use case.

Device Resources:
  1. Data Streamer;

  2. Generic Driver Interfaces;

  3. Delay Driver.

Data Streamer:
  • Software Settings:
    1. UART Dependency Selection: Select an instance of the UART that is connected to the debugger.

    2. Data Streamer Table: Add a variable with the name "ambientTempValue" and type "uint32_t".

    3. Data Streamer Table: Add a variable with the name "digitalPotValue" and type "float".

Generic Driver Interfaces:
  • Interface Settings:
    1. Add UART Interface: Yes.

UARTX:
  • Configuration Settings:
    1. Redirect Printf: Yes.

  • Interrupt Settings:
    1. Interrupt Driven: No.

System:
  • Pins: (for all pins check the hardware schematics)
    1. Pin Grid View: Select pin for UART Transmit - TX.

After configuring the components as described above, click 'Generate' to generate the code. The following files show how the generated Data Streamer driver should implement the generic APIs for its UART operations:

  • data_streamer_comms.h - contains the declarations for all the UART-related APIs;
    • #include "../generic_driver_interfaces/generic_driver_interfaces.h"
      
      // Creates an instance of the generic UART APIs
      extern comms_uart_interface_t DataStreamer_Comms;
      
      // Writes a variable to the Data Streamer.
      void DataStreamer_VariableWrite(char var);
      
      // Returns true if the data transmission is completed.
      bool DataStreamer_IsTxDone(void);
      
  • data_streamer_comms.c - contains the implementation of all the UART-related APIs;
    • #include "../data_streamer_comms.h"
      
      void DataStreamer_VariableWrite(char var)
      {
          while (DataStreamer_Comms.IsTxReady() == false){}
          DataStreamer_Comms.ByteWrite(var);
      };
      
      bool DataStreamer_IsTxDone(void)
      {
          return DataStreamer_Comms.IsTxDone();
      };
      
  • data_streamer.h - contains the declarations for the main driver APIs;
    • #include <stddef.h>
      #include <stdint.h>
      #include "data_streamer_comms.h"
      
      // Macros that represents the bytes sent to signify the beginning and end of the Data Streamer frame.
      #define DATA_STREAMER_START_BYTE 3
      #define DATA_STREAMER_END_BYTE (255 - DATA_STREAMER_START_BYTE)
      
      // Structure containing the initial Data Streamer variables.
      struct __attribute__((packed)) DATA_STREAMER_STRUCT
      {
          float ambientTempValue;
          uint32_t digitalPotValue;
      };
      extern struct DATA_STREAMER_STRUCT DataStreamer;
      
      // Macro used to write the frame to the Data Streamer.
      #define WriteFrame() DataStreamer_FrameSend(DATA_STREAMER_PACKAGE.varStruct,DATA_STREAMER_PACKAGE.length);
      
      // Writes the frame to the Data Streamer.
      void DataStreamer_FrameSend(void * package, size_t length);
      
  • data_streamer.c - contains the implementation of the main driver APIs;
    • #include "../data_streamer.h"
      
      struct DATA_STREAMER_STRUCT DataStreamer;
      
      // Global API Implementation 
      void DataStreamer_FrameSend(void * package, size_t length)
      {
          char * dp = package;
          DataStreamer_VariableWrite(DATA_STREAMER_START_BYTE);
          size_t counter = length;
          while (counter > 0U){
              DataStreamer_VariableWrite(*dp++);
              counter--;
          }
          DataStreamer_VariableWrite(DATA_STREAMER_END_BYTE);
          while (DataStreamer_IsTxDone() == false){}
      };
      
  • For the example use case, add the following code snippets to the application main.c.
    • #include "mcc_generated_files/system/system.h"
      #include "mcc_generated_files/data_streamer/data_streamer.h" 
      #include "mcc_generated_files/timer/delay.h"
      /* Todo: Add the headers from both Use Cases 1 and 2 */
      
      // Identify the required UART functions for the Data Streamer driver and assign these functions to the generic APIs
      comms_uart_interface_t DataStreamer_Comms = {
          .ByteWrite = UART1_Write,
          .IsTxDone = UART1_IsTxDone,
          .IsTxReady = UART1_IsTxReady,
      };
      /* Todo: Add the API assignments from both Use Cases 1 and 2 */
      
      int main(void)
      {
          SYSTEM_Initialize();        
          float ambientTempValue = 0.0;
          uint8_t digitalPotValue = 0;
          
          /* Todo: Add the routines of both Use Cases 1 and 2 */
          
          // Use Case 3 routine - Stream the data into the Data Visualizer tool and print the results to a terminal
          DataStreamer.ambientTempValue = ambientTempValue;
          DataStreamer.digitalPotValue = (uint32_t) digitalPotValue * 100000U / 256U;
      
          while (1)
          {
              DELAY_milliseconds(10);
              (void) printf("\nUse Case 1: Ambient Temperature = %f Celsius", DataStreamer.ambientTempValue);
              DELAY_milliseconds(5);
              (void) printf("\nUse Case 2: Digital POT WIP0 Value = %lu Ohms", DataStreamer.digitalPotValue);
              DELAY_milliseconds(5);
              (void) printf("\nUse Case 3: Data Streaming - ");
              DELAY_milliseconds(5);
              WriteFrame();
              DELAY_milliseconds(1000);
          }
      }
      

1.4.1.2 Generic Driver Interfaces

This contains the API interface for the Generic Driver Interfaces APIs.

1.4.1.2.1 Module description

This contains the API interface for the Generic Driver Interfaces APIs.

Version: Generic Driver Interfaces Version 1.0.0 Package Version 1.0.0

1.4.2 Data Structure Documentation

1.4.2.1 comms_i2c_interface_t Struct Reference

Struct for generic I2C driver communication APIs.

1.4.2.1.1 Detailed Description

Struct for generic I2C driver communication APIs.

#include <generic_driver_interfaces.h>

Data Fields
  • bool(* Write )(uint16_t i2cAddress, uint8_t *dataBuffer, size_t dataLength)

  • bool(* Read )(uint16_t i2cAddress, uint8_t *dataBuffer, size_t dataLength)

  • bool(* WriteRead )(uint16_t i2cAddress, uint8_t *wrDataBuffer, size_t wrDataLength, uint8_t *rdDataBuffer, size_t rdLength)

  • void(* CallbackRegister )(void(*callbackHandler)(void))

  • void(* Initialize )(void)

  • void(* Deinitialize )(void)

  • bool(* IsBusy )(void)

  • void(* Tasks )(void)

  • uint8_t(* ErrorGet )(void)

  • bool isInterruptDriven

1.4.2.1.2 Field Documentation

The documentation for this struct was generated from the following file:

source/

generic_driver_interfaces.h

CallbackRegister

void(* CallbackRegister) (void(*callbackHandler)(void))

Deinitialize

void(* Deinitialize) (void)

ErrorGet

uint8_t(* ErrorGet) (void)

Initialize

void(* Initialize) (void)

IsBusy

bool(* IsBusy) (void)

isInterruptDriven

bool isInterruptDriven

Read

bool(* Read) (uint16_t i2cAddress, uint8_t *dataBuffer, size_t dataLength)

Tasks

void(* Tasks) (void)

Write

bool(* Write) (uint16_t i2cAddress, uint8_t *dataBuffer, size_t dataLength)

WriteRead

bool(* WriteRead) (uint16_t i2cAddress, uint8_t *wrDataBuffer, size_t wrDataLength, uint8_t *rdDataBuffer, size_t rdLength)

1.4.2.2 comms_spi_interface_t Struct Reference

Struct for generic SPI driver communication APIs.

1.4.2.2.1 Detailed Description

Struct for generic SPI driver communication APIs.

#include <generic_driver_interfaces.h>

Data Fields
1.4.2.2.2 Field Documentation

The documentation for this struct was generated from the following file:

source/

generic_driver_interfaces.h

BufferExchange

void(* BufferExchange) (void *dataBuffer, size_t dataLength)

BufferRead

void(* BufferRead) (void *dataBuffer, size_t dataLength)

BufferWrite

void(* BufferWrite) (void *dataBuffer, size_t dataLength)

ByteExchange

uint8_t(* ByteExchange) (uint8_t dataByte)

ByteRead

uint8_t(* ByteRead) (void)

ByteWrite

void(* ByteWrite) (uint8_t dataByte)

configIndex

uint8_t configIndex

Index for the SPI Host configuration

CsDisable

void(* CsDisable) (void)

CsEnable

void(* CsEnable) (void)

Deinitialize

void(* Deinitialize) (void)

ErrorGet

uint8_t(* ErrorGet) (void)

Initialize

void(* Initialize) (void)

IsBusy

bool(* IsBusy) (void)

isInterruptDriven

bool isInterruptDriven

Open

bool(* Open) (uint8_t configIndex)

RxCompleteCallbackRegister

void(* RxCompleteCallbackRegister) (void(*callbackHandler)(void))

Tasks

void(* Tasks) (void)

TxCompleteCallbackRegister

void(* TxCompleteCallbackRegister) (void(*callbackHandler)(void))

1.4.2.3 comms_uart_interface_t Struct Reference

Struct for generic UART driver communication APIs.

1.4.2.3.1 Detailed Description

Struct for generic UART driver communication APIs.

#include <generic_driver_interfaces.h>

Data Fields
1.4.2.3.2 Field Documentation

The documentation for this struct was generated from the following file:

source/

generic_driver_interfaces.h

BufferRead

void(* BufferRead) (uint8_t *dataBuffer, size_t readLength)

BufferWrite

void(* BufferWrite) (uint8_t *dataBuffer, size_t dataLength)

ByteRead

uint8_t(* ByteRead) (void)

ByteWrite

void(* ByteWrite) (uint8_t dataByte)

Deinitialize

void(* Deinitialize) (void)

ErrorGet

uint8_t(* ErrorGet) (void)

EventCallbackRegister

void(* EventCallbackRegister) (void(*callbackHandler)(void))

FramingErrorCallbackRegister

void(* FramingErrorCallbackRegister) (void(*callbackHandler)(void))

Initialize

void(* Initialize) (void)

IsBusy

bool(* IsBusy) (void)

isInterruptDriven

bool isInterruptDriven

IsRxReady

bool(* IsRxReady) (void)

IsTxDone

bool(* IsTxDone) (void)

IsTxReady

bool(* IsTxReady) (void)

OverrunErrorCallbackRegister

void(* OverrunErrorCallbackRegister) (void(*callbackHandler)(void))

ParityErrorCallbackRegister

void(* ParityErrorCallbackRegister) (void(*callbackHandler)(void))

RxCompleteCallbackRegister

void(* RxCompleteCallbackRegister) (void(*callbackHandler)(void))

Tasks

void(* Tasks) (void)

TxCompleteCallbackRegister

void(* TxCompleteCallbackRegister) (void(*callbackHandler)(void))

1.4.3 File Documentation

1.4.3.1 source/gendrv.dox File Reference

1.4.3.2 source/generic_driver_interfaces.h File Reference

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
1.4.3.2.2 Detailed Description

Generic Driver Interfaces API Header File.

1.5 Revision History

A concise summary of the changes for each version of the Generic Driver Interfaces.

RevisionDateDescription
1.0.008/2024Initial Release