Skip to main content
Graduate II
May 16, 2024
Question

How to make custom user USBPD PPS sink application on NucleoG0B1RE + SNK1M1

  • May 16, 2024
  • 3 replies
  • 4839 views

Dear community,

I have deployed a basic USB PD sink application according to AN5418 on NucleoG0B1RE + SNK1M1 hardware. I  have also implemented a USBPD_DPM_RequestMessageRequest() function inside usbpd_dmp_user.c (according to this post), so the user can ask for power profiles in CubeMonitor-UCPD

I have also added some custom application code app.c in order to utilize encoder button and segment display. I want the user to choose relevant voltage and current value and make a request for new PPS. Yet the problem is that I am having quite hard time understanding all the different functions and underlying dependencies. Are there some further materials to understand the USBPD lib expect UM2902, and UM2552

I guess my steps are:

1. Get the SRC PDOs and limit the user voltage range

  • Extract the max and min voltages from following struct: DPM_Ports[PortNum].DPM_ListOfRcvSRCPDO[(IndexSrcPDO - 1)];
  • limit the user to choose from allowed voltage range

2. Build rdo object from user request details

  • build USBPD_SNKRDO_TypeDef rdo.d32 or rdo.ProgRDO in my case? from the USBPD_DPM_SNKPowerRequestDetailsTypeDef

3. Find the SrcPDOIndex matching the user request

  • use previously builded rdo in order to find relevant SRC APDO index, probably by using USER_SERV_FindVoltageIndex() (located in usbpd_user_services.c)

4. Request the PPS using USBPD_DPM_RequestMessageRequest() (located in usbpd_dpm_user.c)

  • use SrcPDOIndex from point 3 to make new request

github: link

 

Could you please give me some small guidance or provide me with further resources to better grasp the USB PD library as a whole?

 

Related question:

