cancel
Showing results for 
Search instead for 
Did you mean: 

Trying to de-initialize the USBPD library

RHelv.1
Associate II

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?

1 ACCEPTED SOLUTION

Accepted Solutions
Dominique
ST Employee

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

View solution in original post

7 REPLIES 7
Nicolas P.
ST Employee

Hello @RHelv.1​ 

This is something that was not planned.

Can you tell a bit more why you need this ?

Regards,

Nicolas

RHelv.1
Associate II

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.1
Associate II

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

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.1
Associate II

@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 

Dominique
ST Employee

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.1
Associate II

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

Rob