Skip to main content
Visitor II
March 31, 2023
Solved

Trying to de-initialize the USBPD library

  • March 31, 2023
  • 6 replies
  • 2722 views

I'm using ST's USBPD middleware library and have it successfully working as both a source and a sink. However, I have a use case where I will need to de-initialize the USBPD library and reset the state machine back to it's initial state. Ideally when this happens, the loadswitch opens and power to the accessory is killed. I can't find an easy way to fake a dettach event, or even to de-initialize the USBPD library and reset the internal state machine. Are there functions that support this?

    This topic has been closed for replies.
    Best answer by Dominique

    Hello @RHelv.1​ 

    I checked you file and if your application is using DRP state machine you need to apply following fix to avoid a new attachment

    line 854 of your files

    case USBPD_CAD_STATE_SWITCH_TO_SRC:
     case USBPD_CAD_STATE_SWITCH_TO_SNK:
     {
     /* patch force detach */
     if( _handle->ForceSRCDetach == USBPD_TRUE)
     {
     _timing = CAD_INFINITE;
     }
     else
     {
     LL_UCPD_RxDisable(Ports[PortNum].husbpd);
     if (USBPD_CAD_STATE_SWITCH_TO_SRC == _handle->cstate)
     {
     USBPDM1_AssertRp(PortNum);
     Ports[PortNum].params->PE_PowerRole = USBPD_PORTPOWERROLE_SRC;
     Ports[PortNum].params->PE_DataRole = USBPD_PORTDATAROLE_DFP;
     _timing = Ports[PortNum].settings->CAD_SRCToggleTime;
     }
     if (USBPD_CAD_STATE_SWITCH_TO_SNK == _handle->cstate)
     {
     USBPDM1_AssertRd(PortNum);
     Ports[PortNum].params->PE_PowerRole = USBPD_PORTPOWERROLE_SNK;
     Ports[PortNum].params->PE_DataRole = USBPD_PORTDATAROLE_UFP;
     _timing = Ports[PortNum].settings->CAD_SNKToggleTime;
     }
     _handle->CAD_tToggle_start = HAL_GetTick();
     _handle->cstate = USBPD_CAD_STATE_DETACHED;
     }
     }
     break;

    This patch is not perfect because it doesn't allow switching to SNK case (which could be interesting for a DRP requiring power). But you can easily allow the execution of the sink part in ForceSRCDetach case.

    BR

    Dominique

    6 replies

    ST Employee
    April 3, 2023

    Hello @RHelv.1​ 

    This is something that was not planned.

    Can you tell a bit more why you need this ?

    Regards,

    Nicolas

    RHelv.1Author
    Visitor II
    April 3, 2023

    Hi @Nicolas P​ 

    My product is battery powered, and is a SRC. If the battery state of charge decreases past a certain value, say 5% or so, I need to kill power to all connected SNKs. I can do by opening the hardware loadswitch, but then in this case, the USBPD state machine is not in sync with the hardware. If the user then plugs in their device to recharge the battery and the battery voltage increases past a certain value, I need to re-initialize the USBPD library. I can't just close the hardware load switch in this case, as the USBPD library may be in a different state.

    Please let me know if there are elegant ways to reset or re-initialize the internal state machines of the USBPD library.

    Thanks,

    Rob

    RHelv.1Author
    Visitor II
    April 5, 2023

    @Nicolas P​ Just wanted to follow up here. For my application, I can prevent powering the SNK during this low battery voltage state. But I've noticed that if I get into this low battery voltage state without a SNK applied, and then attach a SNK, SRC_CAP gets sent indefinety. This obviously isn't good for MCU cycles, or even the attached SNK. Is there a way in the STM32 code to prevent SRC_CAP from being sent? I don't see this functionality.

    Again, this is why the library should be de-initialized, and then re-initialized, to prevent these corner cases.

    ST Employee
    April 6, 2023

    Hello @RHelv.1​ 

    To better understand the states and the sequences, I hope you have implemented the debug trace as described here in the wiki. If will really help. And for further support, it would be better if you can share the obtained trace.

    So for your request, you could try the following idea : add a new parameter "forceSRCDetach" in CAD_HW_HandleTypeDef structure, to stick the CAD in detach.

    We've prototype it, and it looks ok. Here what you can try, based on the version in github here.

    In file usbpd_cad_hw_if.c :

    typedef struct
    {
     CCxPin_TypeDef cc : 2;
     CAD_HW_Condition_TypeDef CurrentHWcondition : 3;
     uint32_t CAD_tDebounce_flag : 1;
     uint32_t CAD_tDebounceAcc_flag : 1;
     uint32_t CAD_ErrorRecoveryflag : 1;
     uint32_t CAD_ResistorUpdateflag : 1;
     USBPD_CAD_STATE cstate : 5; /* current state */
     uint32_t CAD_Accessory_SRC : 1;
     uint32_t CAD_Accessory_SNK : 1;
     uint32_t reserved : 1;
     USBPD_CAD_STATE pstate : 5; /* previous state */
     uint32_t forceSRCDetach : 1; /* force detach patch */
    #if defined(USBPDCORE_VPD)
     uint32_t CAD_VPD_SRC : 1;
     uint32_t CAD_VPD_SNK : 1;
     uint32_t reserved2 : 7; /* force detach patch */
    #else
     uint32_t reserved2 : 9; /* force detach patch */
    #endif /* USBPDCORE_VPD */
     
    #if defined(_DRP) || defined(_ACCESSORY_SNK)
     uint32_t CAD_tToggle_start;
    #endif /* _DRP */
     uint32_t CAD_tDebounce_start; /* Variable used for attach or detach debounce timers */
     CAD_StateMachinePtr *CAD_PtrStateMachine;
    } CAD_HW_HandleTypeDef;

    Then declare a new function : CAD_SRC_ForceDetach

    /* force detach patch */ 
    void CAD_SRC_ForceDetach(uint8_t PortNum, uint8_t State)
    {
     if (State == 0)
     {
     CAD_HW_Handles[PortNum].forceSRCDetach = USBPD_FALSE;
     USBPDM1_AssertRp(PortNum);
     }
     else
     {
     CAD_HW_Handles[PortNum].forceSRCDetach = USBPD_TRUE;
     }
     /* wakeup CAD */
     Ports[PortNum].USBPD_CAD_WakeUp();
    }

    Then in the CAD_StateMachine_SRC (or DRP) you need to check if you are in forced detach state, to avoid attaching again :

    case USBPD_CAD_STATE_DETACH_SRC :
     {
    #if defined(_VCONN_SUPPORT)
     /* DeInitialize Vconn management */
     (void)BSP_USBPD_PWR_VCONNDeInit(PortNum, (Ports[PortNum].CCx == CC1) ? 1u : 2u);
    #endif /* _VCONN_SUPPORT */
     /* DeInitialise VBUS power */
     (void)BSP_USBPD_PWR_VBUSDeInit(PortNum);
     /* Reset the resistor */
     if( _handle->ForceSRCDetach == USBPD_FALSE) /* force detach patch */ 
     {
     USBPDM1_AssertRp(PortNum);
     }
     
     _handle->cstate = USBPD_CAD_STATE_DETACHED;
     _timing = 0;
     break;
     }

    Then you need to check also if you are in forced detach state, whenever you go into the USBPD_CAD_STATE_DETACHED case, in the CAD_StateMachine_SRC (or DRP). It would look like :

    case USBPD_CAD_STATE_DETACHED:
     {
     /* force detach patch */ 
     if( _handle->ForceSRCDetach == USBPD_TRUE)
     {
     _timing = CAD_INFINITE;
     }
     else
     {
     USBPDM1_AssertRp(PortNum);
     _timing = ManageStateDetached_SRC(PortNum);
     }
     break;
     }

    Last modification to be done, in function ManageStateAttached_SRC :

    /* Check if CC lines is opened or switch to debug accessory */
     /* force detach patch */
     if (_handle->forceSRCDetach == USBPD_TRUE)
     {
     HW_SignalDetachment(PortNum);
     USBPDM1_EnterErrorRecovery(PortNum);
     _handle->cstate = USBPD_CAD_STATE_DETACH_SRC;
     *pEvent = USBPD_CAD_EVENT_DETACHED;
     *pCCXX = CCNONE;
     _timing = CAD_INFINITE_TIME;
     }
     else
     {
    ...

    And to finish, you can now call in your application :

    CAD_SRC_ForceDetach(PortNum, 1); /* force the detach */
    CAD_SRC_ForceDetach(PortNum, 0); /* release the detach */

    Hope it helps.

    Regards,

    Nicolas

    RHelv.1Author
    Visitor II
    April 6, 2023

    @nicolas P​ Thank you for this code and ongoing support. After testing it, when I force the Detach, instead the USBPD code oscillates between Attached and Detached. I've attached my usbpd_cad_hw_if.c file for you to review.

    Specifically, I'm unsure where in the CAD_StateMachine_DRP function (not SRC) I need to make edits, and also if the else clause in ManageStateAttached_SRC was implemented properly.

    Thank you,

    Rob
    _legacyfs_online_stmicro_images_0693W00000bhgptQAA.png 

    DominiqueAnswer
    ST Employee
    April 7, 2023

    Hello @RHelv.1​ 

    I checked you file and if your application is using DRP state machine you need to apply following fix to avoid a new attachment

    line 854 of your files

    case USBPD_CAD_STATE_SWITCH_TO_SRC:
     case USBPD_CAD_STATE_SWITCH_TO_SNK:
     {
     /* patch force detach */
     if( _handle->ForceSRCDetach == USBPD_TRUE)
     {
     _timing = CAD_INFINITE;
     }
     else
     {
     LL_UCPD_RxDisable(Ports[PortNum].husbpd);
     if (USBPD_CAD_STATE_SWITCH_TO_SRC == _handle->cstate)
     {
     USBPDM1_AssertRp(PortNum);
     Ports[PortNum].params->PE_PowerRole = USBPD_PORTPOWERROLE_SRC;
     Ports[PortNum].params->PE_DataRole = USBPD_PORTDATAROLE_DFP;
     _timing = Ports[PortNum].settings->CAD_SRCToggleTime;
     }
     if (USBPD_CAD_STATE_SWITCH_TO_SNK == _handle->cstate)
     {
     USBPDM1_AssertRd(PortNum);
     Ports[PortNum].params->PE_PowerRole = USBPD_PORTPOWERROLE_SNK;
     Ports[PortNum].params->PE_DataRole = USBPD_PORTDATAROLE_UFP;
     _timing = Ports[PortNum].settings->CAD_SNKToggleTime;
     }
     _handle->CAD_tToggle_start = HAL_GetTick();
     _handle->cstate = USBPD_CAD_STATE_DETACHED;
     }
     }
     break;

    This patch is not perfect because it doesn't allow switching to SNK case (which could be interesting for a DRP requiring power). But you can easily allow the execution of the sink part in ForceSRCDetach case.

    BR

    Dominique

    RHelv.1Author
    Visitor II
    April 10, 2023

    Thank you @Dominique​ . I have this working. Much appreciated.

    Rob