2024-12-30 12:01 AM
im struggling to get usb fs working on a custom stm32f446vet dev board. im slightly aware that this may be an explored problem but in need of some guidance.
i suspect the PLLSAI never locks.
some init code for context
void USB::Init(bool softReset)
{
volatile uint32_t count = 0;
if (!softReset) {
GpioPin::Init(GPIOA, 9, GpioPin::Type::Input); // PA9: USB_OTG_HS_VBUS
GpioPin::Init(GPIOA, 11, GpioPin::Type::AlternateFunction, 10); // PA11: USB_OTG_HS_DM
GpioPin::Init(GPIOA, 12, GpioPin::Type::AlternateFunction, 10); // PA12: USB_OTG_HS_DP
RCC->DCKCFGR2 |= RCC_DCKCFGR2_CK48MSEL; // 0 = PLLQ Clock; 1 = PLLSAI_P
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; // USB OTG FS clock enable
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable system configuration clock: used to manage external interrupt line connection to GPIOs
NVIC_SetPriority(OTG_FS_IRQn, 2);
NVIC_EnableIRQ(OTG_FS_IRQn);
}
USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_PWRDWN; // Activate the transceiver in transmission/reception. When reset, the transceiver is kept in power-down. 0 = USB FS transceiver disabled; 1 = USB FS transceiver enabled
USB_OTG_FS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; // Force USB device mode
DelayMS(50);
// Clear all transmit FIFO address and data lengths - these will be set to correct values below for endpoints 0,1 and 2
for (uint8_t i = 0; i < 15; i++) {
USB_OTG_FS->DIEPTXF[i] = 0;
}
USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; // Enable HW VBUS sensing
USBx_DEVICE->DCFG |= USB_OTG_DCFG_DSPD; // 11: Full speed using internal FS PHY
USB_OTG_FS->GRSTCTL |= USB_OTG_GRSTCTL_TXFNUM_4; // Select buffers to flush. 10000: Flush all the transmit FIFOs in device or host mode
USB_OTG_FS->GRSTCTL |= USB_OTG_GRSTCTL_TXFFLSH; // Flush the TX buffers
while (++count < 100000 && (USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH));
USB_OTG_FS->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; // Flush the RX buffers
count = 0;
while (++count < 100000 && (USB_OTG_FS->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH));
USB_OTG_FS->GINTSTS = 0xBFFFFFFF; // Clear pending interrupts (except SRQINT Session request/new session detected)
// Enable interrupts
USB_OTG_FS->GINTMSK = 0; // Disable all interrupts
USB_OTG_FS->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_USBSUSPM | // Receive FIFO non-empty mask; USB suspend
USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | // USB reset; Enumeration done
USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_WUIM | // IN endpoint; OUT endpoint; Resume/remote wakeup detected
USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_OTGINT; // Session request/new session detected; OTG interrupt
// NB - FIFO Sizes are in words NOT bytes. There is a total size of 320 (320x4 = 1280 bytes) available which is divided up thus:
// FIFO Start Size
// RX 0 128
// EP0 TX 128 64
// EP1 TX 192 64
// EP2 TX 256 64
USB_OTG_FS->GRXFSIZ = 128; // Rx FIFO depth
// Endpoint 0 Transmit FIFO size/address (as in device mode - this is also used as the non-periodic transmit FIFO size in host mode)
USB_OTG_FS->DIEPTXF0_HNPTXFSIZ = (64 << USB_OTG_TX0FD_Pos) | // IN Endpoint 0 Tx FIFO depth
(128 << USB_OTG_TX0FSA_Pos); // IN Endpoint 0 FIFO transmit RAM start address - this is offset from the RX FIFO (set above to 128)
// Endpoint 1 FIFO size/address (address is offset from EP0 address+size above)
USB_OTG_FS->DIEPTXF[0] = (64 << USB_OTG_DIEPTXF_INEPTXFD_Pos) | // IN endpoint 1 Tx FIFO depth
(192 << USB_OTG_DIEPTXF_INEPTXSA_Pos); // IN endpoint 1 FIFO transmit RAM start address
// Endpoint 2 FIFO size/address (address is offset from EP1 address+size above)
USB_OTG_FS->DIEPTXF[1] = (64 << USB_OTG_DIEPTXF_INEPTXFD_Pos) | // IN endpoint 2 Tx FIFO depth
(256 << USB_OTG_DIEPTXF_INEPTXSA_Pos); // IN endpoint 2 FIFO transmit RAM start address
USBx_DEVICE->DCTL &= ~USB_OTG_DCTL_SDIS; // Activate USB
USB_OTG_FS->GAHBCFG |= USB_OTG_GAHBCFG_GINT; // Activate USB Interrupts
transmitting = false;
}
struct PLLDividers {
uint32_t M;
uint32_t N;
uint32_t P;
uint32_t Q;
};
const PLLDividers mainPLL {4, 180, 2, 7}; // Clock: 8MHz / 4(M) * 168(N) / 2(P) = 180MHz
const PLLDividers saiPLL {6, 144, 4, 0}; // USB: 8MHz / 6(M) * 144(N) / 4(P) = 48MHz
void InitClocks()
{
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
volatile uint32_t dummy = RCC->APB2ENR & RCC_APB2ENR_SYSCFGEN;
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS_0;
SCB->CPACR |= ((3 << 10 * 2) | (3 << 11 * 2));
RCC->CR |= RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) == 0);
RCC->PLLCFGR = (mainPLL.M << RCC_PLLCFGR_PLLM_Pos) |
(mainPLL.N << RCC_PLLCFGR_PLLN_Pos) |
(((mainPLL.P >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos) |
(mainPLL.Q << RCC_PLLCFGR_PLLQ_Pos) |
RCC_PLLCFGR_PLLSRC_HSE;
RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV2;
RCC->CR |= RCC_CR_PLLON;
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
RCC->PLLSAICFGR = (saiPLL.M << RCC_PLLSAICFGR_PLLSAIM_Pos) |
(saiPLL.N << RCC_PLLSAICFGR_PLLSAIN_Pos) |
(((saiPLL.P >> 1) - 1) << RCC_PLLSAICFGR_PLLSAIP_Pos);
RCC->CR |= RCC_CR_PLLSAION;
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
FLASH->ACR |= FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN;
SystemCoreClockUpdate();
}
2024-12-30 01:59 AM
Hello @pingpong999,
Could you verify that the PLLSAI is locking by checking the PLLSAIRDY flag?
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.
2024-12-30 02:31 AM
thanks for the reply.
i just ran this block and can confirm it locks which is great. im a little stumped on why im not able to register usb communication. should my d plus line be high? both d- and d+ are low
struct PLLDividers {
uint32_t M;
uint32_t N;
uint32_t P;
uint32_t Q;
};
const PLLDividers mainPLL {4, 180, 2, 7};
const PLLDividers saiPLL {6, 144, 4, 0};
void InitClocks() {
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
[[maybe_unused]] volatile uint32_t dummy = RCC->APB2ENR & RCC_APB2ENR_SYSCFGEN;
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS_0;
SCB->CPACR |= ((3 << 10 * 2) | (3 << 11 * 2));
RCC->CR |= RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) == 0);
RCC->PLLCFGR =
(mainPLL.M << RCC_PLLCFGR_PLLM_Pos) |
(mainPLL.N << RCC_PLLCFGR_PLLN_Pos) |
(((mainPLL.P >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos) |
(mainPLL.Q << RCC_PLLCFGR_PLLQ_Pos) |
RCC_PLLCFGR_PLLSRC_HSE;
RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV2;
RCC->CR |= RCC_CR_PLLON;
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
RCC->PLLSAICFGR =
(saiPLL.M << RCC_PLLSAICFGR_PLLSAIM_Pos) |
(saiPLL.N << RCC_PLLSAICFGR_PLLSAIN_Pos) |
(((saiPLL.P >> 1) - 1) << RCC_PLLSAICFGR_PLLSAIP_Pos);
RCC->CR |= RCC_CR_PLLSAION;
while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0); // Check pllsairdy here
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
FLASH->ACR |= FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN;
SystemCoreClockUpdate();
}
int main() {
RCC->CR |= RCC_CR_PLLSAION;
while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0);
printf("PLLSAI locked and ready\n");
while (1);
}
2024-12-30 09:24 AM - edited 2024-12-30 09:27 AM
Observe the USB registers in debugger, whether they do have the expected values.
> USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; // Enable HW VBUS sensing
Do you have VBUS-derived voltage connected to given pin? Is GOTGCTL.BSVLD set?
JW
2024-12-30 04:01 PM
this is my usb register dump, GOTGCTL = 0x03010000 does not have bit 19 set, so BSVLD is not set which makes me think its a hardware issue
GOTGCTL = 0x03010000
GOTGINT = 0x00100000
GAHBCFG = 0x00000001
GUSBCFG = 0x40001440
GRSTCTL = 0x80000000
GINTSTS = 0x04008020
GINTMSK = 0xC00C3814
GRXFSIZ = 0x00000080
DIEPTXF0 = 0x00400080
DIEPTXF1 = 0x004000C0
DIEPTXF2 = 0x00400100
GCCFG = 0x0021FFF0
CID = 0x00002000
DCFG = 0x08200003
DCTL = 0x00000000
DSTS = 0x0007FF06
DIEPMSK = 0x00000000
DOEPMSK = 0x00000000
DAINT = 0x00000000
DAINTMSK = 0x00000000
,
2024-12-30 11:38 PM - edited 2024-12-30 11:41 PM
> BSVLD is not set
Do you have VBUS-derived voltage connected to given pin?
You can override VBUS detection by clearing GCCFG.VBDEN and setting GOTGCTL.VBVALOEN and GOTGCTL.BVALOVAL.
JW