What is the point of predefining voltage and current ranges for APDOs like in STM32G0C1E-EV/.../usbpd_pdo_defs.h if we dont know what will be the ranges of actual wall adapter (SRC provider)? Or should we just use predefined ranges according to USB PD Specification on (usb.org) under 10.2.3.2.1 SPR Programmable Power Supply Voltage Ranges?

    This topic has been closed for replies.

    3 replies

    Technical Moderator
    May 23, 2024

    Hello @Marosh 

     

    Interesting question!

    First, the steps are correct:

    1. Get the SRC PDOs and limit the user voltage range

    2. Build RDO object from user request details

    3. Find the SrcPDOIndex matching the user request

    4. then request the PPS using USBPD_DPM_RequestMessageRequest() (as you mentioned located in usbpd_dpm_user.c)

     

    Second, the predefined voltage and current ranges in usbpd_pdo_defs.h depends on customer application (either fixed PDO or PPS Augmented PDO) In fact; PPS is meant to charge directly battery with needed precise voltage. 

    We propose this demo on G0 Eval board or x-cube-tcpp/Projects/NUCLEO-G071RB/Applications/USB_PD/SNK1M1_Sink_PPS at main · STMicroelectronics/x-cube-tcpp (github.com) to learn how to use PPS

    These are common use cases that should be compliant with the USB PD specification. These predefined voltage and current ranges are used to configure the device's capabilities and can be adjusted based on your application.

    When developing a product, you should consider the range of sources your device will support and define the PDOs accordingly. If you expect to support a wide range of sources, you may need to define a wider set of PDOs.

    Indeed, the USBPD specification provides standard ranges for PDOs, but you can also define custom PDOs based on the specific requirements of your application and the capabilities of the sources you intend to have.

    In case the source is a multiple port wall charger, it can request to the sink, its capabilities. It will help the wall charger to adapt its power strategy. Because power contract can change a long time. (if you charge a laptop, when charge finishes, you may change PDO to use a lower profile)

    We suggest this App Note as well to learn more about the implementation of the VBUS control algorithm VBUS control algorithm compliant with USB Type-C and Power Delivery specifications - Application note (st.com)

    MaroshAuthor
    Graduate II
    May 28, 2024

    Thank you for your advice @FBL 

    I have successfully extracted the src PDOs limits and I know how to build the RDO, yet after further investigation I am stuck on the 3. and 4. point (Finding voltage index and making a Request). Since both these function use function USER_SERV_SNK_EvaluateMatchWithSRCPDO() which requires predefined SNK PDOs. Yet my application is versatile and should support wide range of wall adapters up to 100W. This means I dont know the precise APDO profiles a priori and predefining multiple most common ones, only by guess, doesnt seem like the correct way to do so to me. Is there some way to define the SNK PDOs  (APDOs in my case) dynamically based on received SRC PDOs or could I somehow bypass the need to have predefined SNK PDOs by making my own custom USER_SERV_SNK_EvaluateMatchWithSRCPDO() function?
    Also as it seems to me the basic USER_SERV_FindVoltageIndex() function is called within USBPD_USER_SERV_EvaluateCapabilities() which is called on the first contract establishment and later on if user request cannot be reached. Contrary to my previous believe the FindVoltageIndex() function seems to rather work like finding for example maximal SRC PDO index based on chosen MAX_PWR Method. Yet If I want to find PDO index based on user PowerRequest (voltage, current) I probably need to make my own custom function to do so as mentioned in Introduction to USB PD (9.9 SNK PDO selection policy) 

    **
     * @brief Evaluate received Capabilities Message from Source port and prepare the request message
     * @param PortNum Port number
     * @param PtrRequestData Pointer on selected request data object
     * @param PtrPowerObjectType Pointer on the power data object
     * @retval None
     */
    void USBPD_USER_SERV_EvaluateCapa(uint8_t PortNum,
     uint32_t *PtrRequestData,
     USBPD_CORE_PDO_Type_TypeDef *PtrPowerObjectType)
    {
     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_SNKPowerRequestDetailsTypeDef snkpowerrequestdetails;
     uint32_t pdoindex;
     uint32_t size;
     uint32_t snkpdolist[USBPD_MAX_NB_PDO];
     USBPD_PDO_TypeDef snk_fixed_pdo;
    
     snkpowerrequestdetails.RequestedVoltageInmVunits = 0;
     snkpowerrequestdetails.OperatingCurrentInmAunits = 0;
    
     /* Find the Pdo index for the requested voltage, depending on the wanted method */
     pdoindex = USER_SERV_FindVoltageIndex(PortNum, &snkpowerrequestdetails, USER_SERV_PDO_Sel_Method);
    
     /* 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)
     uint8_t msg[] = "USBPD_USER_SERV_EvaluateCapa: could not find desired voltage";
     USBPD_TRACE_Add(USBPD_TRACE_DEBUG, PortNum, 0, (uint8_t *)msg, sizeof(msg));
    #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 = 1U;
     rdo.FixedVariableRDO.OperatingCurrentIn10mAunits = fixed_pdo.SRCFixedPDO.MaxCurrentIn10mAunits;
     rdo.FixedVariableRDO.MaxOperatingCurrent10mAunits = fixed_pdo.SRCFixedPDO.MaxCurrentIn10mAunits;
     rdo.FixedVariableRDO.CapabilityMismatch = 1U;
     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 = 5000U;
     return;
     }
    
     USER_SERV_SNK_BuildRDOfromSelectedPDO(PortNum, pdoindex, &snkpowerrequestdetails, &rdo, PtrPowerObjectType);
    
     *PtrRequestData = pdhandle->DPM_RequestDOMsg;
    }
    MaroshAuthor
    Graduate II
    June 4, 2024

    I have managed to dynamicaly change the SNK PDOs according to received SRC PDOs by adjusting the
    USBPD_USER_SERV_StoreSRCPDO() function:

    void USBPD_USER_SERV_StoreSRCPDO(uint8_t PortNum, uint8_t *Ptr, uint32_t Size)
    {
     /*!< Storage of Received Source PDO values */
     if (Size <= (USBPD_MAX_NB_PDO * 4U))
     {
     uint8_t *rdo;
     DPM_Ports[PortNum].DPM_NumberOfRcvSRCPDO = (Size / 4U);
     /* Copy PDO data in DPM Handle field */
     for (uint32_t index = 0; index < (Size / 4U); index++)
     {
     rdo = (uint8_t *)&DPM_Ports[PortNum].DPM_ListOfRcvSRCPDO[index];
     (void)memcpy(rdo, (Ptr + (index * 4U)), (4U * sizeof(uint8_t)));
     }
    
     // Copy PDO data in SINK pdo definition
    	for (uint32_t index = 0; index < (Size / 4U); index++)
    	{
    	 rdo = (uint8_t *)&PORT0_PDO_ListSNK[index];
    	 (void)memcpy(rdo, (Ptr + (index * 4U)), (4U * sizeof(uint8_t)));
    	}
    
     }
    }

     But the problem is that when I try to request specific profile in UCPD monitor, it works fine for Fixed profiles but APDO requests are rejected. If I later on delte the APDO profile and redefine it in UCPD Monitor with the same values it suddenly works. What could be causing this problem?

     

    Technical Moderator
    June 5, 2024

    Hello @Marosh 


    According to spec, Sink sends Sink_Capabilities Message in response to a Get_Sink_Cap Message. Typically, it cannot initiate the sending of its capabilities without a request from the source. Also, Sink's capabilities are typically declared in the form of Power Data Objects (PDOs) during the initial power negotiation phase. For that reason, if you reconfigure after flashing and send it to the target APDO request. 

    Handling different types of PDOs is managed by the protocol and the devices' PD controllers, which are designed to interpret and negotiate power contracts.

    ST Employee
    June 14, 2024

    Hello Marosh,

    I am not sure to understand your point. According to USB Power Delivery standard a sink device can draw up to 5A. This is the maximum current value you can reach. If your hardware is able to draw such current you can define your  sink APDO so it can accept a maximum current of 5A. Then whatever be the maximum current of the source you will always be able to negociate a contract with it.

     

    Best Regards,

    Lucas