Getting started with USB-Power Delivery Dual Role

Revision as of 14:46, 8 February 2023 by Registered User

Target description

This tutorial aims to help you to:

  • use the X-NUCLEO-DRP1M1 shield that includes a TCPP03-M20 protection circuit and provides a USB Type-C® connector
  • create a USB-PD Source application with the NUCLEO-G0B1RE board and the X-NUCLEO-DRP1M1 shield by using STM32CubeIDE software

Prerequisites

  • Computer with Windows 7 (or higher)

Hardware

  • NUCLEO-G0B1RE (tested on rev C) [1]
  • X-NUCLEO-DRP1M1 shield [2]
  • A USB-PD sink device to test our USB Source device (it can be the sink created in this wiki article, or a USB Type-C® mobile phone or device)
  • USB cable Type-A to Mini-B
  • USB Type-C® to Type-C® cable

Software

  • STM32CubeIDE (tested with V1.9.0) [3]
  • X-CUBE-TCPP MCU Firmware Package (BSP) [4]

Literature

  • UM2324 NUCLEO-G0B1RE User Manual
  • UM2891 X-NUCLEO-DRP1M1 User Manual

Create a USB-PD Source Device

Clock.png Total 60min

1. Creating the project

Clock.png 5min

Open STM32CubeIDE and create a new STM32 project. As a target selection, choose the NUCLEO-G071RE from the Board Selector Tab

USBPD 0-newproject.png


Click "Next", then enter your project name. Leave the other fields as default and click "Finish".

USBPD DRP 1-newproject.png


When prompted for initializing peripherals with their default mode, click No.

2. Configuring the system

Clock.png 15min

At this point, your project is created and you are left with the STM32CubeMX view. The next steps, deal with the peripherals configuration and the options required for the project.

2.1. Clear the pinout

To start from a blank configuration, click on the Pinout menu and select Clear Pinouts. This resets the pinouts in the pinout view.

USBPD 0-pinoutConf.png


2.2. Configure the system timebase

For this simple example, we use SysTick as the system timebase. In the System Core section, select SYS and change the Timebase Source to SysTick.

USBPD 0-sysConf.png


2.3. Configure UCPD peripheral

In the Connectivity tab, select the UCPD1 peripheral and enable it in source mode. Under the NVIC Settings tab, enable UCPD global interrupts.

USBPD DRP 0-USBPDConf.png

Under the DMA Settings tab, add UCPD1_RX and UCPD1_TX DMA requests. Select DMA1 channel 4 for RX and DMA1 channel 2 for TX.

USBPD 1-UCPD1Conf.png
Info white.png Information
You can use any DMA channel you want except for DMA1_Channel1 which would be used later by the BSP drivers.

2.4. Configure FreeRTOS Middleware

In the Middleware section, enable FreeRTOS with CMSIS_V1 interface. Under the Config Parameters tab, change "TOTAL_HEAP_SIZE" to 7000 bytes.

USBPD 0-FreeRTOSConf.png


Then, under the Include Parameters tab, Enable eTaskGetState include definition.

USBPD 1-FreeRTOSConf.png


Info white.png Information

If an STM32G4 is used of a G0, LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY needs to be set to 3 instead of CubeMX's default value 5. In some cases with STM32G4, leaving it to 5 will get the code execution stuck in vPortValidateInterruptPriority function.

2.5. Configure USBPD Middleware

In the Middleware section, enable USBPD with the following configuration:

  • Port Configuration: Port 0: UCPD1
  • Stack Configuration: PD3 Full Stack
  • Timer service Source: TIM1

Under the PDO General Definitions tab, verify the following configuration:

  • Number of Sink PDOs for port 0: 1
  • Port 0 Sink PDO 1 0x0001912C (correspond to a simple 5V / 3A source)
  • Number of Sink PDOs for port 0: 1
  • Port 0 Source PDO 1 0x26019096 (correspond to a simple 5V / 3A source)
USBPD DRP 1-USBPDConf.png

The following table is extracted from USB Power Delivery Specification, Table 6-9 Fixed Supply PDO - Source. Used values and associated decoding for this project have been added to the table.

Bit(s) Description Used value Decoding
B31..30 Fixed supply 00b Fixed
B29 Dual-Role Power 0b No
B28 USB Suspend Supported 0b No
B27 Unconstrained Power 0b No
B26 USB Communications Capable 0b No
B25 Dual-Role Data 0b No
B24..22 Reserved - Shall de set to zero 000b No
B21..20 Peak Current 00b Peak Equals
B19..10 Voltage in 50mV units 0001100100b 5V
B9..0 Maximum current in 10mA units 0100101100b 3A

In the User port 0 Parameters section, enable Data role swap: Data role swap: Supported

In the Stack Port 0 parameters section, enable CAD role toggle: CAD role toggle: Supported

2.6. Configure ADC peripheral

For the Power Delivery stack to work, VBUS needs to be monitored. To do it, an ADC needs to be configured to measure the VBUS voltage and current.
As we are going to use the X-NUCLEO-DRP1M1 BSP, the ADC configuration does not need to be done in CubeMX.
As we need the ADC LL drivers for it to work properly, we still need to configure the ADC in CubeMX for it to include the driver files, but the actual configuration and init function will not be called in our project.

In the Analog section, enable ADC1 peripheral channel 0. Leave the configuration as default, as the X-NUCLEO-DRP1M1 BSP will reconfigure it.

USBPD 0-ADC1Conf.png


2.7. Configure I2C peripheral

As the X-NUCLEO-DRP1M1 shield includes a TCPP03-M20 that communicates via I2C, we need to enable the I2C peripheral in our project.

In the Connectivity section, enable I2C2 peripheral, in I2C mode. Leave the configuration as default, as the X-NUCLEO-DRP1M1 BSP will reconfigure it.

USBPD 0-I2C2Conf.png


Note: We need to enable the I2C2 peripheral in the CubeMX view for code generation to include the I2C drivers as we do for the ADC.

2.8. Configure Clocks

Under Clock Configuration main tab, change system clock mux to PLLCLK. It will set HCLK clock to 64MHz.

USBPD 0-Clock.png


Info white.png Information
The mandatory settings for the simple USB-PD sink application are finished.

The following part is highly recommended for debugging

2.9. [OPTIONAL] Configure Tracer for debug

2.9.1. Configure LPUART

On the STM32G0 Nucleo-64 board, the Virtual COM port connected to the ST-LINK is the LPUART1.

Warning white.png Warning
The default STM32CubeMX pins used by LPUART1 must be changed to match the STM32G0 Nucleo-64 hardware:
  • PA2 for TX
  • PA3 for RX.

In the Connectivity section, enable LPUART1 in Asynchronous mode, and baudrate 921600 bauds. Leave the rest as default.

USBPD 0-LPUARTConf.png


In the pinout view, left-click PA2 and PA3 to remap them to LPUART1_TX and LPUART1_RX.

USBPD 0b-LPUARTConf.png


Under the DMA Configuration tab, add a request for LPUART1_TX. Use DMA1 channel 3.

USBPD 1-LPUARTConf.png


Finally, under the NVIC Settings tab, enable LPUART1 global interrupts.

USBPD 2-LPUARTConf.png


2.9.2. Configure embedded tracer

In the Utilities section, select TRACER_EMB and use LPUART1 as the trace source.

USBPD 0-tracerConf.png


Then, go back to the USBPD middleware configuration and check the Tracer Source checkbox.

USBPD 1-tracerConf.png


2.9.3. Configure UCPD monitor firmware responder for debug

