STM32StepByStep:Getting started with USB-Power Delivery Source: Difference between revisions

Registered User
mNo edit summary
Registered User
mNo edit summary
Tag: 2017 source edit
 
(47 intermediate revisions by 3 users not shown)
Line 1: Line 1:
<big><big>'''Target description'''</big></big><br>
<big><big>'''Target description'''</big></big><br>


This tutorial will help you to:
This tutorial helps to:
:*Use the X-NUCLEO-SRC1M1 shield that includes a TCPP02-M18 protection circuit and provides a USB Type-C<sup>®</sup> connector
:*Use the X-NUCLEO-SRC1M1 shield that includes a TCPP02-M18 protection circuit and provides a USB Type-C<sup>®</sup> connector
:*Create a USB-PD Source application with the NUCLEO-G0B1RE board and the X-NUCLEO-SRC1M1 shield by using STM32CubeIDE software<br>
:*Create a USB-PD source application with the NUCLEO-G071RB board and the X-NUCLEO-SRC1M1 shield by using STM32CubeIDE software<br>


<big><big>'''Prerequisites'''</big></big><br>
<big><big>'''Prerequisites'''</big></big><br>
Line 11: Line 11:
<big><big>'''Hardware'''</big></big><br>
<big><big>'''Hardware'''</big></big><br>


