2023-03-30 07:17 PM
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?
Solved! Go to Solution.
2023-04-07 01:08 AM
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
2023-04-03 08:59 AM
Hello @RHelv.1
This is something that was not planned.
Can you tell a bit more why you need this ?
Regards,
Nicolas
2023-04-03 09:05 AM
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
2023-04-05 01:18 PM
@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.
2023-04-06 10:13 AM
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
2023-04-06 02:28 PM - edited 2023-11-20 09:05 AM
@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
2023-04-07 01:08 AM
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
2023-04-10 08:40 AM
Thank you @Dominique . I have this working. Much appreciated.
Rob