The firmware interactive stack responder can be activated if interaction with the USB-PD stack is needed, using the UCPD monitor tool STM32CubeMonUCPD. In the Utilities section, enable GUI_INTERFACE, then enter free text to describe the board.

USBPD 0-GUIConf.png


3. Configure project

Clock.png 5min

Under the Project Manager main tab, configure the minimum stack size to 0xC00 under the Project tab. This is a first value, which can be tuned later, depending on application needs.

USBPD DRP 0-projectConf.png


Under the Advanced Settings tab, change the LPUART driver to LL to save a bit of memory heap size. As we do not need ADC and I2C initialization functions (handled by the BSP drivers), uncheck Generate Code for the MX_I2C2_Init and MX_ADC1_Init functions.

USBPD 2-projectConf.png


4. Add BSP to the project

Clock.png 5min

Board Support Package (BSP) for the X-NUCLEO-DRP1M1 shield needs to be added to the project. The files need to be copied manually in the project's folder.
Get the latest BSP from GitHub x-cube-tcpp.

Manually copy the three following folders:

x-cube-tcpp
└── Drivers
    └── BSP
        ├── 📁STM32G0xx_Nucleo
        ├── 📁X-NUCLEO-DRP1M1
        └── Components
            └── 📁tcpp0203

Into:

<ProjectFolder>
└── Drivers
    └── BSP
        ├── 📁STM32G0xx_Nucleo
        ├── 📁X-NUCLEO-DRP1M1
        └── Components
            └── 📁tcpp0203

Then, create a file named ".extSettings" at the project's root folder (please mind the dot character in the filename) and fill it with the following code:

[ProjectFiles]
HeaderPath=Drivers\BSP\X-NUCLEO-DRP1M1;Drivers\BSP\STM32G0xx_Nucleo;Drivers\BSP\Components\tcpp0203
[Others]
Define=TCPP0203_SUPPORT;USE_STM32G0XX_NUCLEO
HALModule=
[Groups]
Drivers/BSP/X-NUCLEO-DRP1M1=Drivers/BSP/X-NUCLEO-DRP1M1/drp1m1_usbpd_pwr.c;Drivers/BSP/X-NUCLEO-DRP1M1/drp1m1_bus.c;
Drivers/BSP/STM32G0xx_Nucleo=Drivers/BSP/STM32G0xx_Nucleo/stm32g0xx_nucleo.c
Drivers/BSP/Components/tcpp0203=Drivers/BSP/Components/tcpp0203/tcpp0203.c;Drivers/BSP/Components/tcpp0203/tcpp0203_reg.c

This file is used to tell the code generator to include the BSP files when generating the project.

Info white.png Information
You can double-click the code zones to select it all, then copy it with Ctrl+c.

5. Generate code

Clock.png 5min

Save your file with Ctrl+s and select generate code if prompted. You can also generate code from the STM32CubeIDE menu by clicking Project/Generate Code, or by pressing Alt+K.

USBPD 0-projGen.png


A warning appears, informing that a proper HAL timebase is not defined. It is safer to use a dedicated timer as a HAL timebase source.
For this demonstration, the below warning can be ignored by clicking Yes.

USBPD 1-projGen.png


Info white.png Information
This becomes the recommended standard way of working in the forthcoming firmware package deliveries, especially when using CMSIS OS V2, which defines Systick as FreeRTOS™ timebase.

For this demonstration, the warning can be ignored by clicking Yes.


In this project, different folders can be found:

  • The USBPD folder contains the source files that we need to edit to enrich the Power Delivery application.
  • The Core folder contains the source files for the core of the project.
  • The Drivers folder contains the HAL drivers for the STM32, and the BSP for the Nucleo board and X-NUCLEO-SRC1M1 shield.
  • The Middleware folder contains the source files and the libraries for FreeRTOS™ and USB-PD.
  • The Utilities folder contains the GUI (UCPD monitor) and tracer embedded source files part.

The Drivers folder in the Explorer view of the project must contain the BSP folders added earlier.

USBPD DRP 0-projTree.png


6. Complete USB-PD application

Clock.png 20min

Now that the peripherals are initialized by STM32CubeMX, some minimum level of the application needs to be added:

  • drp1m1_conf.h file needs to be created from its template, and added to the project
  • User code needs to be added in several files

6.1. Add DRP1M1 configuration file

In the Drivers\BSP\X-NUCLEO-DRP1M1 folder you will find drp1m1_conf_template.h. Copy it to Core\Inc folder, and rename it src1m1_conf.h. It is the configuration file used for the X-NUCLEO-DRP1M1 BSP.

6.2. Modification in main.c

Info white.png Information
You can double-click the code zones to select it all, then copy it with Ctrl+c.

Add the following code between the /* USER CODE BEGIN-END Includes */ tags:

/* USER CODE BEGIN Includes */
#include "stm32g0xx_ll_utils.h"
#include "stm32g0xx_ll_system.h"
#include "stm32g0xx_ll_rcc.h"
#include "stm32g0xx_ll_cortex.h"
#include "usbpd_dpm_user.h"
/* USER CODE END Includes */

6.3. Modification in stm32g0xx_it.c

Add the following code between the /* USER CODE BEGIN-END Includes */ tags:

/* USER CODE BEGIN Includes */
#include "usbpd_def.h"
#include "usbpd_dpm_core.h"
#include "usbpd_hw_if.h"
#if defined(_GUI_INTERFACE)
#include "gui_api.h"
#endif /* _GUI_INTERFACE */
#if defined(TCPP0203_SUPPORT)
#include "drp1m1_conf.h"
#endif /* TCPP0203_SUPPORT */
/* USER CODE END Includes */

6.4. Modification in stm32g0xx_it.c

Add the following code between the /* USER CODE BEGIN-END PD */ tags:

/* USER CODE BEGIN PD */
#if defined(TCPP0203_SUPPORT)
extern I2C_HandleTypeDef    TCPP0X_HANDLE_I2C;
#endif /* TCPP0203_SUPPORT */
/* USER CODE END PD */

Add the following code between the /* USER CODE BEGIN-END 1 */ tags:

/* USER CODE BEGIN 1 */
#if defined(TCPP0203_SUPPORT)
/**
  * @brief This function handles I2C1 event global interrupt
  */
void I2C1_IRQHandler(void)
{
  if (TCPP0X_HANDLE_I2C.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR))
  {
    HAL_I2C_ER_IRQHandler(&TCPP0X_HANDLE_I2C);
  }
  else
  {
    HAL_I2C_EV_IRQHandler(&TCPP0X_HANDLE_I2C);
  }
}
/**
  * @brief  This function handles external line 4_15 interrupt request.
  *         (Associated to FLGn line in case of TCPP0203 management)
  * @retval None
  */
void TCPP0203_PORT0_FLG_EXTI_IRQHANDLER(void)
{
  /* Manage Flags */
  if (TCPP0203_PORT0_FLG_EXTI_IS_ACTIVE_FLAG() != RESET)
  {
    /* Call BSP USBPD PWR callback */
    BSP_USBPD_PWR_EventCallback(USBPD_PWR_TYPE_C_PORT_1);
    /* Clear Flag */
    TCPP0203_PORT0_FLG_EXTI_CLEAR_FLAG();
  }
  /* Process Button event in case EXTI has been generated by user button press */
  HAL_GPIO_EXTI_IRQHandler(USER_BUTTON_PIN);
}
#endif /* TCPP0203_SUPPORT */
/* USER CODE END 1 */

6.5. Modification in usbpd_dpm_user.h

