cancel
Showing results for 
Search instead for 
Did you mean: 

USB PD Source Capabilities send too early

abtq
Associate II

Hi ST Community,

once again I have found a USB PD Specification violation, that I can't find the reason for:

Source operation: According to USB PD Spec the order of action is as follows:

1. Detect Sink using CC lines and only if sink is detected continue with the next points

2. Enable VBus to safe5V

3. Once safe5V is applied send source capabilities message

 

However current implementation of the ST Stack does not care about the state of VBus. It starts immediately sending out source capabilities messages.

I guess the problem is that PE Task is started immediately on Attach event in USBPD_DPM_CADCallback. And it never checks for the bus voltage.

 

I composed a crude minimal example that does not even do anything when it should enable VBus but still source capabilities message is blown out immediately. Please find the minimal example attached.

 

Did I miss anything? Will I need to find a workaround or can we get a fixed version of the library?

Best regards,
Adrian

5 REPLIES 5
abtq
Associate II

I don't know if it is related but it might be:
I just found another Problem: When acting as a source the sink sends a request for a specific PDO and the "magic" ST Stack just answers PS RDY exactly 30ms after. According to Spec before the USB C 3.2 Spec 7.3.1.1.1 "Generic Transition Diagram for changing the Source to another (A)PDO" source needs to wait tSrcTransition (25-35ms) before it is even allowed to transition to the new voltage setpoint. That 30ms I measure fits the tSrcTransition time window perfectly, so maybe the stack waits that time window and then immediately sends PS RDY?
But why does it not check voltage Levels? Am I missing something?

 

Putting a breakpoint in BSP_USBPD_PWR_VBUSGetVoltage is not triggered when before transitioning. USBPD_PWR_IF_SupplyReady is not called either....

 

Hope you can help me on this. Unfortunately the stack documentation is quite weak and as it is a black box where you can't look into I can't find further places I can dig into. Sometimes I feel like I am the first one to test it for specification compliance....

Best Regards,

Adrian

Edit: when looking at the example at https://github.com/STMicroelectronics/STM32CubeH5/blob/main/Projects/STM32H573I-DK/Applications/USBPD/USBPD_SRC/USBPD/App/usbpd_pwr_if.c
I can see vbus_transition is set to different voltage levels, but that variable is never used? Where should it have been used?

FBL
ST Employee

Hi @abtq 


We need to control periodically VBUS as specified in UM2552  after USBPD_DPM_CADCallback, we need to launch the PE task and we control periodically VBUS.. Our documentation clearly explains how it should work !

FBL_0-1757326878331.png

I couldn't reproduce the behavior using H5 disco board and your firmware! Would you attach .cpd trace using Cube Monitor UCPD for quick check?

About your second point, I assume vbus_transition is not used in this example because it initializes the type C port in source mode with only one PDO at 5V.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.


abtq
Associate II

Hi @FBL ,

 

thanks for the support!
As I am running on a custom hardware I wasn't easily able to run STM32CubeMonitor-UCPD, but I managed to at least output the trace as text using the "Select Tracer Port" in the top right (however a txt is generated then instead of a .cpd). Hope this helps as well, otherwise I will need to dig deeper into that. Please find the txt attached.

 

Here you can see the issue as well: At timestamp 19773 VBus is enabled (which just does nothing in the dummy implementation attached to my first post) and immediately after at timestamp 19773 to 19778 three source capabilities messages are sent out. But in between BSP_USBPD_PWR_VBUSGetVoltage is never queried...

abtq_2-1757400101648.png

In an external trace tool you can see the same: Without VBus enabling (obviously as in this minimal example I am doing nothing to enable VBus) Source Capabilities are spammed out on CC lines...

 

Regarding your first point: I don't really get your point. In my example I am source and not sink. So I guess the relevant timing diagram from UM2552 would be this?

abtq_0-1757399643321.png

