cancel
Showing results for 
Search instead for 
Did you mean: 

OTG_HPRT>PENCHNG bit in STM32H743

JBernard
Associate II

[Not a bug report, explanation below, first post kept for context]

I'm writing a USB host library for the STM32H743 rev V. Bits PENA and PENCHNG of register OTG_HPRT represent Port enable and Port enable/disable change. Port enable is supposed to be a status bit - can't be set, can be reset which disables the port, or is reset when the port is disabled because of a disconnect.

PENCHNG is supposed to be change detection bit about PENA, gets set to 1 whenever PENA change, optionally triggers an interrupt via a chain of interrupt-aggregating register. PENCHNG is supposed to be reset when it causes an interrupt. This reset is supposed to be accomplished by writing 1 to this bit, as per the datasheet. PENA is also described as rc_w1 (clearable by writing 1) but that's probably a mistake.

Except, writing 1 to PENCHNG doesn't clear PENCHNG, it clears PENA, and disables the port. Writing 1 to PENCHNG a second time (when PENA is 0) sets PENCHNG to 0 as expected. This makes it impossible to clear the bit so that it triggers an interrupt when the port is disconnected, without disconnecting the port yourself.

JBernard_1-1725316374583.png

JBernard_2-1725316381451.png

A USB reset starts between log1 and log2. It ends between log2 and log3. Log3 reports the port is now enabled. PENCHNG is set between log3 and log4. PENCHNG is still 1, PENA is 0 (the port is now disabled). PENCHNG is set again between log4 and log5. PENCHNG is now 0.

For the record, here's the relevant excerpts from the reference:

lS9ZB1H.png

K831E2a.png

Also, HPRT isn't built into CubeMX, as far as I can tell, this is how I defined USB2_OTG_FS__HPRT:

 

 

 

__IO uint32_t& USB2_OTG_FS__HPRT = (__IO uint32_t&)*(((uint8_t*)USB2_OTG_FS_PERIPH_BASE)+0x440);

 

1 ACCEPTED SOLUTION

Accepted Solutions
JBernard
Associate II

The lightbulb went on as I read this again and realized that |= writes 1 to all the bits that are set to 1.

That means the first write writes to PENCHNG and PENA, clears both of them, then PENCHNG goes back to 1 because PENA just went to 0.

So the correct way to go about a shared normal bits / rc_w1 register is to make a mask with all the rc_w1 register so you can keep the normal bits untouched but write 0 to all the rc_w1 bits except the one you want to write to. In this case that would be:

 

USB2_OTG_FS__HPRT = (USB2_OTG_FS__HPRT & ~(USB_OTG_HPRT_PENCHNG | USB_OTG_HPRT_PENA | USB_OTG_HPRT_PCDET | USB_OTG_HPRT_POCCHNG)) | USB_OTG_HPRT_PENCHNG;

 

I'd have deleted this 'bug report' but apparently I can't, so instead let's hope my struggles help someone else out !

View solution in original post

2 REPLIES 2
JBernard
Associate II

The lightbulb went on as I read this again and realized that |= writes 1 to all the bits that are set to 1.

That means the first write writes to PENCHNG and PENA, clears both of them, then PENCHNG goes back to 1 because PENA just went to 0.

So the correct way to go about a shared normal bits / rc_w1 register is to make a mask with all the rc_w1 register so you can keep the normal bits untouched but write 0 to all the rc_w1 bits except the one you want to write to. In this case that would be:

 

USB2_OTG_FS__HPRT = (USB2_OTG_FS__HPRT & ~(USB_OTG_HPRT_PENCHNG | USB_OTG_HPRT_PENA | USB_OTG_HPRT_PCDET | USB_OTG_HPRT_POCCHNG)) | USB_OTG_HPRT_PENCHNG;

 

I'd have deleted this 'bug report' but apparently I can't, so instead let's hope my struggles help someone else out !

Registers which are changed by both user and hardware, and are mixed rw and write-some-value-to-clear (and, as a pinnacle, the pervert "toggle" bits mixed with rw in the device-only USB) are always a bad idea, and require extra cognitive effort to handle. But it is what it is, one has to look out.

I just had a look at how do I handle this particular situation - I generally tend to read in status registers to local variable, mask with enable register and write back/write to clear register to clear the interrupt flags; and then resolve the interrupts individually according to that stored status in the local variable. A "no brainer" scheme but of course has to be used judiciously. For this particular case, I have roughly this code:

 

    uint32_t hprt = usb->c->u->HPRT;

    hprt &= ~USB_OTG_HPRT_PENA;  // PENA is rc_w1 but we don't want to clear it here

    // writeback clears all three rc_w1 pendings
    usb->c->u->HPRT = hprt;

 

Thanks for coming back with the solution. Please click on "Accept as solution" in that post so that the thread is marked as solved.

JW