Add the following code between the /* USER CODE BEGIN-END Include */ tags:

/* USER CODE BEGIN Include */
 #ifdef _RTOS
 #include "cmsis_os.h"
 #endif /* _RTOS */
/* USER CODE END Include */

Add the following code between the /* USER CODE BEGIN-END Typedef */ tags:

/* USER CODE BEGIN Typedef */
// TBC
/* USER CODE BEGIN typedef */

Add the following code between the ''/* USER CODE BEGIN-END Define */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN Define */
/*
 * USBPD FW version
 */
#define USBPD_FW_VERSION  0x05022020u
/*
 * USBPD Start Port Number
 */
#define USBPD_START_PORT_NUMBER  0u
/*
 * Number of thread defined by user to include in the low power control
 */
#define USBPD_USER_THREAD_COUNT    0
/* USER CODE END Define */

Add the following code between the ''/* USER CODE BEGIN-END Private_Variables */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN Private_Variables */
#if !defined(USBPD_DPM_USER_C)
extern USBPD_HandleTypeDef DPM_Ports[USBPD_PORT_COUNT];
#else
USBPD_HandleTypeDef DPM_Ports[USBPD_PORT_COUNT] =
{
  {
    .DPM_Reserved = 0,
#if defined(USBPDCORE_SNK_CAPA_EXT)
    .DPM_RcvSNKExtendedCapa = {0},                  /*!< SNK Extended Capability received by port partner                     */
#endif /* USBPDCORE_SNK_CAPA_EXT */
  }
};
#endif /* !USBPD_DPM_USER_C */
/* USER CODE END Private_Variables */

6.6. Modification in usbpd_dpm_user.c

Add the following code between the /* USER CODE BEGIN-END Private_Define */ tags:

/* USER CODE BEGIN Private_Typedef */
/** @brief  Sink Request characteritics Structure definition
  *
  */
typedef struct
{
  uint32_t RequestedVoltageInmVunits;              /*!< Sink request operating voltage in mV units       */
  uint32_t MaxOperatingCurrentInmAunits;           /*!< Sink request Max operating current in mA units   */
  uint32_t OperatingCurrentInmAunits;              /*!< Sink request operating current in mA units       */
  uint32_t MaxOperatingPowerInmWunits;             /*!< Sink request Max operating power in mW units     */
  uint32_t OperatingPowerInmWunits;                /*!< Sink request operating power in mW units         */
} USBPD_DPM_SNKPowerRequestDetails_TypeDef;
/* USER CODE END Private_Typedef */

Add the following code between the ''/* USER CODE BEGIN-END Private_Define */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN Private_Define */
#define DPM_TIMER_ENABLE_MSK      ((uint16_t)0x8000U)       /*!< Enable Timer Mask                                                        */
#define DPM_TIMER_READ_MSK        ((uint16_t)0x7FFFU)       /*!< Read Timer Mask                                                          */
#define DPM_BOX_MESSAGES_MAX      30u
#define DPM_NO_SRC_PDO_FOUND      0xFFU        /*!< No match found between Received SRC PDO and SNK capabilities                             */
#if defined(_RTOS)
#if (osCMSIS < 0x20000U)
#define DPM_USER_STACK_SIZE_ADDON_FOR_CMSIS              1
#else
#define DPM_USER_STACK_SIZE_ADDON_FOR_CMSIS              3
#endif /* osCMSIS < 0x20000U */
#define FREERTOS_DPM_PRIORITY                    osPriorityLow
#define FREERTOS_DPM_STACK_SIZE                 (300U * DPM_USER_STACK_SIZE_ADDON_FOR_CMSIS)
#if (osCMSIS < 0x20000U)
osThreadDef(DPM, USBPD_DPM_UserExecute, FREERTOS_DPM_PRIORITY, 0, FREERTOS_DPM_STACK_SIZE);
#else /* osCMSIS >= 0x20000U */
osThreadAttr_t DPM_Thread_Atrr = {
  .name       = "DPM",
  .priority   = FREERTOS_DPM_PRIORITY, /*osPriorityLow,*/
  .stack_size = FREERTOS_DPM_STACK_SIZE
};
#endif /* osCMSIS < 0x20000U */
#endif /* _RTOS */
/* USER CODE END Private_Define */

Add the following code between the /* USER CODE BEGIN-END Private_Macro */ tags:

/* USER CODE BEGIN Private_Macro */
#if (osCMSIS < 0x20000U)
#define DPM_START_TIMER(_PORT_,_TIMER_,_TIMEOUT_)   do{                                                               \
                                                      DPM_Ports[_PORT_]._TIMER_ = (_TIMEOUT_) |  DPM_TIMER_ENABLE_MSK;\
                                                      osMessagePut(DPMMsgBox,DPM_USER_EVENT_TIMER, 0);                \
                                                    }while(0);
#else
#define DPM_START_TIMER(_PORT_,_TIMER_,_TIMEOUT_)   do{                                                               \
                                                      uint32_t event = DPM_USER_EVENT_TIMER;                          \
                                                      DPM_Ports[_PORT_]._TIMER_ = (_TIMEOUT_) |  DPM_TIMER_ENABLE_MSK;\
                                                      (void)osMessageQueuePut(DPMMsgBox, &event, 0U, NULL);           \
                                                    }while(0);
#endif /* osCMSIS < 0x20000U */
#define IS_DPM_TIMER_RUNNING(_PORT_, _TIMER_)       ((DPM_Ports[_PORT_]._TIMER_ & DPM_TIMER_READ_MSK) > 0)
#define IS_DPM_TIMER_EXPIRED(_PORT_, _TIMER_)       (DPM_TIMER_ENABLE_MSK == DPM_Ports[_PORT_]._TIMER_)
/* USER CODE END Private_Macro */

Add the following code between the /* USER CODE BEGIN-END Private_Variables */ tags:

