LINUX.ORG.RU

STM32F4 и USB

 , ,


1

3

С низкоуровневой работой с 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 МГц.

★★★★★

Последнее исправление: KivApple (всего исправлений: 1)

А ты на энумерацию правильно ответил? Там пустой IN(1) передать надо, чтоб уведомить что ты адрес принял. Если не ответишь, или ответишь STALL'ом - то хост RESET может послать, ибо решит что девайс походу не алё.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от Stanson

Речь идёт не о той енумерации, когда назначается адрес, а о той, при которой phy согласует скорость. Именно после неё должен придти set address. Это терминология из даташита и там рекомендуют активировать ep0 именно по этому событию. Проблема в том, что никакие пакеты не приходят. Я предполагаю, что что-то не так настроил.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от Stanson

Они внутри микроконтроллера. При том в режиме девайса МК умеет только full speed. Плюс у меня STM32F4-DISCOVERY - там обвязка 100% верная.

Тут именно я что-то в регистры не то записал или не в том порядке, иных вариантов нет.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Ну как бы хост детектит подключение девайса и режим работы именно по pull-up резистору. Если оно есть - хост обязан что-то плюнуть в девайс. Обычно это всяческий setup в control endpoint. Потыкайся осциллом, там вообще есть чо, на линиях D+/D-

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Осциллографа сейчас нет под рукой, однако в dmesg сыпятся сообщения, что устройство не захотело принимать адрес.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Хотя бы тестером померяй, притянут D+ или D- к +3.3V если подать питание на твою платку.

Stanson ★★★★★
()
13 марта 2016 г.

И как? Ведь можно взять работающий пример и кусочками посверять.

gag ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.