Wziąłem się za przeglądanie przykładów atmela dostarczanych z AVR32 Przykład usb_cdc. Możliwa praca albo z FREERTOS albo z poolingiem. W obu przypadkach część funkcji jest realizowana na przerwaniach. Biorę pod uwagę tylko tę cześć programu, które odpowiada wyłącznie za tryb pracy DEVICE.
Najpierw mamy wywołanie: pm_configure_usb_clock() Spoko. Uruchamia PLL1 dla USB na 48MHz.
Potem: usb_task_init(); Też w miarę OK.
- Instaluje handler przerwania.
- Wymusza tryb pracy DEVICE.
- Wywołuje usb_device_task_init(); Ten też OK.
- Uruchamia pad OTG
- Uruchamia USBE
- Odmraża zegar dla USB
- I tu uwaga! Uruchamia przerwanie USB od VBUS (kontroli napięcia zasilania na lini VBUS).
Inicjalizację mamy za sobą. Teraz w kółko wywoływana jest procedura usb_task() która w przypadku DEVICE wywołuje usb_device_task().
Co widzimy na początku usb_device_task():
if (!usb_connected && Is_usb_vbus_high()) { usb_start_device();
Czyli: usb_connected to flaga programu. Oczywiście wyzerowana. Is_usb_vbus_high() natomiast robi pooling USBSTA bitu VBUS. Czyli usb_start_device() zostanie wywołane w momencie podania napięcia na VBUS.
I byłoby OK, ale zajrzyjmy do procedury obsługi przerwań od USB, czyli usb_general_interrupt(void):
if (Is_usb_vbus_transition() && Is_usb_vbus_interrupt_enabled()) { Usb_ack_vbus_transition(); if (Is_usb_vbus_high()) { usb_start_device();
Tutaj w przerwaniu mamy dokładnie to samo w pętli głównej, tyle że na przerwaniach. W momencie przyjścia napięcia na VBUS zostaje wywołana ta sama procedura co w programie głównym. Co będzie jeśli przerwanie przyjdzie wtedy, kiedy główna procedura będzie odczytywała stan bitu VBUS w USBSTA? Zostanie dwukrotnie wywołane usb_start_device(). Mało prawdopodobna sytuacja, ale możlwia. I co wtedy? Będziemy mieć np. podwójne zaalokowanie pamięci dla endpoint 0. Bardzo wredny błąd, bo ciężki do wychwycenia.
Dlatego chciałem napisać własną obsługę USB CDC niż korzystać z wątpliwych gotowców atmela, tyle że procek nie bardzo chce pracować wg tego co piszą w PDFie.
Robię tak:
// usbb base
mov.w r12, USBB_BASE
// disable usb, freeze clock, force device mode
mov.w r0, (1 << USBCON_FRZCLK) | (1 << USBCON_OTGPADE) | (1 << USBCON_UIMOD) | (1 << USBCON_VBUSHWC) st.w r12[USBB_USBCON], r0
// enable usb, unfreeze clock, force device mode
mov.w r0, (1 << USBCON_USBE) | (1 << USBCON_OTGPADE) | (1 << USBCON_UIMOD) | (1 << USBCON_VBUSHWC) st.w r12[USBB_USBCON], r0
// enable endpoint
mov r0, (1 << UERST_EPEN0) st.w r12[USBB_UERST], r0
// configure endpoint
mov.w r0, (3 << UECFG_EPSIZE) | (1 << UECFG_ALLOC) st.w r12[USBB_UECFG0], r0
// attach
mov r0, 0 st.w r12[USBB_UDCON], r0
I lipa. Attach nie działa. Żadna z linii D+/D- nie zostaje podciągnięta.
SM