/* USER CODE BEGIN Private_Variables */
#if defined(_RTOS)
osMessageQId  DPMMsgBox;
#endif /* _RTOS */
extern USBPD_ParamsTypeDef DPM_Params[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */

Add the following code between the /* USER CODE BEGIN-END Private_Function_Prototypes */ tags:

/* USER CODE BEGIN USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */
static  void     DPM_SNK_BuildRDOfromSelectedPDO(uint8_t PortNum, uint8_t IndexSrcPDO, USBPD_DPM_SNKPowerRequestDetails_TypeDef* PtrRequestPowerDetails,
                                             USBPD_SNKRDO_TypeDef* Rdo, USBPD_CORE_PDO_Type_TypeDef *PtrPowerObject);
static uint32_t  DPM_FindVoltageIndex(uint32_t PortNum, USBPD_DPM_SNKPowerRequestDetails_TypeDef* PtrRequestPowerDetails);
#if defined(_RTOS)
static uint32_t CheckDPMTimers(void);
#endif /* _RTOS */
static USBPD_StatusTypeDef DPM_TurnOnPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
static USBPD_StatusTypeDef DPM_TurnOffPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
static void                DPM_AssertRp(uint8_t PortNum);
static void                DPM_AssertRd(uint8_t PortNum);
/* USER CODE END USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_UserInit */ tags:

/* USER CODE BEGIN USBPD_DPM_UserInit */
  /* PWR SET UP */
  if(USBPD_OK !=  USBPD_PWR_IF_Init())
  {
    return USBPD_ERROR;
  }
#if defined(_RTOS)
#if (osCMSIS < 0x20000U)
  osMessageQDef(MsgBox, DPM_BOX_MESSAGES_MAX, uint32_t);
  DPMMsgBox = osMessageCreate(osMessageQ(MsgBox), NULL);
  if(NULL == osThreadCreate(osThread(DPM), &DPMMsgBox))
#else
  DPMMsgBox = osMessageQueueNew (DPM_BOX_MESSAGES_MAX, sizeof(uint32_t), NULL);
  if (NULL == osThreadNew(USBPD_DPM_UserExecute, &DPMMsgBox, &DPM_Thread_Atrr))
#endif /* osCMSIS < 0x20000U */
  {
    return USBPD_ERROR;
  }
#endif /* _RTOS */
  return USBPD_OK;
/* USER CODE END USBPD_DPM_UserInit */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_UserExecute */ tags:

/* USER CODE BEGIN USBPD_DPM_UserExecute */
#if defined(_RTOS)
  /* User code implementation */
  uint32_t _timing = osWaitForever;
  osMessageQId  queue = *(osMessageQId *)argument;
  do
  {
    osEvent event = osMessageGet(queue, _timing);
    switch (((DPM_USER_EVENT)event.value.v & 0xF))
    {
    case DPM_USER_EVENT_TIMER:
      {
        break;
      }
    default:
      break;
    }
    _timing = CheckDPMTimers();
  }
  while(1);
#endif /* _RTOS */
/* USER CODE END USBPD_DPM_UserExecute */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_UserCableDetection */ tags:

/* USER CODE BEGIN USBPD_DPM_UserCableDetection */
  switch(State)
  {
  case USBPD_CAD_EVENT_ATTACHED:
  case USBPD_CAD_EVENT_ATTEMC:
    {
    if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
    {
      if (USBPD_OK != USBPD_PWR_IF_VBUSEnable(PortNum))
      {
        /* Should not occur */
        osDelay(6000);
        NVIC_SystemReset();
      }
    }
    DPM_Ports[PortNum].DPM_IsConnected = 1;
    break;
    }
  case USBPD_CAD_EVENT_DETACHED :
  case USBPD_CAD_EVENT_EMC :
  default :

    if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
    {
      if (USBPD_OK != USBPD_PWR_IF_VBUSDisable(PortNum))
      {
        /* Should not occur */
        while(1);
      }
    }
    /* reset all values received from port partner */
    memset(&DPM_Ports[PortNum], 0, sizeof(DPM_Ports[PortNum]));
    break;
  }
/* USER CODE END USBPD_DPM_UserCableDetection */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_HardReset */ tags:

/* USER CODE BEGIN USBPD_DPM_HardReset */
  switch (Status)
  {
  case USBPD_HR_STATUS_WAIT_VBUS_VSAFE0V:
    if (USBPD_PORTPOWERROLE_SRC == CurrentRole)
    {
      /* Reset the power supply */
      DPM_TurnOffPower(PortNum, USBPD_PORTPOWERROLE_SRC);
    }
    break;
  case USBPD_HR_STATUS_WAIT_VBUS_VSAFE5V:
    if (CurrentRole == USBPD_PORTPOWERROLE_SRC)
    {
      /* Power on the power supply */
      DPM_TurnOnPower(PortNum, CurrentRole);
    }
    break;
  default:
    break;
  }
/* USER CODE END USBPD_DPM_HardReset */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_Notification */ tags:

/* USER CODE BEGIN USBPD_DPM_Notification */
  switch(EventVal)
  {
    /***************************************************************************
                              Power Notification
    */
    case USBPD_NOTIFY_POWER_EXPLICIT_CONTRACT :
      /* Power ready means an explicit contract has been establish and Power is available */
      break;
    /*
                              End Power Notification
     ***************************************************************************/
    /***************************************************************************
                               REQUEST ANSWER NOTIFICATION
    */
    case USBPD_NOTIFY_REQUEST_ACCEPTED:
      /* Update DPM_RDOPosition only if current role is SNK */
      if (USBPD_PORTPOWERROLE_SNK == DPM_Params[PortNum].PE_PowerRole)
      {
        USBPD_SNKRDO_TypeDef rdo;
        rdo.d32                             = DPM_Ports[PortNum].DPM_RequestDOMsg;
        DPM_Ports[PortNum].DPM_RDOPosition  = rdo.GenericRDO.ObjectPosition;
#ifdef _GUI_INTERFACE
        if (NULL != DPM_GUI_SaveInfo)
        {
          DPM_GUI_SaveInfo(PortNum, USBPD_CORE_DATATYPE_RDO_POSITION, (uint8_t*)&DPM_Ports[PortNum].DPM_RDOPosition, 4);
        }
#endif /* _GUI_INTERFACE */
      }
    break;
    /*
                              End REQUEST ANSWER NOTIFICATION
     ***************************************************************************/
    case USBPD_NOTIFY_STATE_SNK_READY:
      {
      }
      break;
    case USBPD_NOTIFY_STATE_SRC_DISABLED:
      {
        /* SINK Port Partner is not PD capable. Legacy cable may have been connected
           In this state, VBUS is set to 5V */
      }
      break;
    default :
      break;
  }
/* USER CODE END USBPD_DPM_Notification */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_GetDataInfo */ tags:

/* USER CODE BEGIN USBPD_DPM_GetDataInfo */
  /* Check type of information targeted by request */
  switch (DataId)
  {
    /* Case Port Source PDO Data information :
    Case Port SINK PDO Data information :
    Call PWR_IF PDO reading request.
    */
  case USBPD_CORE_DATATYPE_SRC_PDO :
  case USBPD_CORE_DATATYPE_SNK_PDO :
    USBPD_PWR_IF_GetPortPDOs(PortNum, DataId, Ptr, Size);
    *Size *= 4;
    break;
    /* Case Requested voltage value Data information */
  case USBPD_CORE_DATATYPE_REQ_VOLTAGE :
    *Size = 4;
    (void)memcpy((uint8_t*)Ptr, (uint8_t *)&DPM_Ports[PortNum].DPM_RequestedVoltage, *Size);
    break;
  case USBPD_CORE_PPS_STATUS :
    {
      /* Get current drawn by sink */
      USBPD_PPSSDB_TypeDef pps_status = {0};
      *Size = 4;
      (void)memcpy((uint8_t*)Ptr, (uint8_t *)&pps_status.d32, *Size);
    }
    break;
#if defined(USBPDCORE_SNK_CAPA_EXT)
  case USBPD_CORE_SNK_EXTENDED_CAPA :
    {
      *Size = sizeof(USBPD_SKEDB_TypeDef);
      memcpy((uint8_t*)Ptr, (uint8_t *)&DPM_USER_Settings[PortNum].DPM_SNKExtendedCapa, *Size);
     }
     break;
#endif /* USBPDCORE_SNK_CAPA_EXT */
  default :
    *Size = 0;
    break;
  }
/* USER CODE END USBPD_DPM_GetDataInfo */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_SetDataInfo */ tags:

/* USER CODE BEGIN USBPD_DPM_SetDataInfo */
  uint32_t index;
  /* Check type of information targeted by request */
  switch (DataId)
  {
  /* Case Received Request PDO Data information :
   */
  case USBPD_CORE_DATATYPE_RDO_POSITION :
    if (Size == 4)
    {
      uint8_t* temp;
      temp = (uint8_t*)&DPM_Ports[PortNum].DPM_RDOPosition;
      (void)memcpy(temp, Ptr, Size);
      DPM_Ports[PortNum].DPM_RDOPositionPrevious = *Ptr;
      temp = (uint8_t*)&DPM_Ports[PortNum].DPM_RDOPositionPrevious;
      (void)memcpy(temp, Ptr, Size);
    }
    break;
    /* Case Received Source PDO values Data information :
    */
  case USBPD_CORE_DATATYPE_RCV_SRC_PDO :
    if (Size <= (USBPD_MAX_NB_PDO * 4))
    {
      uint8_t* rdo;
      DPM_Ports[PortNum].DPM_NumberOfRcvSRCPDO = (Size / 4);
      /* Copy PDO data in DPM Handle field */
      for (index = 0; index < (Size / 4); index++)
      {
        rdo = (uint8_t*)&DPM_Ports[PortNum].DPM_ListOfRcvSRCPDO[index];
        (void)memcpy(rdo, (Ptr + (index * 4u)), (4u * sizeof(uint8_t)));
      }
    }
    break;
    /* Case Received Sink PDO values Data information :
    */
  case USBPD_CORE_DATATYPE_RCV_SNK_PDO :
    if (Size <= (USBPD_MAX_NB_PDO * 4))
    {
      uint8_t* rdo;
      DPM_Ports[PortNum].DPM_NumberOfRcvSNKPDO = (Size / 4);
      /* Copy PDO data in DPM Handle field */
      for (index = 0; index < (Size / 4); index++)
      {
        rdo = (uint8_t*)&DPM_Ports[PortNum].DPM_ListOfRcvSNKPDO[index];
        (void)memcpy(rdo, (Ptr + (index * 4u)), (4u * sizeof(uint8_t)));
      }
    }
    break;
    /* Case Received Request PDO Data information :
    */
  case USBPD_CORE_DATATYPE_RCV_REQ_PDO :
    if (Size == 4)
    {
      uint8_t* rdo;
      rdo = (uint8_t*)&DPM_Ports[PortNum].DPM_RcvRequestDOMsg;
      (void)memcpy(rdo, Ptr, Size);
    }
    break;
  case USBPD_CORE_PPS_STATUS :
    {
      uint8_t*  ext_capa;
      ext_capa = (uint8_t*)&DPM_Ports[PortNum].DPM_RcvPPSStatus;
      memcpy(ext_capa, Ptr, Size);
    }
    break;
#if defined(USBPDCORE_SNK_CAPA_EXT)
  case USBPD_CORE_SNK_EXTENDED_CAPA :
    {
      uint8_t*  _snk_ext_capa;
      _snk_ext_capa = (uint8_t*)&DPM_Ports[PortNum].DPM_RcvSNKExtendedCapa;
      memcpy(_snk_ext_capa, Ptr, Size);
    }
    break;
#endif /* USBPDCORE_SNK_CAPA_EXT */
    /* In case of unexpected data type (Set request could not be fulfilled) :
    */
  default :
    DPM_USER_DEBUG_TRACE(PortNum, "ADVICE: update USBPD_DPM_SetDataInfo:%d", DataId);
    break;
  }
/* USER CODE END USBPD_DPM_SetDataInfo */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_EvaluateRequest */ tags:

/* USER CODE BEGIN USBPD_DPM_EvaluateRequest */
  USBPD_StatusTypeDef _retr = USBPD_REJECT;
  USBPD_PDO_TypeDef pdo;
  USBPD_SNKRDO_TypeDef rdo;
  /* read the request value received */
  rdo.d32 = DPM_Ports[PortNum].DPM_RcvRequestDOMsg;
  /* Search PDO in Port Source PDO list, that corresponds to Position provided in Request RDO */
  if (USBPD_PWR_IF_SearchRequestedPDO(PortNum,  rdo.GenericRDO.ObjectPosition, &pdo.d32) == USBPD_OK)
  {
    /* Evaluate the request */
    if(pdo.GenericPDO.PowerObject == USBPD_CORE_PDO_TYPE_FIXED)
    {
      if((rdo.FixedVariableRDO.OperatingCurrentIn10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits)
         || (rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits))
      {
        /* Sink requests too much maximum operating current */
        /* USBPD_DPM_EvaluateRequest: Sink requests too much maximum operating current */
        _retr =  USBPD_REJECT;
      }
      else
      {
        /* Save the power object */
        *PtrPowerObject = pdo.GenericPDO.PowerObject;
        /* Set RDO position and requested voltage in DPM port structure */
        DPM_Ports[PortNum].DPM_RequestedVoltage = pdo.SRCFixedPDO.VoltageIn50mVunits * 50;
        DPM_Ports[PortNum].DPM_RDOPositionPrevious = DPM_Ports[PortNum].DPM_RDOPosition;
        DPM_Ports[PortNum].DPM_RDOPosition = rdo.GenericRDO.ObjectPosition;
         _retr = USBPD_ACCEPT;
      }
    }
  }
  return _retr;
/* USER CODE END USBPD_DPM_EvaluateRequest */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_EvaluateCapabilities */ tags:

/* USER CODE BEGIN USBPD_DPM_SNK_EvaluateCapabilities */
  USBPD_PDO_TypeDef  fixed_pdo;
  USBPD_SNKRDO_TypeDef rdo;
  USBPD_HandleTypeDef *pdhandle = &DPM_Ports[PortNum];
  USBPD_USER_SettingsTypeDef *puser = (USBPD_USER_SettingsTypeDef *)&DPM_USER_Settings[PortNum];
  USBPD_DPM_SNKPowerRequestDetails_TypeDef snkpowerrequestdetails;
  uint32_t pdoindex, size;
  uint32_t snkpdolist[USBPD_MAX_NB_PDO];
  USBPD_PDO_TypeDef snk_fixed_pdo;
  /* USBPD_DPM_EvaluateCapabilities: Port Partner Requests Max Voltage */
  /* Find the Pdo index for the requested voltage */
  pdoindex = DPM_FindVoltageIndex(PortNum, &snkpowerrequestdetails);
  /* Initialize RDO */
  rdo.d32 = 0;
  /* If no valid SNK PDO or if no SRC PDO match found (index>=nb of valid received SRC PDOs or function returned DPM_NO_SRC_PDO_FOUND*/
  if (pdoindex >= pdhandle->DPM_NumberOfRcvSRCPDO)
  {
#if defined(_TRACE)
    USBPD_TRACE_Add(USBPD_TRACE_DEBUG, PortNum, 0, (uint8_t *) "PE_EvaluateCapability: could not find desired voltage", sizeof("PE_EvaluateCapability: could not find desired voltage"));
#endif /* _TRACE */
    fixed_pdo.d32 = pdhandle->DPM_ListOfRcvSRCPDO[0];
    /* Read SNK PDO list for retrieving useful data to fill in RDO */
    USBPD_PWR_IF_GetPortPDOs(PortNum, USBPD_CORE_DATATYPE_SNK_PDO, (uint8_t*)&snkpdolist[0], &size);
    /* Store value of 1st SNK PDO (Fixed) in local variable */
    snk_fixed_pdo.d32 = snkpdolist[0];
    rdo.FixedVariableRDO.ObjectPosition = 1;
    rdo.FixedVariableRDO.OperatingCurrentIn10mAunits  =  fixed_pdo.SRCFixedPDO.MaxCurrentIn10mAunits;
    rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits = puser->DPM_SNKRequestedPower.MaxOperatingCurrentInmAunits / 10;
    rdo.FixedVariableRDO.CapabilityMismatch = 1;
    rdo.FixedVariableRDO.USBCommunicationsCapable = snk_fixed_pdo.SNKFixedPDO.USBCommunicationsCapable;
    DPM_Ports[PortNum].DPM_RequestedCurrent = puser->DPM_SNKRequestedPower.MaxOperatingCurrentInmAunits;
    pdhandle->DPM_RequestDOMsg = rdo.d32;
    *PtrPowerObjectType = USBPD_CORE_PDO_TYPE_FIXED;
    *PtrRequestData     = rdo.d32;
    pdhandle->DPM_RequestedVoltage = 5000;
    return;
  }
  DPM_SNK_BuildRDOfromSelectedPDO(PortNum, pdoindex, &snkpowerrequestdetails,&rdo, PtrPowerObjectType);
  *PtrRequestData = pdhandle->DPM_RequestDOMsg;
/* USER CODE END USBPD_DPM_SNK_EvaluateCapabilities */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_PowerRoleSwap */ tags:

/* USER CODE BEGIN USBPD_DPM_PowerRoleSwap */
    switch (Status)
    {
    case USBPD_PRS_STATUS_VBUS_OFF:
      if (CurrentRole == USBPD_PORTPOWERROLE_SRC)
      {
#if defined(_VCONN_SUPPORT)
        /* This variable is used to avoid VCONN disable during HR procedure */
        uint32_t _vconnstate = DPM_Params[PortNum].VconnStatus;
        DPM_Params[PortNum].VconnStatus = USBPD_FALSE;
#endif /* _VCONN_SUPPORT */
        /* In case of power role swap keep VCONN On */
        DPM_TurnOffPower(PortNum, CurrentRole);
#if defined(_VCONN_SUPPORT)
        /* restore vconn status */
        DPM_Params[PortNum].VconnStatus = _vconnstate;
#endif /* _VCONN_SUPPORT */
      }
      break;
    case USBPD_PRS_STATUS_SRC_RP2RD:
      DPM_AssertRd(PortNum);
      USBPD_DPM_WaitForTime(5);
      break;
    case USBPD_PRS_STATUS_SNK_RD2RP:
      DPM_AssertRp(PortNum);
      break;
    case USBPD_PRS_STATUS_VBUS_ON:
      DPM_TurnOnPower(PortNum, CurrentRole);
      break;
    default:
      break;
    }
/* USER CODE END USBPD_DPM_PowerRoleSwap */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_ExtendedMessageReceived */ tags:

/* USER CODE BEGIN USBPD_DPM_ExtendedMessageReceived */
  if (DataSize == 0)
  {
    /* No data received. */
    return;
  }
  switch(MsgType)
  {
    default:
      break;
  }
/* USER CODE END USBPD_DPM_ExtendedMessageReceived */

Add the following code between the /* USER CODE BEGIN-END USB¨PD_DPM_RequestMessageRequest */ tags:

/* USER CODE BEGIN USBPD_DPM_RequestMessageRequest */
  uint32_t voltage, allowablepower;
  USBPD_SNKRDO_TypeDef rdo;
  USBPD_PDO_TypeDef  pdo;
  USBPD_CORE_PDO_Type_TypeDef pdo_object;
  USBPD_USER_SettingsTypeDef *puser = (USBPD_USER_SettingsTypeDef *)&DPM_USER_Settings[PortNum];
  USBPD_DPM_SNKPowerRequestDetails_TypeDef request_details;
  rdo.d32 = 0;
  /* selected SRC PDO */
  pdo.d32 = DPM_Ports[PortNum].DPM_ListOfRcvSRCPDO[(IndexSrcPDO - 1)];
  voltage = RequestedVoltage;
  allowablepower = (puser->DPM_SNKRequestedPower.MaxOperatingCurrentInmAunits * RequestedVoltage) / 1000;
  if (USBPD_TRUE == USBPD_DPM_SNK_EvaluateMatchWithSRCPDO(PortNum, pdo.d32, &voltage, &allowablepower))
  {
    /* Check that voltage has been correctly selected */
    if (RequestedVoltage == voltage)
    {
      request_details.RequestedVoltageInmVunits    = RequestedVoltage;
      request_details.OperatingCurrentInmAunits    = (1000 * allowablepower)/RequestedVoltage;
      request_details.MaxOperatingCurrentInmAunits = puser->DPM_SNKRequestedPower.MaxOperatingCurrentInmAunits;
      request_details.MaxOperatingPowerInmWunits   = puser->DPM_SNKRequestedPower.MaxOperatingPowerInmWunits;
      request_details.OperatingPowerInmWunits      = puser->DPM_SNKRequestedPower.OperatingPowerInmWunits;
      DPM_SNK_BuildRDOfromSelectedPDO(PortNum, (IndexSrcPDO - 1), &request_details, &rdo, &pdo_object);
      _status = USBPD_PE_Send_Request(PortNum, rdo.d32, pdo_object);
    }
  }
  else
  {
    DPM_USER_DEBUG_TRACE(PortNum, "REQUEST not accepted by the stack");
  }
/* USER CODE END USBPD_DPM_RequestMessageRequest */


6.7. Modification in usbpd_pwr_if.c

Add the following code between the /* USER CODE BEGIN-END Private_Variables */ tags:

/* USER CODE BEGIN Private_Variables */
/**
  * @brief  USBPD Port PDO Storage array declaration
  */
USBPD_PWR_Port_PDO_Storage_TypeDef PWR_Port_PDO_Storage[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */

Add the following code between the /* USER CODE BEGIN-END USBPD_PWR_IF_Init */ tags:

/* USER CODE BEGIN USBPD_PWR_IF_Init */
  USBPD_StatusTypeDef _status = USBPD_OK;

  /* Set links to PDO values and Port 0 number (defined in PDO arrays in H file). */
  PWR_Port_PDO_Storage[USBPD_PORT_0].SourcePDO.ListOfPDO = (uint32_t *) PORT0_PDO_ListSRC;
  PWR_Port_PDO_Storage[USBPD_PORT_0].SourcePDO.NumberOfPDO = &USBPD_NbPDO[1];

  return _status;
/* USER CODE END USBPD_PWR_IF_Init */

Add the following code between the /* USER CODE BEGIN-END USBPD_PWR_IF_SetProfile */ tags:

/* USER CODE BEGIN USBPD_PWR_IF_SetProfile */
  USBPD_PDO_TypeDef        _pdo;
  USBPD_SNKRDO_TypeDef     _rdo;

  _rdo.d32 = DPM_Ports[PortNum].DPM_RcvRequestDOMsg;
  _pdo.d32 = PORT0_PDO_ListSRC[0];

  return (BSP_ERROR_NONE ==BSP_USBPD_PWR_VBUSSetVoltage_Fixed(PortNum,
                                                    _pdo.SRCFixedPDO.VoltageIn50mVunits * 50,
                                                    (_rdo.FixedVariableRDO.OperatingCurrentIn10mAunits * 10),
                                                    (_rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits * 10)
                                                    )? USBPD_OK : USBPD_ERROR);
/* USER CODE END USBPD_PWR_IF_SetProfile */

Add the following code between the /* USER CODE BEGIN-END USBPD_PWR_IF_GetPortPDOs */ tags:

/* USER CODE BEGIN USBPD_PWR_IF_GetPortPDOs */
  uint32_t   nbpdo, index, nb_valid_pdo = 0;
  uint32_t   *ptpdoarray = NULL;
  USBPD_PDO_TypeDef pdo_first;
  USBPD_PDO_TypeDef pdo;

  /* Check if valid port */
  if (USBPD_PORT_IsValid(PortNum))
  {
    /* According to the type of PDO to be read, set the pointer on values and number of elements */
    switch(DataId)
    {
    case USBPD_CORE_DATATYPE_SRC_PDO :
      nbpdo = *PWR_Port_PDO_Storage[PortNum].SourcePDO.NumberOfPDO;
      ptpdoarray = PWR_Port_PDO_Storage[PortNum].SourcePDO.ListOfPDO;
      /* Save the 1st PDO */
      pdo_first.d32 = *ptpdoarray;
      /* Reset un-chunked bit if current revision is PD2.0*/
      if (USBPD_SPECIFICATION_REV2 == DPM_Params[PortNum].PE_SpecRevision)
      {
        pdo_first.SRCFixedPDO.UnchunkedExtendedMessage  = USBPD_PDO_SRC_FIXED_UNCHUNK_NOT_SUPPORTED;
      }
      break;
    default :
      nbpdo = 0;
      break;
    }

    /* Copy PDO data in output buffer */
    for (index = 0; index < nbpdo; index++)
    {
      pdo.d32 = *ptpdoarray;
      /* Copy only PDO (and not APDO in case of current revision is PD2.0) */
      if ((USBPD_SPECIFICATION_REV2 == DPM_Params[PortNum].PE_SpecRevision)
          && (pdo.GenericPDO.PowerObject == USBPD_CORE_PDO_TYPE_APDO))
      {
      }
      else
      {
        /* Copy 1st PDO as potentially FRS or UNCHUNKED bits have been reset */
        if (0 == index)
        {
          (void)memcpy(Ptr, (uint8_t*)&pdo_first.d32, 4u);
        }
        else
        {
          (void)memcpy((Ptr + (nb_valid_pdo * 4u)), (uint8_t*)ptpdoarray, 4u);
        }
        nb_valid_pdo++;
      }
      ptpdoarray++;
    }
    /* Set the number of read PDO (number of u32 elements); */
    *Size = nb_valid_pdo;
  }
/* USER CODE END USBPD_PWR_IF_GetPortPDOs */

Add the following code between the /* USER CODE BEGIN-END USBPD_PWR_IF_SearchRequestedPDO */ tags:

/* USER CODE BEGIN USBPD_PWR_IF_SearchRequestedPDO */
  if((RdoPosition == 0) || (RdoPosition > *PWR_Port_PDO_Storage[PortNum].SourcePDO.NumberOfPDO))
  {
    /* Invalid PDO index */
  return USBPD_FAIL;
  }

  *Pdo = PWR_Port_PDO_Storage[PortNum].SourcePDO.ListOfPDO[RdoPosition - 1];
  return USBPD_OK;
/* USER CODE END USBPD_PWR_IF_SearchRequestedPDO */

6.8. Modification in usbpd_dpm_user.c

Add the following code between the /* USER CODE BEGIN-END Private_Variables */ tags:

/* USER CODE BEGIN Private_Variables */
USBPD_HandleTypeDef DPM_Ports[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */

Add the following code between the /* USER CODE BEGIN-END USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */ tags:

/* USER CODE BEGIN USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */
static USBPD_StatusTypeDef DPM_TurnOnPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
static USBPD_StatusTypeDef DPM_TurnOffPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
/* USER CODE END USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_UserInit */ tags:

/* USER CODE BEGIN USBPD_DPM_UserInit */
  /* PWR SET UP */
  if(USBPD_OK !=  USBPD_PWR_IF_Init())
  {
    return USBPD_ERROR;
  }
  return USBPD_OK;
/* USER CODE END USBPD_DPM_UserInit */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_UserCableDetection */ tags:

/* USER CODE BEGIN USBPD_DPM_UserCableDetection */
  switch(State)
  {
  case USBPD_CAD_EVENT_ATTACHED:
  case USBPD_CAD_EVENT_ATTEMC:
    {
      if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
      {
        if (USBPD_OK != USBPD_PWR_IF_VBUSEnable(PortNum))
        {
          /* Should not occur */
          osDelay(6000);
          NVIC_SystemReset();
        }
      }
      break;
    }
  case USBPD_CAD_EVENT_DETACHED :
  case USBPD_CAD_EVENT_EMC :
  default :
    {
      if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
      {
        if (USBPD_OK != USBPD_PWR_IF_VBUSDisable(PortNum))
        {
          /* Should not occur */
          while(1);
        }
      }
      break;
    }
  }
/* USER CODE END USBPD_DPM_UserCableDetection */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_HardReset */ tags:

/* USER CODE BEGIN USBPD_DPM_HardReset */
  switch (Status)
  {
  case USBPD_HR_STATUS_WAIT_VBUS_VSAFE0V:
    if (USBPD_PORTPOWERROLE_SRC == CurrentRole)
    {
      /* Reset the power supply */
      DPM_TurnOffPower(PortNum, USBPD_PORTPOWERROLE_SRC);
    }
    break;

  case USBPD_HR_STATUS_WAIT_VBUS_VSAFE5V:
    if (CurrentRole == USBPD_PORTPOWERROLE_SRC)
    {
      /* Power on the power supply */
      DPM_TurnOnPower(PortNum, CurrentRole);
    }
    break;

  default:
    break;
  }
/* USER CODE END USBPD_DPM_HardReset */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_GetDataInfo */ tags:

/* USER CODE BEGIN USBPD_DPM_GetDataInfo */
  /* Check type of information targeted by request */
  switch(DataId)
  {
    case USBPD_CORE_DATATYPE_REQ_VOLTAGE:       /*!< Get voltage value requested for BIST tests, expect 5V */
      *Size = 4;
      (void)memcpy((uint8_t*)Ptr, (uint8_t *)&DPM_Ports[PortNum].DPM_RequestedVoltage, *Size);
      break;
    case USBPD_CORE_DATATYPE_SRC_PDO:           /*!< Handling of port Source PDO                           */
      USBPD_PWR_IF_GetPortPDOs(PortNum, DataId, Ptr, Size);
      *Size *= 4;
      break;
 // case USBPD_CORE_PPS_STATUS:                 /*!< PPS Status message content                            */
    // break;
 // case USBPD_CORE_SNK_EXTENDED_CAPA:          /*!< Retrieve of Sink Extended capability message content */
    // break;
 // case USBPD_CORE_INFO_STATUS:                /*!< Information status message content                    */
    // break;
 // case USBPD_CORE_MANUFACTURER_INFO:          /*!< Retrieve of Manufacturer info message content         */
    // break;
 // case USBPD_CORE_BATTERY_STATUS:             /*!< Retrieve of Battery status message content            */
    // break;
 // case USBPD_CORE_BATTERY_CAPABILITY:         /*!< Retrieve of Battery capability message content        */
    // break;
    default:
      DPM_USER_DEBUG_TRACE(PortNum, "ADVICE: update USBPD_DPM_GetDataInfo:%d", DataId);
      break;
  }
/* USER CODE END USBPD_DPM_GetDataInfo */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_SetDataInfo */ tags:

/* USER CODE BEGIN USBPD_DPM_SetDataInfo */
  /* Check type of information targeted by request */
  switch(DataId)
  {
   case USBPD_CORE_DATATYPE_RDO_POSITION:   /*!< Reset the PDO position selected by the sink only          */
    if (Size == 4)
    {
      uint8_t* temp;
      temp = (uint8_t*)&DPM_Ports[PortNum].DPM_RDOPosition;
      (void)memcpy(temp, Ptr, Size);
      DPM_Ports[PortNum].DPM_RDOPositionPrevious = *Ptr;
      temp = (uint8_t*)&DPM_Ports[PortNum].DPM_RDOPositionPrevious;
      (void)memcpy(temp, Ptr, Size);
    }
    break;
 // case USBPD_CORE_DATATYPE_RCV_SRC_PDO:   /*!< Storage of Received Source PDO values                     */
   // break;
 // case USBPD_CORE_DATATYPE_RCV_SNK_PDO:   /*!< Storage of Received Sink PDO values                       */
   // break;
    case USBPD_CORE_DATATYPE_RCV_REQ_PDO :  /*!< Storage of Received Sink Request PDO value                */
      if (Size == 4)
      {
        memcpy((uint8_t *)&DPM_Ports[PortNum].DPM_RcvRequestDOMsg,  Ptr, 4);
      }
      break;
 // case USBPD_CORE_INFO_STATUS:            /*!< Information status message content                        */
   // break;
 // case USBPD_CORE_ALERT:                  /*!< Storing of received Alert message content                 */
   // break;
 // case USBPD_CORE_GET_MANUFACTURER_INFO:  /*!< Storing of received Get Manufacturer info message content */
   // break;
 // case USBPD_CORE_GET_BATTERY_STATUS:     /*!< Storing of received Get Battery status message content    */
   // break;
 // case USBPD_CORE_GET_BATTERY_CAPABILITY: /*!< Storing of received Get Battery capability message content*/
   // break;
 // case USBPD_CORE_SNK_EXTENDED_CAPA:      /*!< Storing of Sink Extended capability message content       */
   // break;
    default:
      DPM_USER_DEBUG_TRACE(PortNum, "ADVICE: update USBPD_DPM_SetDataInfo:%d", DataId);
      break;
  }
/* USER CODE END USBPD_DPM_SetDataInfo */

Add the following code between the /* USER CODE BEGIN-END USBPD_DPM_EvaluateRequest */ tags:

/* USER CODE BEGIN USBPD_DPM_EvaluateRequest */
  USBPD_StatusTypeDef _retr = USBPD_REJECT;
  USBPD_PDO_TypeDef pdo;
  USBPD_SNKRDO_TypeDef rdo;

  /* read the request value received */
  rdo.d32 = DPM_Ports[PortNum].DPM_RcvRequestDOMsg;

  /* Search PDO in Port Source PDO list, that corresponds to Position provided in Request RDO */
  if (USBPD_PWR_IF_SearchRequestedPDO(PortNum,  rdo.GenericRDO.ObjectPosition, &pdo.d32) == USBPD_OK)
  {

    /* Evaluate the request */
    if(pdo.GenericPDO.PowerObject == USBPD_CORE_PDO_TYPE_FIXED)
    {
      if((rdo.FixedVariableRDO.OperatingCurrentIn10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits)
         || (rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits))
      {
        /* Sink requests too much maximum operating current */
        /* USBPD_DPM_EvaluateRequest: Sink requests too much maximum operating current */
        _retr =  USBPD_REJECT;
      }
      else
      {
        /* Save the power object */
        *PtrPowerObject = pdo.GenericPDO.PowerObject;
        /* Set RDO position and requested voltage in DPM port structure */
        DPM_Ports[PortNum].DPM_RequestedVoltage = pdo.SRCFixedPDO.VoltageIn50mVunits * 50;
        DPM_Ports[PortNum].DPM_RDOPositionPrevious = DPM_Ports[PortNum].DPM_RDOPosition;
        DPM_Ports[PortNum].DPM_RDOPosition = rdo.GenericRDO.ObjectPosition;
         _retr = USBPD_ACCEPT;
      }
    }
  }

  return _retr;
/* USER CODE END USBPD_DPM_EvaluateRequest */

Add the following code between the /* USER CODE BEGIN-END USBPD_USER_PRIVATE_FUNCTIONS */ tags:

/* USER CODE BEGIN USBPD_USER_PRIVATE_FUNCTIONS */
/**
  * @brief  Turn Off power supply.
  * @param  PortNum The current port number
  * @param  Role    Port power role
  * @retval USBPD_OK, USBPD_ERROR
  */
static USBPD_StatusTypeDef DPM_TurnOffPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role)
{
  USBPD_StatusTypeDef status;

  status = USBPD_PWR_IF_VBUSDisable(PortNum);
  return status;
}

/**
  * @brief  Turn On power supply.
  * @param  PortNum The current port number
  * @param  Role    Port power role
  * @retval USBPD_ACCEPT, USBPD_WAIT, USBPD_REJECT
  */
static USBPD_StatusTypeDef DPM_TurnOnPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role)
{
  USBPD_StatusTypeDef status;

  /* Enable the output */
  status = USBPD_PWR_IF_VBUSEnable(PortNum);
  return status;
}
/* USER CODE END USBPD_USER_PRIVATE_FUNCTIONS */

7. Configure the shield's jumpers

Place jumpers on the X-NUCLEO-SRC1M1 shield as shown in the picture.

USBPD SRC1M1 JP Conf.png


Next, plug an external 5V source into the green "source" connector.

With this configuration, the board will be powered by the ST-Link of the Nucleo board.
If you want to power your system from the external power supply connected to the "source" terminal, and not from the ST-Link, please add the JP1 jumpers between 1-2 and 3-4.

8. Compile and run the application

The compilation must be performed without error or warnings.
Build the application by clicking on the Built Button.png button (or select Project/Build Project).
Run the application by clicking on the DownloadRun Button.png button (or select Run/Run)

9. Establish the first explicit contract

Clock.png 5min

With your application running on the board, launch the STM32CubeMonitor-UCPD application. The user's board must appear in the list when clicking "Refresh list of connected boards", so double click on the corresponding line (or click "NEXT").

USBPD 0-cubeMon.png


Note: The ComPort may be different. It depends on the number of boards installed on the computer. Then double click on the desired UCPD port, here Port 0, or select it and click "NEXT".

USBPD 1-cubeMon.png


Click on the TRACES button in the bottom right corner to get protocol traces. You can then plug a power delivery sink into the USB Type-C® receptacle of the X-NUCLEO-SRC1M1 shield. The screen may look like this:

USBPD 2-cubeMon.png


The figure above shows the communication between the STM32G0 and the power delivery sink on the right panel. It is possible to verify the correct sequence to reach an explicit contract:

  1. The capabilities are sent by the STM32G0 source (OUT orange message).
  2. The request is sent by the sink (IN green message).
  3. The ACCEPT and the PS_RDY are sent by the STM32G0 source (OUT orange message).
  4. The contract negotiation ends by the POWER_EXPLICIT_CONTRACT notification (blue message).

For more details on how to use this tool, refer to UM2468. And for more details on the protocol, refer to UM2552. Note that this trace is very helpful for debugging and application development.

You can also use the Measurement window in STM32CubeMonitor-UCPD to display a graph of the measured VBUS voltage and delivered current. Set the sampling period and click start.

USBPD 3-cubeMon.png




You can find other applicative examples on GitHub: x-cube-tcpp

10. References




10.1. Check the clock configuration

Under Clock Configuration main tab.

USBnoPD 0 CLKConf2.png


11. Configure the project

Clock.png 5min

Under the Project Manager main tab, and the Advanced Settings tab, as we do not need I2C initialization functions (handled by the BSP drivers), uncheck Generate Code for the MX_I2C1_Init.

USBnoPD 0 ConfProjet.png


12. Add BSP to the project

Clock.png 5min

Board support package (BSP) for the X-NUCLEO-SRC1M1 shield needs to be added to the project. The files need to be copied manually in the



[[Category:USB Power Delivery|30]] [[Category:Getting started with STM32 : STM32 step by step|84]]