:*'''NUCLEO-G0B1RE''' (tested on rev C) <ref>[https://www.st.com/en/evaluation-tools/nucleo-g0b1re.html  NUCLEO-G0B1RE]</ref>  
:*'''NUCLEO-G071RB''' (tested on rev C) <ref>[https://www.st.com/en/evaluation-tools/nucleo-g0b1re.html  NUCLEO-G071RB]</ref>
:*'''X-NUCLEO-SRC1M1''' shield <ref>[https://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-src1m1.html X-NUCLEO-SRC1M1]</ref>
:*'''X-NUCLEO-SRC1M1''' shield <ref>[https://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-src1m1.html X-NUCLEO-SRC1M1]</ref>
:*A '''USB-PD sink device''' to test our USB-PD device (it can be the sink created in [[STM32StepByStep:STM32 Advance USB-Power Delivery Sink|this wiki article]], or a PD-capable mobile phone or device)
:*A '''USB-PD sink device''' to test our USB-PD device (it can be the sink created in [[STM32StepByStep:STM32 Advance USB-Power Delivery Sink|this wiki article]], or a PD-capable mobile phone or device)
Line 19: Line 19:
<big><big>'''Software'''</big></big><br>
<big><big>'''Software'''</big></big><br>


:*'''STM32CubeIDE''' (tested with V1.8.0) <ref>[https://www.st.com/en/development-tools/stm32cubeide.html STM32CubeIDE]</ref>
:*'''STM32CubeMX''' (tested with V6.11.0 - minimal release 6.11.0) <ref>[https://www.st.com/en/development-tools/stm32cubemx.html STM32CubeMX]</ref>
:*'''STM32CubeMonitor-UCPD''' (tested with V1.2.0) <ref>[https://www.st.com/en/development-tools/stm32cubemonucpd.html STM32CubeMonitor-UCPD]</ref>
:*'''STM32CubeIDE''' (tested with V1.14.0) <ref>[https://www.st.com/en/development-tools/stm32cubeide.html STM32CubeIDE]</ref>
:*'''STM32CubeMonitor-UCPD''' (tested with V1.3.0) <ref>[https://www.st.com/en/development-tools/stm32cubemonucpd.html STM32CubeMonitor-UCPD]</ref>
:*X-CUBE-TCPP MCU Firmware Package (BSP) <ref>[https://github.com/STMicroelectronics/x-cube-tcpp X-CUBE-TCPP]</ref>
:*X-CUBE-TCPP MCU Firmware Package (BSP) <ref>[https://github.com/STMicroelectronics/x-cube-tcpp X-CUBE-TCPP]</ref>


<big><big>'''Literature'''</big></big><br>
<big><big>'''Literature'''</big></big><br>


:*[https://www.st.com/resource/en/user_manual/um2324-stm32-nucleo64-boards-mb1360-stmicroelectronics.pdf UM2324] NUCLEO-G0B1RE User Manual
:*[https://www.st.com/resource/en/user_manual/um2324-stm32-nucleo64-boards-mb1360-stmicroelectronics.pdf UM2324] NUCLEO-G071RB User Manual
:*[https://www.st.com/resource/en/user_manual/um2973-getting-started-with-the-xnucleosrc1m1-usb-typec-power-delivery-source-expansion-board-based-on-tcpp02m18-for-stm32-nucleo-stmicroelectronics.pdf UM2973] X-NUCLEO-SRC1M1 User Manual
:*[https://www.st.com/resource/en/user_manual/um2973-getting-started-with-the-xnucleosrc1m1-usb-typec-power-delivery-source-expansion-board-based-on-tcpp02m18-for-stm32-nucleo-stmicroelectronics.pdf UM2973] X-NUCLEO-SRC1M1 User Manual
 
:*[[File:pc_videol.png|middle|20px|link=https://www.youtube.com/watch?v=tA5v4JjV-T8]] [https://www.youtube.com/watch?v=tA5v4JjV-T8  How to build an USBPD Source application using the X-Cube-TCPP software pack]
 


<big><big><big><big><big>'''Create a USB-PD Source Device'''</big></big></big></big></big>
<big><big><big><big><big>'''Create a USB-PD Source Device'''</big></big></big></big></big>
<br><br>
<br><br>
[[File:Clock.png|40px|middle]] Total 60min<br><br>
[[File:Clock.png|40px|middle]] Total 60min<br><br>
==Software pack installation==
Open STM32CubeMX, in the software pack area, click on the ''install/remove'' button.
[[File:STM32StepByStep Install SP 1bis.png|center]]<br>
Then select the ''STMicroelectronics'' tab, scroll down to the ''X-Cube-TCPP'' software pack, and click on the ''install'' button if it is not already installed.
[[File:STM32StepByStep Install SP 2.png|center]]<br>


==Creating the project==
==Creating the project==
[[File:Clock.png|40px|middle]] 5min<br><br>
[[File:Clock.png|40px|middle]] 5min<br><br>
Open STM32CubeIDE and create a New STM32 Project. As a target selection, choose the NUCLEO-G0B1RE from the ''Board Selector'' Tab<br>
In STM32CubeMX, create a new STM32 project. As a target selection, choose the NUCLEO-G071RB from the ''Board Selector'' Tab.<br>
[[File:USBPD_0-newproject.png|frame|center]]<br>
[[File:STM32StepByStep 03 Start Project_G0.png|center]]<br>
 
Click ''Start Project'', then in the file menu, create a new folder under your project's name, and click ''Save''.
[[File:STM32StepByStep Save Project As bis.png|center]]<br>


Click "Next", then enter your project's name. Leave the other fields as default and click "Finish".
[[File:USBPD_1-newproject.png|center]]<br>
When prompted for initializing peripherals with their default mode, click No.
When prompted for initializing peripherals with their default mode, click No.


Line 46: Line 54:
[[File:Clock.png|40px|middle]] 15min<br><br>
[[File:Clock.png|40px|middle]] 15min<br><br>


At this point, your project is created and you are left with the STM32CubeMX view.
At this point, your project is created. The next steps focus on the configuration of the peripherals and options needed for the project.
In the next steps, we will configure the peripherals and options needed for the project.
<br><br>
<br><br>


===Clear the pinout===
===Clear the pinout===
To start from a blank configuration, click on the ''Pinout'' menu and select ''Clear Pinouts''. This will reset the pinouts in the Pinout view.
To start from a blank configuration, click on the ''Pinout'' menu and select ''Clear Pinouts''. This resets the pinouts in the Pinout view.
[[File:USBPD_0-pinoutConf.png|frame|center]]<br>
[[File:USBPD_0-pinoutConf.png|frame|center]]<br>


===Configure the system timebase===
===Select the X-Cube-TCPP software pack===
From the software pack menu:
[[File:STM32StepByStep SP Menu v4.png|center]]<br>
 
Select the X-CUBE-TCPP software pack and enable its ''Source'' application, the ''tcpp0203'' board part, and the ''X-NUCLEO-SRC1M1'' board support.
[[File:STM32StepByStep 06 SP Select SRC1M1.png|center]]<br>
 
<!-- Ok by default ===Configure the system timebase===
For this simple example, we will use SysTick as the system timebase. In the System Core section, select ''SYS'' and change the ''Timebase Source'' to ''SysTick''.
For this simple example, we will use SysTick as the system timebase. In the System Core section, select ''SYS'' and change the ''Timebase Source'' to ''SysTick''.
[[File:USBPD_0-sysConf.png|frame|center]]<br>
[[File:USBPD_0-sysConf.png|frame|center]]<br> -->


===Configure UCPD peripheral===
===Configure UCPD peripheral===
Line 65: Line 79:
[[File:USBPD_1-UCPD1Conf.png|frame|center]]<br>
[[File:USBPD_1-UCPD1Conf.png|frame|center]]<br>


{{Info | You can use any DMA channel you want except for DMA1_Channel1 which would be used later by the BSP drivers.}}
{{Info | You can use any DMA channel except for DMA1_Channel1, which is used later by the BSP drivers.}}




Line 72: Line 86:
[[File:USBPD_0-FreeRTOSConf.png|frame|center]]<br>
[[File:USBPD_0-FreeRTOSConf.png|frame|center]]<br>


Then, under the ''Include Parameters'' tab, Enable ''eTaskGetState'' include definition.
<!-- Ok by default Then, under the ''Include Parameters'' tab, Enable ''eTaskGetState'' include definition.
[[File:USBPD_1-FreeRTOSConf.png|frame|center]]<br>
[[File:USBPD_1-FreeRTOSConf.png|frame|center]]<br>-->


{{Info|
{{Info|
If an STM32'''G4''' 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.
If an STM32'''G4''' is used instead 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 set to 5 causes the code execution to get stuck in the vPortValidateInterruptPriority function.
}}
}}


===Configure USBPD Middleware===
===Configure USBPD Middleware===
In the ''Middleware'' section, enable ''USBPD'' with the following configuration:
In the ''Middleware'' section, enable ''USBPD'' with the following configuration:
*Port Configuration: '''Port 0: UCPD1'''
*Port configuration: '''Port 0: UCPD1'''
*Stack Configuration: '''PD3 Full Stack'''
*Stack configuration: '''PD3 Full Stack'''
*Timer service Source: '''TIM1'''
*Timer service source: '''TIM1'''


Under the ''PDO General Definitions'' tab, verify the following configuration:
Under the ''PDO Source'' tab, verify the following configuration:
*Number of Sink PDOs for port 0: '''1'''
*Number of Source PDOs for port 0: '''1'''
*Port 0 Sink PDO 1            '''0x0001912C''' (correspond to a simple 5V / 3A source)
*Port 0 Source PDO 0 : '''5V 100mA'''
[[File:USBPD_0-USBPDConf.png|frame|center]]<br>
[[File:STM32StepByStep_20 SP Source PDO Def.png|frame|center]]<br>
 
The following table is extracted from [https://www.usb.org/document-library/usb-power-delivery 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
|}


===Configure ADC peripheral===
===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.<br>
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.<br>
As we are going to use the X-NUCLEO-SRC1M1 BSP, the ADC configuration does not need to be done in CubeMX. <br>
Since the X-NUCLEO-SRC1M1 BSP is used, the ADC configuration does not need to be done in CubeMX. <br>
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.<br>
As the ADC HAL drivers are required for it to work properly, the ADC needs to be configured in CubeMX for it to include the driver files, but the actual configuration and init function are not called in this project.<br>
<br>
<br>
In the ''Analog'' section, enable ''ADC1'' peripheral channel 0. Leave the configuration as default, as the X-NUCLEO-SRC1M1 BSP will reconfigure it.<br>
In the ''Analog'' section, enable ''ADC1'' peripheral channel 0. Leave the configuration as default, as the software pack reconfigures it.<br>
[[File:USBPD_0-ADC1Conf.png|frame|center]]<br>
[[File:USBPD_0-ADC1Conf.png|frame|center]]<br>


===Configure I2C peripheral===
===Configure I2C peripheral===
As the X-NUCLEO-SRC1M1 shield includes a TCPP02-M18 that communicates via I2C, we need to enable the I2C peripheral in our project.<br><br>
As the X-NUCLEO-SRC1M1 shield includes a TCPP02-M18 that communicates through I2C, the I2C peripheral needs to be enabled in this project.<br><br>
In the ''Connectivity'' section, enable ''I2C2'' peripheral, in ''I2C'' mode. Leave the configuration as default, as the X-NUCLEO-SRC1M1 BSP will reconfigure it.<br>
In the ''Connectivity'' section, enable ''I2C2'' peripheral in ''I2C'' mode. Leave the configuration as default, as the software pack reconfigures it.<br>
[[File:USBPD_0-I2C2Conf.png|frame|center]]<br>
[[File:USBPD_0-I2C2Conf.png|frame|center]]<br>
''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.''
''Note: Enable the I2C2 peripheral in the CubeMX view for code generation to include the I2C drivers as done for the ADC.''
 
===Enable the software pack===
In the middleware and software pack category, select the X-Cube-TCPP software pack. Enable the 'Source' application, the 'tcpp0203' board part, and the 'X-NUCLEO-SRC1M1' board support.
[[File:STM32StepByStep 07 SP Enable SRC.png|center]]<br>


===Configure Clocks===
===Configure Clocks===
Under ''Clock Configuration'' main tab, change system clock mux to ''PLLCLK''. It will set ''HCLK'' clock to 64MHz.
Under the ''Clock Configuration'' main tab, change system clock mux to ''PLLCLK''. It sets ''HCLK'' clock to 64 MHz.
[[File:USBPD_0-Clock.png|frame|center]]<br>
[[File:USBPD_0-Clock.png|frame|center]]<br>


{{Info|'''The mandatory settings for the simple USB-PD sink application are finished.'''</big><br>
{{Info|'''The mandatory settings for the simple USB-PD sink application are finished.'''</big><br>
<big><big>'''The following part is highly recommended for debugging'''</big></big><br>
<big><big>'''The following part is highly recommended for debugging.'''</big></big><br>
}}
}}


Line 144: Line 135:
{{Warning|The default STM32CubeMX pins used by LPUART1 must be changed to match the STM32G0 Nucleo-64 hardware: <br>
{{Warning|The default STM32CubeMX pins used by LPUART1 must be changed to match the STM32G0 Nucleo-64 hardware: <br>
*'''PA2 for TX'''<br>
*'''PA2 for TX'''<br>
*'''PA3 for RX'''.}}
*'''PA3 for RX'''}}
In the ''Connectivity'' section, enable ''LPUART1'' in Asynchronous mode, and baudrate '''921600 bauds'''. Leave the rest as default.<br>
In the ''Connectivity'' section, enable ''LPUART1'' in asynchronous mode, and baudrate '''921600 bauds'''. Leave the rest as default.<br>
[[File:USBPD_0-LPUARTConf.png|frame|center]]<br>
[[File:USBPD_0-LPUARTConf.png|frame|center]]<br>
In the pinout view, left-click PA2 and PA3 to remap them to ''LPUART1_TX'' and ''LPUART1_RX''.
In the pinout view, left-click PA2 and PA3 to remap them to ''LPUART1_TX'' and ''LPUART1_RX''.
Line 168: Line 159:
[[File:USBPD_0-GUIConf.png|frame|center]]<br>
[[File:USBPD_0-GUIConf.png|frame|center]]<br>


==Configure project==
==Configure the project==
[[File:Clock.png|40px|middle]] 5min<br><br>
[[File:Clock.png|40px|middle]] 5min<br><br>
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.
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.
[[File:USBPD_0-projectConf.png|frame|center]]<br>
[[File:STM32StepByStep Config Proj2.png|center]]<br>
For STM32G0 or G4 MCU, uncheck “Use default firmware location” and instead, select the software pack “c:|\user\ … \STM32Cube\Repositoryctronics/Packs\STMicroelectronics\X-CUBE-TCPP\V4.1.0\” as firmware location to be sure to use the latest USBPD lib releases as the standard evolution is very fast.
[[File:STM32StepByStep 08 Config Proj2 SRC.png|center]]<br>
<br>
<br>
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.
[[File:USBPD_2-projectConf.png|frame|center]]<br>
==Add BSP to the project==
[[File:Clock.png|40px|middle]] 5min<br><br>
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 project's folder.<br>
Get the latest BSP from GitHub [https://github.com/STMicroelectronics/x-cube-tcpp/tree/main/Drivers/BSP x-cube-tcpp].<br><br>
Manually copy the three following folders:
<syntaxhighlight>
x-cube-tcpp
└── Drivers
    └── BSP
        ├── 📁STM32G0xx_Nucleo
        ├── 📁X-NUCLEO-SRC1M1
        └── Components
            └── 📁tcpp0203
</syntaxhighlight>


Into:
<syntaxhighlight>
<ProjectFolder>
└── Drivers
    └── BSP
        ├── 📁STM32G0xx_Nucleo
        ├── 📁X-NUCLEO-SRC1M1
        └── Components
            └── 📁tcpp0203
</syntaxhighlight>


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:
Under the ''Advanced Settings'' tab, change the LPUART driver to LL to save a bit of memory heap size. As ADC and I2C initialization functions are not needed (handled by the BSP drivers), uncheck ''Generate Code'' for the MX_I2C_Init and MX_ADC1_Init functions.
<syntaxhighlight>
[[File:STM32StepByStep Conf Advanced Settings3.png|center]]<br>
[ProjectFiles]
HeaderPath=Drivers\BSP\X-NUCLEO-SRC1M1;Drivers\BSP\STM32G0xx_Nucleo;Drivers\BSP\Components\tcpp0203
[Others]
Define=TCPP0203_SUPPORT;USE_STM32G0XX_NUCLEO
HALModule=
[Groups]
Drivers/BSP/X-NUCLEO-SRC1M1=Drivers/BSP/X-NUCLEO-SRC1M1/src1m1_usbpd_pwr.c;Drivers/BSP/X-NUCLEO-SRC1M1/src1m1_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
</syntaxhighlight>


This file is used to tell the code generator to include the BSP files when generating the project.
==Generate the code==
 
{{Info | You can double-click the code zones to select it all, then copy it with Ctrl+c.}}
 
==Generate code==
[[File:Clock.png|40px|middle]] 5min<br><br>
[[File:Clock.png|40px|middle]] 5min<br><br>


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.
Save your file with Ctrl+s and select generate code.
[[File:USBPD_0-projGen.png|frame|center]]<br>
[[File:STM32StepByStep Generate3.png|center]]<br>


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.<br>
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.<br>
Line 231: Line 182:
{{Info|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.<br>
{{Info|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.<br>
For this demonstration, the warning can be ignored by clicking Yes.}}
For this demonstration, the warning can be ignored by clicking Yes.}}
<!--
<br>-->
STM32CubeIDE starts and imports the project.


<br>


In this project, different folders can be found:
This project contains the following folders:
*The ''USBPD'' folder contains the source files that we need to edit to enrich the Power Delivery application.
*The ''USBPD'' folder contains the source files that are needed to edit to enrich the Power Delivery application.
*The ''Core'' folder contains the source files for the core of the project.
*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 ''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 ''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 ''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.
The ''Drivers'' folder in the Explorer view of the project must contain the BSP folders added earlier.
[[File:USBPD_0-projTree.png|frame|center]]<br>
[[File:STM32StepByStep SRC1M1 Project Structure.png|frame|center]]<br>
 
==Configure the shield's jumpers==
Place the jumpers on the X-NUCLEO-SRC1M1 shield as shown in the picture.
[[File:USBPD_SRC1M1_JP_Conf.png|frame|center]]<br>


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


==Complete USB-PD application==
With this configuration, the board is powered by the ST-Link of the Nucleo board.<br>
[[File:Clock.png|40px|middle]] 20min<br><br>
To power your system from the external power supply connected to the "source" terminal, and not from the ST-Link, add the JP1 jumpers between 1-2 and 3-4.
Now that the peripherals are initialized by STM32CubeMX, some minimum level of the application needs to be added:
*src1m1_conf.h file needs to be created from its template, and added to the project
*User code needs to be added in several files


===Add SRC1M1 configuration file===
==Compile and run the application==
In the ''Drivers\BSP\X-NUCLEO-SRC1M1'' folder you will find ''src1m1_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-SRC1M1 BSP.
The compilation must be performed without error or warnings.<br>
Build the application by clicking on the [[File:Built_Button.png|20px|middle]] button (or select ''Project/Build Project'').<br>
Run the application by clicking on the [[File:DownloadRun_Button.png|20px|middle]] button (or select ''Run/Run'')<br>
 
==Establish the first explicit contract==
[[File:Clock.png|40px|middle]] 5min<br><br>
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").
[[File:USBPD_0-cubeMon.png|frame|center]]<br>


===Modification in stm32g0xx_it.c===
''Note: The ComPort may be different. It depends on the number of boards installed on the computer.''
{{Info | You can double-click the code zones to select it all, then copy it with Ctrl+c.}}
Double click on the desired UCPD port, here Port 0, or select it and click "NEXT".
Add the following code between the ''/* USER CODE BEGIN-END Includes */'' tags:
[[File:USBPD_1-cubeMon.png|frame|center]]<br>
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN Includes */
#include "src1m1_conf.h"
/* USER CODE END Includes */
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END 1 */'' tags:
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:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN 1 */
#if defined(TCPP0203_SUPPORT)
/**
  * @brief  This function handles the 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 */
[[File:USBPD_2-cubeMon.png|frame|center]]<br>
    TCPP0203_PORT0_FLG_EXTI_CLEAR_FLAG();
  }
}
#endif /* TCPP0203_SUPPORT */
/* USER CODE END 1 */
</syntaxhighlight>


===Modification in usbpd_dpm_user.h===
The figure above shows the communication between the STM32G0 and the power delivery sink on the right panel. It is
Add the following code between the ''/* USER CODE BEGIN-END Typedef */'' tags:
possible to verify the correct sequence to reach an explicit contract:
<syntaxhighlight lang="c" class="noscroll">
# The '''capabilities are sent''' by the STM32G0 '''source''' (OUT orange message).
/* USER CODE BEGIN Typedef */
# The '''request is sent''' by the '''sink''' (IN green message).
typedef struct
# The '''ACCEPT''' and the '''PS_RDY''' are sent by the STM32G0 '''source''' (OUT orange message).
{
# The contract negotiation ends by the '''POWER_EXPLICIT_CONTRACT''' notification (blue message).
  uint32_t  DPM_ListOfRcvSNKPDO[USBPD_MAX_NB_PDO]; /*!< The list of received Sink Power Data Objects from
For more details on how to use this tool, refer to [https://www.st.com/resource/en/user_manual/dm00536366-stm32cubemonitorucpd-software-tool-for-usb-typec-power-delivery-port-management-stmicroelectronics.pdf UM2468].
                                            Port partner (when Port partner is a Sink or a DRP port).     */
And for more details on the protocol, refer to [https://www.st.com/resource/en/user_manual/dm00598101-managing-usb-power-delivery-systems-with-stm32-microcontrollers-stmicroelectronics.pdf UM2552].
  uint32_t  DPM_NumberOfRcvSNKPDO;      /*!< The number of received Sink Power Data Objects from port
Note that this trace is very helpful for debugging and application development.
                                            Partner (when Port partner is a Sink or a DRP port).  
                                            This parameter must be set to a value lower than
                                            USBPD_MAX_NB_PDO                                              */
  uint32_t  DPM_RDOPosition;            /*!< RDO Position of requested DO in Source list of capabilities  */
  uint32_t  DPM_RDOPositionPrevious;    /*!< RDO Position of requested DO in Source list of capabilities  */
  uint32_t  DPM_RequestedVoltage;      /*!< Value of requested voltage                                    */
  uint32_t  DPM_RequestedCurrent;      /*!< Value of requested current                                    */
  uint32_t  DPM_RcvRequestDOMsg;


  uint32_t  DPM_RequestDOMsg;          /*!< Request Power Data Object message to be sent                  */
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.
  uint32_t  DPM_RequestDOMsgPrevious;  /*!< Previous Request Power Data Object message to be sent        */
[[File:USBPD_3-cubeMon.png|frame|center]]<br>
} USBPD_HandleTypeDef;
<br><br>
/* USER CODE END Typedef */
<big>'''You can find other applicative examples on GitHub: [https://github.com/STMicroelectronics/x-cube-tcpp x-cube-tcpp]'''</big>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END Private_Variables */'' tags:
==Information focus : Code inserted by the software pack==
<syntaxhighlight lang="c" class="noscroll">
By enabling the software pack in [[#Enable the software pack|section 3.8]], the code below has been added automatically in following files:
/* USER CODE BEGIN Private_Variables */
extern USBPD_HandleTypeDef DPM_Ports[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */
</syntaxhighlight>


===Modification in usbpd_pdo_defs.h===
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
Add the following code between the ''/* USER CODE BEGIN-END typedef */'' tags:
| <strong>usbpd_dpm_user_h</strong>
|-
|'''<u>Between the ''/* USER CODE BEGIN-END Typedef */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN typedef */
#if !defined(USBPD_REV_MAJOR)
#define USBPD_REV_MAJOR      (3U)    /* USBPD Specification revision major */
#define USBPD_REV_MINOR      (1U)    /* USBPD Specification revision minor */
#define USBPD_VERSION_MAJOR  (1U)    /* USBPD Specification version major  */
#define USBPD_VERSION_MINOR  (7U)    /* USBPD Specification version minor  */
#endif /* !USBPD_REV_MAJOR */


/**
/**
   * @brief  USBPD Port PDO Structure definition
   * @brief  USBPD DPM handle Structure definition
  * @{
   */
   */
typedef struct
typedef struct
{
{
   uint32_t *ListOfPDO;         /*!< Pointer on Power Data Objects list, defining
   uint32_t                     DPM_ListOfRcvSNKPDO[USBPD_MAX_NB_PDO];  /*!< The list of received Sink Power Data Objects from Port partner (when Port partner is a Sink or a DRP port). */
                                      port capabilities */
  uint32_t                      DPM_NumberOfRcvSNKPDO;                   /*!< The number of received Sink Power Data Objects from port Partner (when Port partner is a Sink or a DRP port). */
  uint32_t                      DPM_ListOfRcvSRCPDO[USBPD_MAX_NB_PDO];  /*!< The list of received Source Power Data Objects from Port partner    */
  uint32_t                      DPM_NumberOfRcvSRCPDO;                  /*!< The number of received Source Power Data Objects from port Partner  (when Port partner is a Source or a DRP port). */
  uint32_t                      DPM_RcvRequestDOMsg;                    /*!< Received request Power Data Object message from the port Partner    */
  uint32_t                      DPM_RequestDOMsgPrevious;                /*!< Previous Request Power Data Object message to be sent                */


  uint8_t  *NumberOfPDO;      /*!< Number of Power Data Objects defined in ListOfPDO
                                    This parameter must be set at max to @ref USBPD_MAX_NB_PDO value */
} USBPD_PortPDO_TypeDef;


/**
   USBPD_PPSSDB_TypeDef          DPM_RcvPPSStatus;                       /*!< PPS Status received by port partner                                  */
  * @brief  USBPD Port PDO Storage Structure definition
  USBPD_SKEDB_TypeDef          DPM_RcvSNKExtendedCapa;                 /*!< SNK Extended Capability received by port partner                    */
  */
typedef struct
{
   USBPD_PortPDO_TypeDef    SourcePDO;     /*!< SRC Power Data Objects */
} USBPD_PWR_Port_PDO_Storage_TypeDef;
/* USER CODE END typedef */
</syntaxhighlight>


===Modification in usbpd_pwr_if.c===
  uint32_t                      DPM_RequestDOMsg;                        /*!< Request Power Data Object message to be sent                        */
Add the following code between the ''/* USER CODE BEGIN-END Private_Variables */'' tags:
  uint32_t                      DPM_RDOPosition;                        /*!< RDO Position of requested DO in Source list of capabilities          */
<syntaxhighlight lang="c" class="noscroll">
  uint32_t                      DPM_RDOPositionPrevious;                /*!< RDO Position of previous requested DO in Source list of capabilities */
/* USER CODE BEGIN Private_Variables */
   uint32_t                      DPM_RequestedVoltage;                    /*!< Value of requested voltage                                          */
/**
  uint32_t                      DPM_RequestedCurrent;                   /*!< Value of requested current                                          */
   * @brief  USBPD Port PDO Storage array declaration
} USBPD_HandleTypeDef;
  */
USBPD_PWR_Port_PDO_Storage_TypeDef PWR_Port_PDO_Storage[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_Init */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END Variables */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_PWR_IF_Init */
extern USBPD_HandleTypeDef                  DPM_Ports[USBPD_PORT_COUNT];
  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 */
</syntaxhighlight>
</syntaxhighlight>
|}


Add the following code between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_SetProfile */'' tags:
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>usbpd_dpm_user_c</strong>
|-
|'''<u>Between the ''/* USER CODE BEGIN-END Includes */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_PWR_IF_SetProfile */
#if !defined(_TRACE)
  USBPD_PDO_TypeDef        _pdo;
#include "string.h"
  USBPD_SNKRDO_TypeDef    _rdo;
#endif /* !_TRACE */
 
  _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 */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_GetPortPDOs */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END Variables */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_PWR_IF_GetPortPDOs */
USBPD_HandleTypeDef                          DPM_Ports[USBPD_PORT_COUNT];
  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 */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_SearchRequestedPDO */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END Prototypes */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* 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 */
</syntaxhighlight>
===Modification in usbpd_dpm_user.c===
Add the following code between the ''/* USER CODE BEGIN-END Private_Variables */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN Private_Variables */
USBPD_HandleTypeDef DPM_Ports[USBPD_PORT_COUNT];
/* USER CODE END Private_Variables */
</syntaxhighlight>
Add the following code between the ''/* USER CODE BEGIN-END USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */
static USBPD_StatusTypeDef DPM_TurnOnPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
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 USBPD_StatusTypeDef DPM_TurnOffPower(uint8_t PortNum, USBPD_PortPowerRole_TypeDef Role);
/* USER CODE END USBPD_USER_PRIVATE_FUNCTIONS_Prototypes */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_UserInit */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_UserInit */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_DPM_UserInit */
   /* PWR SET UP */
   /* PWR SET UP */
   if(USBPD_OK !=  USBPD_PWR_IF_Init())
   if(USBPD_OK !=  USBPD_PWR_IF_Init())
Line 485: Line 315:
     return USBPD_ERROR;
     return USBPD_ERROR;
   }
   }
  return USBPD_OK;
/* USER CODE END USBPD_DPM_UserInit */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_UserCableDetection */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_UserCableDetection */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_DPM_UserCableDetection */
#ifdef _GUI_INTERFACE
  switch(State)
  {
  case USBPD_CAD_EVENT_ATTEMC:
  case USBPD_CAD_EVENT_ATTACHED:
    /* Format and send a notification to GUI if enabled */
    if (NULL != DPM_GUI_FormatAndSendNotification)
    {
      DPM_GUI_FormatAndSendNotification(PortNum, DPM_GUI_NOTIF_ISCONNECTED, 0);
    }
    break;
  default :
    /* Format and send a notification to GUI if enabled */
    if (NULL != DPM_GUI_FormatAndSendNotification)
    {
      DPM_GUI_FormatAndSendNotification(PortNum, DPM_GUI_NOTIF_ISCONNECTED | DPM_GUI_NOTIF_POWER_EVENT, 0);
    }
  }
#endif /*_GUI_INTERFACE*/
 
   switch(State)
   switch(State)
   {
   {
Line 497: Line 344:
   case USBPD_CAD_EVENT_ATTEMC:
   case USBPD_CAD_EVENT_ATTEMC:
     {
     {
      if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
    if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
    {
      if (USBPD_OK != USBPD_PWR_IF_VBUSEnable(PortNum))
       {
       {
         if (USBPD_OK != USBPD_PWR_IF_VBUSEnable(PortNum))
         /* Should not occur */
        {
        NVIC_SystemReset();
          /* Should not occur */
          osDelay(6000);
          NVIC_SystemReset();
        }
       }
       }
      break;
    }
    break;
     }
     }
   case USBPD_CAD_EVENT_DETACHED :
   case USBPD_CAD_EVENT_DETACHED :
   case USBPD_CAD_EVENT_EMC :
   case USBPD_CAD_EVENT_EMC :
   default :
   default :
    if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
     {
     {
       if (DPM_Params[PortNum].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
       if (USBPD_OK != USBPD_PWR_IF_VBUSDisable(PortNum))
       {
       {
         if (USBPD_OK != USBPD_PWR_IF_VBUSDisable(PortNum))
         /* Should not occur */
        {
        while(1);
          /* Should not occur */
          while(1);
        }
       }
       }
      break;
     }
     }
    break;
   }
   }
/* USER CODE END USBPD_DPM_UserCableDetection */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_HardReset */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_HardReset */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_DPM_HardReset */
   switch (Status)
   switch (Status)
   {
   {
Line 538: Line 381:
     }
     }
     break;
     break;
   case USBPD_HR_STATUS_WAIT_VBUS_VSAFE5V:
   case USBPD_HR_STATUS_WAIT_VBUS_VSAFE5V:
     if (CurrentRole == USBPD_PORTPOWERROLE_SRC)
     if (CurrentRole == USBPD_PORTPOWERROLE_SRC)
Line 546: Line 388:
     }
     }
     break;
     break;
   default:
   default:
     break;
     break;
   }
   }
/* USER CODE END USBPD_DPM_HardReset */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_GetDataInfo */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_GetDataInfo */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_DPM_GetDataInfo */
/* Case Requested voltage value Data information */
   /* Check type of information targeted by request */
   case USBPD_CORE_DATATYPE_REQ_VOLTAGE :
   switch(DataId)
    *Size = 4;
  {
    (void)memcpy((uint8_t*)Ptr, (uint8_t *)&DPM_Ports[PortNum].DPM_RequestedVoltage, *Size);
     case USBPD_CORE_DATATYPE_REQ_VOLTAGE:      /*!< Get voltage value requested for BIST tests, expect 5V */
    break;
      *Size = 4;
   case USBPD_CORE_DATATYPE_SRC_PDO :
       (void)memcpy((uint8_t*)Ptr, (uint8_t *)&DPM_Ports[PortNum].DPM_RequestedVoltage, *Size);
    USBPD_PWR_IF_GetPortPDOs(PortNum, DataId, Ptr, Size);
    *Size *= 4;
     break;
  case USBPD_CORE_REVISION:
    {
       *Size = sizeof(USBPD_RevisionDO_TypeDef);
      USBPD_RevisionDO_TypeDef rev =
      {
        /* Hardcoded values, user should use a global USBPD_RevisionDO_TypeDef variable */
        .b.Revision_major = USBPD_REV_MAJOR,        /*!< Major revision */
        .b.Revision_minor = USBPD_REV_MINOR,         /*!< Minor revision */
        .b.Version_major  = USBPD_VERSION_MAJOR,    /*!< Major version  */
        .b.Version_minor  = USBPD_VERSION_MINOR      /*!< Minor version  */
      };
 
       memcpy((uint8_t *)Ptr, &rev, *Size);
       break;
       break;
     case USBPD_CORE_DATATYPE_SRC_PDO:           /*!< Handling of port Source PDO                           */
     }
       USBPD_PWR_IF_GetPortPDOs(PortNum, DataId, Ptr, Size);
 
      *Size *= 4;
  default:
    break;
</syntaxhighlight>
 
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_SetDataInfo */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
    /* 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;
       break;
// case USBPD_CORE_PPS_STATUS:                /*!< PPS Status message content                            */
 
     // break;
     /* Case Received Sink PDO values Data information :*/
// case USBPD_CORE_SNK_EXTENDED_CAPA:          /*!< Retrieve of Sink Extended capability message content */
     case USBPD_CORE_DATATYPE_RCV_SNK_PDO :
     // break;
      if (Size <= (USBPD_MAX_NB_PDO * 4))
// case USBPD_CORE_INFO_STATUS:               /*!< Information status message content                    */
      {
    // break;
         uint8_t* rdo;
// case USBPD_CORE_MANUFACTURER_INFO:          /*!< Retrieve of Manufacturer info message content         */
        DPM_Ports[PortNum].DPM_NumberOfRcvSNKPDO = (Size / 4);
    // break;
        /* Copy PDO data in DPM Handle field */
// case USBPD_CORE_BATTERY_STATUS:            /*!< Retrieve of Battery status message content            */
        for (uint32_t index = 0; index < (Size / 4); index++)
    // break;
         {
// case USBPD_CORE_BATTERY_CAPABILITY:         /*!< Retrieve of Battery capability message content        */
          rdo = (uint8_t*)&DPM_Ports[PortNum].DPM_ListOfRcvSNKPDO[index];
    // break;
          (void)memcpy(rdo, (Ptr + (index * 4u)), (4u * sizeof(uint8_t)));
    default:
        }
      DPM_USER_DEBUG_TRACE(PortNum, "ADVICE: update USBPD_DPM_GetDataInfo:%d", DataId);
      }
       break;
       break;
  }
/* USER CODE END USBPD_DPM_GetDataInfo */
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_SetDataInfo */'' tags:
<syntaxhighlight lang="c" class="noscroll">
/* 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                */
     case USBPD_CORE_DATATYPE_RCV_REQ_PDO :  /*!< Storage of Received Sink Request PDO value                */
       if (Size == 4)
       if (Size == 4)
Line 613: Line 460:
       }
       }
       break;
       break;
// case USBPD_CORE_INFO_STATUS:           /*!< Information status message content                        */
 
  // break;
    case USBPD_CORE_REVISION:
// case USBPD_CORE_ALERT:                  /*!< Storing of received Alert message content                */
      {
  // break;
        /* Does nothing: User have to implement a global revision variable */
// case USBPD_CORE_GET_MANUFACTURER_INFO: /*!< Storing of received Get Manufacturer info message content */
        USBPD_RevisionDO_TypeDef rev = {0};
  // break;
        memcpy((uint8_t *)&rev, Ptr, sizeof(USBPD_RevisionDO_TypeDef));
// case USBPD_CORE_GET_BATTERY_STATUS:    /*!< Storing of received Get Battery status message content    */
        break;
  // 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:
     default:
      DPM_USER_DEBUG_TRACE(PortNum, "ADVICE: update USBPD_DPM_SetDataInfo:%d", DataId);
    break;
      break;
  }
/* USER CODE END USBPD_DPM_SetDataInfo */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_DPM_EvaluateRequest */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_DPM_EvaluateRequest */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_DPM_EvaluateRequest */
   USBPD_StatusTypeDef _retr = USBPD_REJECT;
   USBPD_StatusTypeDef _retr = USBPD_REJECT;
   USBPD_PDO_TypeDef pdo;
   USBPD_PDO_TypeDef pdo;
   USBPD_SNKRDO_TypeDef rdo;
   USBPD_SNKRDO_TypeDef rdo;
   /* read the request value received */
   /* read the request value received */
   rdo.d32 = DPM_Ports[PortNum].DPM_RcvRequestDOMsg;
   rdo.d32 = DPM_Ports[PortNum].DPM_RcvRequestDOMsg;
Line 645: Line 485:
   if (USBPD_PWR_IF_SearchRequestedPDO(PortNum,  rdo.GenericRDO.ObjectPosition, &pdo.d32) == USBPD_OK)
   if (USBPD_PWR_IF_SearchRequestedPDO(PortNum,  rdo.GenericRDO.ObjectPosition, &pdo.d32) == USBPD_OK)
   {
   {
     /* Evaluate the request */
     /* Evaluate the request */
     if(pdo.GenericPDO.PowerObject == USBPD_CORE_PDO_TYPE_FIXED)
     if(pdo.GenericPDO.PowerObject == USBPD_CORE_PDO_TYPE_FIXED)
     {
     {
       if((rdo.FixedVariableRDO.OperatingCurrentIn10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits)
       if((rdo.FixedVariableRDO.OperatingCurrentIn10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits)
         || (rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits))
         || ((rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits > pdo.SRCFixedPDO.MaxCurrentIn10mAunits)&&(rdo.FixedVariableRDO.CapabilityMismatch==0)))
       {
       {
         /* Sink requests too much maximum operating current */
         /* Sink requests too much maximum operating current */
Line 668: Line 507:
     }
     }
   }
   }
   return _retr;
   return _retr;
/* USER CODE END USBPD_DPM_EvaluateRequest */
</syntaxhighlight>
</syntaxhighlight>


Add the following code between the ''/* USER CODE BEGIN-END USBPD_USER_PRIVATE_FUNCTIONS */'' tags:
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_USER_PRIVATE_FUNCTIONS */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
<syntaxhighlight lang="c" class="noscroll">
/* USER CODE BEGIN USBPD_USER_PRIVATE_FUNCTIONS */
/**
/**
   * @brief  Turn Off power supply.
   * @brief  Turn Off power supply.
Line 685: Line 521:
{
{
   USBPD_StatusTypeDef status;
   USBPD_StatusTypeDef status;
   status = USBPD_PWR_IF_VBUSDisable(PortNum);
   status = USBPD_PWR_IF_VBUSDisable(PortNum);
   return status;
   return status;
Line 699: Line 534:
{
{
   USBPD_StatusTypeDef status;
   USBPD_StatusTypeDef status;
   /* Enable the output */
   /* Enable the output */
   status = USBPD_PWR_IF_VBUSEnable(PortNum);
   status = USBPD_PWR_IF_VBUSEnable(PortNum);
   return status;
   return status;
}
}
/* USER CODE END USBPD_USER_PRIVATE_FUNCTIONS */
</syntaxhighlight>
</syntaxhighlight>
|}
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>usbpd_pdo_defs.h</strong>
|-
|'''<u>Between the ''/* USER CODE BEGIN-END Typedef */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
/**
  * @brief  USBPD Port PDO Structure definition
  */
typedef struct
{
  uint32_t *ListOfPDO;                          /*!< Pointer on Power Data Objects list, defining port capabilities */
  uint8_t  *NumberOfPDO;                        /*!< Number of Power Data Objects defined in ListOfPDO
                                                This parameter must be set at max to @ref USBPD_MAX_NB_PDO value */
} USBPD_PortPDO_TypeDef;


==Configure the shield's jumpers==
Place jumpers on the X-NUCLEO-SRC1M1 shield as shown in the picture.
[[File:USBPD_SRC1M1_JP_Conf.png|frame|center]]<br>


Next, plug an external 5V source into the green "source" connector.<br>
/**
  * @brief  USBPD Port PDO Storage Structure definition
  */
typedef struct
{
  USBPD_PortPDO_TypeDef    SourcePDO;            /*!< SRC Power Data Objects */
  USBPD_PortPDO_TypeDef    SinkPDO;              /*!< SNK Power Data Objects */
 
} USBPD_PWR_Port_PDO_Storage_TypeDef;
</syntaxhighlight>
 
'''<u>Between the ''/* USER CODE BEGIN-END Variables */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
#ifndef _GUI_INTERFACE
#ifndef __USBPD_PWR_IF_C
extern uint8_t USBPD_NbPDO[4];
extern uint32_t PORT0_PDO_ListSRC[USBPD_MAX_NB_PDO];
extern uint32_t PORT0_PDO_ListSNK[USBPD_MAX_NB_PDO];
#else /* __USBPD_PWR_IF_C */
uint8_t USBPD_NbPDO[4] = {(PORT0_NB_SINKPDO),
                          (PORT0_NB_SOURCEPDO)};
#endif /* __USBPD_PWR_IF_C */
#endif /* _GUI_INTERFACE */
</syntaxhighlight>
|}
 
{| role="presentation" class="wikitable mw-collapsible mw-collapsed"
| <strong>usbpd_pwr_if.c</strong>
|-
|'''<u>Between the ''/* USER CODE BEGIN-END Private_Variables */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
/**
  * @brief  USBPD Port PDO Storage array declaration
  */
USBPD_PWR_Port_PDO_Storage_TypeDef PWR_Port_PDO_Storage[USBPD_PORT_COUNT];
</syntaxhighlight>
 
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_Init */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
  USBPD_StatusTypeDef _status = USBPD_OK;
 
  /* Set links to PDO values and number for Port 0 (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];


With this configuration, the board will be powered by the ST-Link of the Nucleo board.<br>
  return _status;
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.
</syntaxhighlight>


==Compile and run the application==
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_SetProfile */'' tags:</u>'''
The compilation must be performed without error or warnings.<br>
<syntaxhighlight lang="c" class="noscroll">
Build the application by clicking on the [[File:Built_Button.png|20px|middle]] button (or select ''Project/Build Project'').<br>
  USBPD_PDO_TypeDef        _pdo;
Run the application by clicking on the [[File:DownloadRun_Button.png|20px|middle]] button (or select ''Run/Run'')<br>
  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);
</syntaxhighlight>


==Establish the first explicit contract==
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_GetPortPDOs */'' tags:</u>'''
[[File:Clock.png|40px|middle]] 5min<br><br>
<syntaxhighlight lang="c" class="noscroll">
With your application running on the board, launch the STM32CubeMonitor-UCPD application.
  if (DataId == USBPD_CORE_DATATYPE_SRC_PDO)
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").
#if defined (_GUI_INTERFACE)
[[File:USBPD_0-cubeMon.png|frame|center]]<br>
    *Size = USBPD_NbPDO[1];
    memcpy(Ptr,PORT0_PDO_ListSRC, sizeof(uint32_t) * USBPD_NbPDO[1]);
#else
    *Size = PORT0_NB_SOURCEPDO;
    memcpy(Ptr,PORT0_PDO_ListSRC, sizeof(uint32_t) * PORT0_NB_SOURCEPDO);
#endif /* _GUI_INTERFACE */
  }
  else
  {
#if defined (_GUI_INTERFACE)
    *Size = USBPD_NbPDO[0];
    memcpy(Ptr,PORT0_PDO_ListSNK, sizeof(uint32_t) * USBPD_NbPDO[0]);
#else
    *Size = PORT0_NB_SINKPDO;
    memcpy(Ptr,PORT0_PDO_ListSNK, sizeof(uint32_t) * PORT0_NB_SINKPDO);
#endif /* _GUI_INTERFACE */
  }
 
  uint32_t  nbpdo, index, nb_valid_pdo = 0;
  uint32_t  *ptpdoarray = NULL;
  USBPD_PDO_TypeDef pdo_first;
  USBPD_PDO_TypeDef pdo;


''Note: The ComPort may be different. It depends on the number of boards installed on the computer.''
  /* Check if valid port */
Then double click on the desired UCPD port, here Port 0, or select it and click "NEXT".
  if (USBPD_PORT_IsValid(PortNum))
[[File:USBPD_1-cubeMon.png|frame|center]]<br>
  {
    /* According to type of PDO to be read, set pointer on values and nb 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 unchunked 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;


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:
      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 nb of read PDO (nb of u32 elements); */
    *Size = nb_valid_pdo;
  }
</syntaxhighlight>


[[File:USBPD_2-cubeMon.png|frame|center]]<br>
'''<u>Between the ''/* USER CODE BEGIN-END USBPD_PWR_IF_SearchRequestedPDO */'' tags:</u>'''
<syntaxhighlight lang="c" class="noscroll">
  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;
</syntaxhighlight>
|}


The figure above shows the communication between the STM32G0 and the power delivery sink on the right panel. It is
==Project with a custom board==
possible to verify the correct sequence to reach an explicit contract:
# The '''capabilities are sent''' by the STM32G0 '''source''' (OUT orange message).
# The '''request is sent''' by the '''sink''' (IN green message).
# The '''ACCEPT''' and the '''PS_RDY''' are sent by the STM32G0 '''source''' (OUT orange message).
# The contract negotiation ends by the '''POWER_EXPLICIT_CONTRACT''' notification (blue message).
For more details on how to use this tool, refer to [https://www.st.com/resource/en/user_manual/dm00536366-stm32cubemonitorucpd-software-tool-for-usb-typec-power-delivery-port-management-stmicroelectronics.pdf UM2468].
And for more details on the protocol, refer to [https://www.st.com/resource/en/user_manual/dm00598101-managing-usb-power-delivery-systems-with-stm32-microcontrollers-stmicroelectronics.pdf 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.
This chapter allows building an USBPD source application using a custom board with an STM32 MCU from series G0, G4, H5, L5, or U5 that includes the UCPD peripheral.
[[File:USBPD_3-cubeMon.png|frame|center]]<br>
{{Info|Selected resources: ADC1-IN0, I2C1, PC5, and PC8, are for the example, replaced by the custom board resources affectation.}}
<br><br>
* As in chapter 2: Create the project
<big>'''You can find other applicative examples on GitHub: [https://github.com/STMicroelectronics/x-cube-tcpp x-cube-tcpp]'''</big>
* As in chapter 3.1: Clear the pinout
* As in chapter 3.2: Select the X-CUBE-TCPP software pack
Do not select the board support for X-NUCLEO-SRC1M1 as your application is based on a custom board.
[[File:STM32StepByStep 12 Custom SP Select SRC.png|center]]<BR>
* As in chapter 3.3: Configure UCPD Peripheral
* As in chapter 3.4: Configure FreeRTOS Middleware
* As in chapter 3.5: Configure USBPD Middleware
* Configure ADC peripheral
Select and configure the ADC and its channel on which Vbus is connected for monitoring.
Select the ADC and its channel.
Adjust the clock prescaler.
Keep 12 Bits resolution.
Enable the continuous conversion mode,
and set a medium cycle sampling time.
[[File:STM32StepByStep 13 Custom DRP SP ADC2.png|center]]<BR>
* Configure I2C Peripheral
Enable the I2C connected to the TCPP02 I2C bus.
Set its speed in ''fast Mode''.
[[File:STM32StepByStep 14 Custom SRC I2C2.png|center]]<BR>
* Configure GPIO
Enable and configure the external interrupt input where the TCPP02 ''FLG'' signal is connected.
Select it in the pinout view.
In the GPIO category, set it as external interrupt mode with falling edge trigger detection.
Enable its ''Pull-up''.
[[File:STM32StepByStep 15 Custom SRC FLG.png|center]]<BR>
In the Pinout view, select the GPIO Output for TCPP02 ''Enable''.
[[File:STM32StepByStep 16 Custom SRC Enable.png|center]]<BR>
* Enable the software Pack
In the middleware category, select the X-Cube-TCPP Software pack and enable its application and board part.
[[File:STM32StepByStep 16 Custom SRC Enable3.png|center]]<BR>
* Assign resources to the application requirements.
[[File:STM32StepByStep 18 Custom SRC SP Platform2.png|center]]<BR>
* As in chapter 4: Configure the project
However, in the advanced settings, keep ADC and I2C initialization code generation.
* As in chapter 5: Generate the code
* As in chapter 7: Compile and run the application


==References==
==References==
Line 756: Line 756:
<noinclude>
<noinclude>
[[Category:USB Power Delivery|20]]
[[Category:USB Power Delivery|20]]
[[Category:Getting started with STM32 : STM32 step by step|83]]
[[Category:Getting started with STM32 and USB-Power Delivery|20]]
 
{{PublicationRequestId | 31067| 14/05/24| }}
{{PublicationRequestId | 21785 | 22W2 | JM LAGOUTTE}}
{{PublicationRequestId | 21785 | 22W2 | JM LAGOUTTE}}
</noinclude>
</noinclude>

Latest revision as of 14:06, 22 August 2024

Target description

This tutorial helps to:

  • Use the X-NUCLEO-SRC1M1 shield that includes a TCPP02-M18 protection circuit and provides a USB Type-C® connector
  • Create a USB-PD source application with the NUCLEO-G071RB board and the X-NUCLEO-SRC1M1 shield by using STM32CubeIDE software

Prerequisites

  • Computer with Windows 7 (or higher)

Hardware

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

Software

  • STM32CubeMX (tested with V6.11.0 - minimal release 6.11.0) [3]
  • STM32CubeIDE (tested with V1.14.0) [4]
  • STM32CubeMonitor-UCPD (tested with V1.3.0) [5]
  • X-CUBE-TCPP MCU Firmware Package (BSP) [6]

Literature

Create a USB-PD Source Device

Clock.png Total 60min

1. Software pack installation

Open STM32CubeMX, in the software pack area, click on the install/remove button.

STM32StepByStep Install SP 1bis.png


Then select the STMicroelectronics tab, scroll down to the X-Cube-TCPP software pack, and click on the install button if it is not already installed.

STM32StepByStep Install SP 2.png


2. Creating the project

Clock.png 5min

In STM32CubeMX, create a new STM32 project. As a target selection, choose the NUCLEO-G071RB from the Board Selector Tab.

STM32StepByStep 03 Start Project G0.png


Click Start Project, then in the file menu, create a new folder under your project's name, and click Save.

STM32StepByStep Save Project As bis.png


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

3. Configuring the system

Clock.png 15min

At this point, your project is created. The next steps focus on the configuration of the peripherals and options needed for the project.

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


3.2. Select the X-Cube-TCPP software pack

From the software pack menu:

STM32StepByStep SP Menu v4.png


Select the X-CUBE-TCPP software pack and enable its Source application, the tcpp0203 board part, and the X-NUCLEO-SRC1M1 board support.

STM32StepByStep 06 SP Select SRC1M1.png



3.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 0-UCPD1Conf.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 except for DMA1_Channel1, which is used later by the BSP drivers.


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



Info white.png Information

If an STM32G4 is used instead 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 set to 5 causes the code execution to get stuck in the vPortValidateInterruptPriority function.

3.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 Source tab, verify the following configuration:

  • Number of Source PDOs for port 0: 1
  • Port 0 Source PDO 0 : 5V 100mA
STM32StepByStep 20 SP Source PDO Def.png


3.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.
Since the X-NUCLEO-SRC1M1 BSP is used, the ADC configuration does not need to be done in CubeMX.
As the ADC HAL drivers are required for it to work properly, the ADC needs to be configured in CubeMX for it to include the driver files, but the actual configuration and init function are not called in this project.

In the Analog section, enable ADC1 peripheral channel 0. Leave the configuration as default, as the software pack reconfigures it.

USBPD 0-ADC1Conf.png


3.7. Configure I2C peripheral

As the X-NUCLEO-SRC1M1 shield includes a TCPP02-M18 that communicates through I2C, the I2C peripheral needs to be enabled in this project.

In the Connectivity section, enable I2C2 peripheral in I2C mode. Leave the configuration as default, as the software pack reconfigures it.

USBPD 0-I2C2Conf.png


Note: Enable the I2C2 peripheral in the CubeMX view for code generation to include the I2C drivers as done for the ADC.

3.8. Enable the software pack

In the middleware and software pack category, select the X-Cube-TCPP software pack. Enable the 'Source' application, the 'tcpp0203' board part, and the 'X-NUCLEO-SRC1M1' board support.

STM32StepByStep 07 SP Enable SRC.png


3.9. Configure Clocks

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

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.

3.10. [OPTIONAL] Configure Tracer for debug

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


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


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


4. Configure the 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.

STM32StepByStep Config Proj2.png


For STM32G0 or G4 MCU, uncheck “Use default firmware location” and instead, select the software pack “c:|\user\ … \STM32Cube\Repositoryctronics/Packs\STMicroelectronics\X-CUBE-TCPP\V4.1.0\” as firmware location to be sure to use the latest USBPD lib releases as the standard evolution is very fast.

STM32StepByStep 08 Config Proj2 SRC.png




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

STM32StepByStep Conf Advanced Settings3.png


5. Generate the code

Clock.png 5min

Save your file with Ctrl+s and select generate code.

STM32StepByStep Generate3.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.

STM32CubeIDE starts and imports the project.


This project contains the following folders:

  • The USBPD folder contains the source files that are needed 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.

STM32StepByStep SRC1M1 Project Structure.png


6. Configure the shield's jumpers

Place the 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 is powered by the ST-Link of the Nucleo board.
To power your system from the external power supply connected to the "source" terminal, and not from the ST-Link, add the JP1 jumpers between 1-2 and 3-4.

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

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

9. Information focus : Code inserted by the software pack

By enabling the software pack in section 3.8, the code below has been added automatically in following files:

10. Project with a custom board

This chapter allows building an USBPD source application using a custom board with an STM32 MCU from series G0, G4, H5, L5, or U5 that includes the UCPD peripheral.

Info white.png Information
Selected resources: ADC1-IN0, I2C1, PC5, and PC8, are for the example, replaced by the custom board resources affectation.
  • As in chapter 2: Create the project
  • As in chapter 3.1: Clear the pinout
  • As in chapter 3.2: Select the X-CUBE-TCPP software pack

Do not select the board support for X-NUCLEO-SRC1M1 as your application is based on a custom board.

STM32StepByStep 12 Custom SP Select SRC.png


  • As in chapter 3.3: Configure UCPD Peripheral
  • As in chapter 3.4: Configure FreeRTOS Middleware
  • As in chapter 3.5: Configure USBPD Middleware
  • Configure ADC peripheral

Select and configure the ADC and its channel on which Vbus is connected for monitoring. Select the ADC and its channel. Adjust the clock prescaler. Keep 12 Bits resolution. Enable the continuous conversion mode, and set a medium cycle sampling time.

STM32StepByStep 13 Custom DRP SP ADC2.png


  • Configure I2C Peripheral

Enable the I2C connected to the TCPP02 I2C bus. Set its speed in fast Mode.

STM32StepByStep 14 Custom SRC I2C2.png


  • Configure GPIO

Enable and configure the external interrupt input where the TCPP02 FLG signal is connected. Select it in the pinout view. In the GPIO category, set it as external interrupt mode with falling edge trigger detection. Enable its Pull-up.

STM32StepByStep 15 Custom SRC FLG.png


In the Pinout view, select the GPIO Output for TCPP02 Enable.

STM32StepByStep 16 Custom SRC Enable.png


  • Enable the software Pack

In the middleware category, select the X-Cube-TCPP Software pack and enable its application and board part.

STM32StepByStep 16 Custom SRC Enable3.png


  • Assign resources to the application requirements.
STM32StepByStep 18 Custom SRC SP Platform2.png


  • As in chapter 4: Configure the project

However, in the advanced settings, keep ADC and I2C initialization code generation.

  • As in chapter 5: Generate the code
  • As in chapter 7: Compile and run the application

11. References