С низкоуровневой работой с USB на STM32F103 я разобрался, теперь очередь USB OTG, который присутствует на более жирных МК (например, STM32F407, который я и использую для всех тестов). Для начала мне вполне хватит device-only mode.
Инициализирую USB я так:
usb->globalRegs->GINTSTS = 0xFFFFFFFF;
usb->globalRegs->GUSBCFG |= OTG_FS_GLOBAL_FS_GUSBCFG_PHYSEL;
usb->globalRegs->GCCFG |= OTG_FS_GLOBAL_FS_GCCFG_PWRDWN | OTG_FS_GLOBAL_FS_GCCFG_VBUSBSEN;
while ((usb->globalRegs->GRSTCTL & OTG_FS_GLOBAL_FS_GRSTCTL_AHBIDL) == 0);
usb->globalRegs->GRSTCTL |= OTG_FS_GLOBAL_FS_GRSTCTL_CSRST;
while (usb->globalRegs->GRSTCTL & OTG_FS_GLOBAL_FS_GRSTCTL_CSRST);
uint32_t ahbFreq = stm32_rccAHBFrequency();
uint32_t trdt;
if (ahbFreq <= 15000000) {
trdt = 0xF;
} else if (ahbFreq <= 16000000) {
trdt = 0xE;
} else if (ahbFreq <= 17200000) {
trdt = 0xD;
} else if (ahbFreq <= 18500000) {
trdt = 0xC;
} else if (ahbFreq <= 20000000) {
trdt = 0xB;
} else if (ahbFreq <= 21800000) {
trdt = 0xA;
} else if (ahbFreq <= 24000000) {
trdt = 0x9;
} else if (ahbFreq <= 27500000) {
trdt = 0x8;
} else if (ahbFreq <= 32000000) {
trdt = 0x7;
} else {
trdt = 0x6;
}
usb->globalRegs->GUSBCFG = (usb->globalRegs->GUSBCFG & ~OTG_FS_GLOBAL_FS_GUSBCFG_TRDT_MASK) | OTG_FS_GLOBAL_FS_GUSBCFG_FDMOD |
(trdt << OTG_FS_GLOBAL_FS_GUSBCFG_TRDT_OFFSET);
usb->deviceRegs->DCFG |= 3 << OTG_FS_DEVICE_FS_DCFG_DSPD_OFFSET;
usb->powerRegs->PCGCCTL = 0;
usb->globalRegs->GAHBCFG |= OTG_FS_GLOBAL_FS_GAHBCFG_GINT;
usb->globalRegs->GINTMSK = OTG_FS_GLOBAL_FS_GINTMSK_ENUMDNEM;
nvicEnableIRQ(OTG_FS_IRQ);
nvicEnableIRQ(OTG_FS_WKUP_IRQ);
Жду прерывание окончания энумерации. Оно приходит при втыкании кабеля USB. При приходе прерывания делаю вот такие вещи:
static void STM32USBDriverClass_resetFIFO(STM32USBDriverClass *usb) {
usb->fifoTop = 0;
}
static uint16_t STM32USBDriverClass_allocFIFO(STM32USBDriverClass *usb, uint16_t count) {
uint16_t r = usb->fifoTop;
usb->fifoTop += count;
return r;
}
...
int i;
for (i = 0; i < STM32USB_MAX_EP_COUNT; i++) {
usb->deviceRegs->DIEP[i].CTL = OTG_FS_DEVICE_FS_DIEPCTL0_SNAK;
usb->deviceRegs->DOEP[i].CTL = OTG_FS_DEVICE_DOEPCTL0_SNAK;
usb->deviceRegs->DIEP[i].INT = 0xFF;
usb->deviceRegs->DOEP[i].INT = 0xFF;
usb->epInfo[i].inCallback = NULL;
usb->epInfo[i].outCallback = NULL;
}
usb->deviceRegs->DAINT = 0xFFFFFFFF;
usb->deviceRegs->DAINTMSK = 0;
usb->globalRegs->GINTSTS = 0xFFFFFFFF;
STM32USBDriverClass_resetFIFO(usb);
STM32USBDriverClass_allocFIFO(usb, 128);
usb->globalRegs->GRXFSIZ = 128;
usb->globalRegs->GINTMSK = OTG_FS_GLOBAL_FS_GINTMSK_ENUMDNEM | OTG_FS_GLOBAL_FS_GINTMSK_RXFLVLM | OTG_FS_GLOBAL_FS_GINTMSK_IEPINT |
OTG_FS_GLOBAL_FS_GINTMSK_OEPINT | OTG_FS_GLOBAL_FS_GINTMSK_USBSUSPM;
usb->deviceRegs->DIEPMSK = OTG_FS_DEVICE_FS_DIEPMSK_XFRCM;
usb->deviceRegs->DOEPMSK = OTG_FS_DEVICE_FS_DOEPMSK_XFRCM | OTG_FS_DEVICE_FS_DOEPMSK_STUPM;
usb->deviceRegs->DCFG = (usb->deviceRegs->DCFG & ~OTG_FS_DEVICE_FS_DCFG_DAD_MASK) | (0 << OTG_FS_DEVICE_FS_DCFG_DAD_OFFSET);
...
// bufSize == 64
bufSize = (bufSize + 3) & ~3;
uint32_t mpsiz;
if (bufSize >= 64) {
mpsiz = 0;
} else if (bufSize >= 32) {
mpsiz = 1;
} else if (bufSize >= 16) {
mpsiz = 2;
} else {
mpsiz = 3;
}
usb->deviceRegs->DOEP[0].SIZE = 0;
usb->deviceRegs->DOEP[0].CTL = mpsiz << OTG_FS_DEVICE_DOEPCTL0_MPSIZ_OFFSET;
usb->deviceRegs->DIEP[0].SIZE = 0;
usb->deviceRegs->DIEP[0].CTL = (mpsiz << OTG_FS_DEVICE_FS_DIEPCTL0_MPSIZ_OFFSET) | (0 << OTG_FS_DEVICE_FS_DIEPCTL0_TXFNUM_OFFSET);
usb->globalRegs->HPTXFSIZ = (STM32USBDriverClass_allocFIFO(usb, bufSize / 4) << OTG_FS_GLOBAL_FS_HPTXFSIZ_PTXSA_OFFSET) |
((bufSize / 4) << OTG_FS_GLOBAL_FS_HPTXFSIZ_PTXFSIZ_OFFSET);
usb->epInfo[ep].rxBufSize = (3 << OTG_FS_DEVICE_DOEPTSIZ0_STUPCNT_OFFSET) | OTG_FS_DEVICE_DOEPTSIZ0_PKTCNT | bufSize;
usb->epInfo[ep].txBufSize = bufSize;
usb->epInfo[ep].outCallback = callback;
usb->epInfo[ep].outCallbackArg = arg;
usb->epInfo[ep].inCallback = callback;
usb->epInfo[ep].inCallbackArg = arg;
usb->deviceRegs->DAINTMSK |= (1 << OTG_FS_DEVICE_FS_DAINTMSK_IEPM_OFFSET) | (1 << OTG_FS_DEVICE_FS_DAINTMSK_OEPINT_OFFSET);
...
// ep == 0, dir == 0
volatile STM32OTGEpRegs *epRegs = (dir ? usb->deviceRegs->DIEP : usb->deviceRegs->DOEP) + ep;
epRegs->SIZE = usb->epInfo[ep].rxBufSize;
epRegs->CTL |= OTG_FS_DEVICE_FS_DIEPCTL0_EPENA | OTG_FS_DEVICE_FS_DIEPCTL0_CNAK;
На этом инициализация оканчивается. Я ожидаю получить прерывание по причине RXFLVL, что будет значить приход первого SETUP-пакета, однако ничего не получаю. Через какое-то время приходит SUSPEND, а затем новый RESET. И так повторяется несколько раз, пока хосту не надоест долбить устройство.
Что я делаю не так в настройке EP0, что устройство не получает ни одного пакета (никаких прерываний об ошибках тоже не приходит)? Проверял адреса полей всех своих структур, описывающих регистры USB OTG - с даташитом совпадают.
С частотами вроде всё тоже ок. При настройке коэффициентов PLL использую те же, что и libopencm3 (там USB работает с такими настройками) для случая кварца 8 МГц и выходной частоте 168 МГц.