In this diagram there is also nowhere mentioned, that actually for USB PD Compliance you would want to wait between VBUS-ON and probably the launch of the PE Task (or you would need to wait in the launched PE Task) until VBus really is there. So I would expect something like this:

abtq_1-1757399958714.png

or alternatively the waiting part in the PE Task.

What am I missing?

 

I can see that the headline of the implementation in my picture is "User Application". Here I have just been using the default generated code by CubeMX:

void USBPD_DPM_CADCallback(uint8_t PortNum, USBPD_CAD_EVENT State, CCxPin_TypeDef Cc)
{
 /* _TRACE */
  (void)(Cc);
  switch (State)
  {
    case USBPD_CAD_EVENT_ATTEMC :
    {
#if defined(_VCONN_SUPPORT)
      DPM_Params[PortNum].VconnStatus = USBPD_TRUE;
#endif /* _VCONN_SUPPORT */
      USBPD_DPM_UserCableDetection(PortNum, USBPD_CAD_EVENT_ATTEMC);
      DPM_StartPETask(PortNum);
      break;
    }
    case USBPD_CAD_EVENT_ATTACHED :
      USBPD_DPM_UserCableDetection(PortNum, USBPD_CAD_EVENT_ATTACHED);
      DPM_StartPETask(PortNum);
      break;
 // .......
}

 VBus is enabled in USBPD_DPM_UserCableDetection and immediately after DPM_StartPETask starts the PE Task. Do I have to add a wait manually in here? (actually you don't want to pause the execution in a callback so I guess I would have to do some state machine magic here and remember to start the PE Task once VBus is on...)

Would a handling inside PE Task (or somewhere else where there is already a state machine) not be more elegant?

Looking forward to your response! I feel like I am heading in the wrong direction here...

FBL
ST Employee

Hi @abtq 

To clarify some points:

 

  1. Regarding VBUS checking before starting the PE task:
    The USB PD stack relies on the CAD state machine to monitor VBUS presence through the power interface functions like BSP_USBPD_PWR_VBUSGetVoltage(). The PE task is triggered by the CAD callback on attach events, but internally the CAD state machine verifies VBUS status before allowing the PE state machine to proceed. This means the stack does not block or wait explicitly before starting PE; instead, it depends on accurate VBUS detection in the CAD layer.

  2. Your observation about missing VBUS voltage queries:
    From your trace and description, it appears that USBPD_PWR_IF_ReadVA() and related power interface functions are not implemented since your code is generated with CubeMX. Without a proper VBUS voltage measurement, the CAD state machine cannot detect VBUS presence correctly. This leads to the PE task starting immediately and Source Capabilities messages being sent prematurely, as you observed.

  3. On the figure 11 in UM2552 and compliance:
    In my understanding, USB PD specification does not explicitly require a delay between enabling VBUS and starting the PE task. Instead, it expects the CAD layer to handle VBUS detection asynchronously. Blocking inside the CAD callback to wait for VBUS is not recommended and would complicate the state machine.

So what is the recommended approach?
You should implement the power interface functions properly so that BSP_USBPD_PWR_VBUSGetVoltage() returns the actual VBUS voltage. This enables the CAD state machine to detect VBUS presence correctly and only then start the PE task. This approach is cleaner and aligns with the stack’s design.

The USBPD example firmware you already mentioned for STM32H573I-DK fully implements these power interface functions and integrates with STM32CubeMonitor-UCPD for trace and debugging. I recommend reviewing that example to understand how VBUS detection and PE task triggering are coordinated. Also, this wiki  STM32StepByStep:Getting started with USB-Power Delivery Source - stm32mcu and Introduction to USB Power Delivery with STM32 - stm32mcu can help you understand better the stack.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.


abtq
Associate II

Hi @FBL ,

thanks for the explanations, however to me it looks like it is behaving differently than you describe it should behave.
I have also implemented all the PWR functions (which I already had in my base project and just didn't bother to do for the minimal example as it already shows the problem) without any difference, but lets walk through it slowly:

 

  1. Regarding your first point: You are describing exactly what I had expected how the system would work but unfortunately looking at the log in my last message this is not what the system does.
    - What I understand that you are saying is the following order: CAD detects cable attach -> VBus is enabled to 5V -> VBUSGetVoltage() is querried repetedly until vSafe5V reached -> PE Task is started
    - What I see in the logs:
    abtq_0-1757507918136.png
    CAD detects cable attach -> VBus is enabled to 5V -> PE Task is started
    There is no VBUSGetVoltage query in between. This can also be seen in the USBPD_DPM_CADCallback() I cited in my last post. VBus is enabled in one line of code and right in the next PE Task is started. There would need to be a state machine in between with a state "WaitForVSafe5V" or something similar.

    The only option how I can get that observation to fit to what I am seeing is that VBus enable would need to be called earlier (maybe in another callback that I am missing and not in USBPD_DPM_CADCallback ?).

    Do you see a different Log in your case? Is there a VBUSGetVoltage query between turning on VBus and enabling PE Task? Can you share your Log?
  2. Yes in the minimal example my PWR functions were barely populated, but breakpoints in there never hit and e.g. looking at that mentioned USBPD_PWR_IF_ReadVA() function, there is not a single use in the project.
    Nevertheless I have now also populated the rest of the PWR functions (with stubs) without any difference:
    abtq_1-1757508836796.pngabtq_2-1757509012654.pngabtq_3-1757509046285.png

    Breakpoints in most of the new ones also never hit. Obviously the implementations are stubs but when VBus does not turn on and VBUSGetVoltage always return 0V then it should never send SRC Capabilities, right? Even if it would magically just not show the call to VBUSGetVoltage in the trace. Or is there a different function that I need to change that is used to determine VBus voltage?
    You are saying "Without a proper VBUS voltage measurement, the CAD state machine cannot detect VBUS presence correctly" - I have always had a "propper" voltage measurement. It does always return BSP_ERROR_NONE and put 0V to where pVoltage points, which is correct as I am never really enabling any voltage. Or does the stack implement something like "oh if voltage is exactly 0V all the time, measurement must be faulty, lets just asume VBus is on"?

  3. Ok understood, but this is kind of the same point as in 1.: If it is the CAD layer's responsibility to enable VBUS and then detect its presence and then enabling the PE Task it needs to enable VBUS first and then wait or rather poll for the bus voltage. But it seems to not do that as already mentioned (see USBPD_DPM_CADCallback reference implementation).

As you also advise against blocking or adding a state machine I think I should fix this issue, but I don't see the solution yet.

 

Looking at the reference https://github.com/STMicroelectronics/STM32CubeH5/blob/main/Projects/STM32H573I-DK/Applications/USBPD/USBPD_SRC/USBPD/Target/usbpd_pwr_user.c BSP_USBPD_PWR_VBUSOn(uint32_t Instance) function it doesn't even turn on VBus (and even returns BSP_ERROR_FEATURE_NOT_SUPPORTED)?! Where does turning on happen? Searching for PG0, GPIOG, UCPD_PWR does not bring any result for project https://github.com/STMicroelectronics/STM32CubeH5/tree/main/Projects/STM32H573I-DK/Applications/USBPD/USBPD_SRC 

 

To sum up:

  1.  Even with PWR Interfaces implemented nothing has changed (maybe I missed a function, that's why I added the diff images)

  2. That nothing changed is expected (from my point of view) as the USBPD_DPM_CADCallback implementation enables VBUS and right after starts the PE Task
  3. Even with that BSP_USBPD_PWR_VBUSGetVoltage implemented to return BSP_ERROR_NONE and 0V it seems to be never called between enabling VBUS and starting PE Task

 

So what's next?
Can you provide your log of it working?
If you confirm it is working for you I will probably have to get one of the USB PD reference boards to check if it works there... (but still I can't expect it to - see point 2 in "To sum up")

 

I am looking forward to your hints, but I am not in office the next two weeks so please apologise a late response from my side.