Josip
In order to better understand the operation I would suggest you do the following:
1. Clone the open source uTasker project from GitHub and build the KL27 target in Visual Studio (with USB_INTERACE and USE_USB_CDC enabled) - there are guides and videos showing how to do this (either with or without USB_SIMPLEX_ENDPOINTS enabled if you want to work with shared bulk endpoint to not) - eg. search uTasker on Youtube.
2. Run its KL27 simulation in Visual Studio and command USB enumeration so that the device is enumerated
3. Set a break-point in the USB-OTG interrupt handler _usb_otg_isr()
4. Use the Port Sim menu to inject bulk endpoint test frames - eg. use the simulation script "CDC_Modbus_EP1.sim" to inject some data in Modbus ASCII format (the content itself is not important)
5. Now you can analyse the reception of multiple bulk data frames to see exactly how the data toggle works in the HW (this is simulated and if you want to understand more HW details you can look in the USB simulation code (see fnSimulateUSB()) that calls the interrupt to see it) and also how the firmware needs to react to it. The reference firmware works on all Kinetis FS OTG controllers and has been proven since 2008 on Coldfires and Kinetis and so represents one of the most reliable, efficient and accurate implementations available, allowing immediate solutions or else for educational purposes and as reference for correcting alternative developments.
Basically the reception works like this (assuming you have initialised the endpoint's buffer to receive even frames to its even buffer and odd frames to its odd buffer):
- The first bulk reception that you will receive on endpoint 1 after enumeration will arrive in the buffer descriptor's "even" buffer. You will find that the STAT register doesn't have the ODD BANK flag set so you know that the data was put into the even buffer.
- your code will process the content and set the OWN flag again so that subsequent frames can be received to it. The data flag will not need to be adjusted since it is already synchronised.
- The second bulk reception that you will receive will be to the buffer descriptor's odd buffer. You will find that the STAT register has the ODD BANK flag set so you know that the data was put into the odd buffer.
- your code will process the content and set the OWN flag again so that subsequent frames can be received to it. The data flag will not need to be adjusted since it is already synchronised.
This means that for data reception on a pure bulk endpoint nothing can really go wrong if you just toggle between the two buffers (according to the STAT register flag): The USB controller will only put even frames into the even buffer and odd frames into the odd buffer (no firmware handling of errors needed).
Endpoints used for control are more complicated since the buffers are used to receive also control frames and bulk frames and need to be resynchronised after each setup reception. The even and odd buffers can then be used in an inverted sense (depending on the implementation) but a solution can be seen in the reference code based on the use of a flag called CONTROL_DATA_TOGGLE_REVERSED which indicates that the odd buffer is presently being used for even buffer reception.
Transmission is more or less the same but in the other sense, whereby the data toggling is purely under firmware control. On a purely bulk endpoint one can set up the buffer descriptor's even buffer for DATA 0 and the odd buffer descriptor for DATA 1 and just toggle between the two, whereby the first after enumeration is a DATA 0 type (send from the even buffer).
One basic thing to understand is that there are two buffers for Rx and two for Tx that are associated with each endpoint (when one endpoint is used for both IN and OUT directions). These operate in a ping-pong style so that the next data can be prepared while present data is being transmitted, or so that next data can be received while present data is being handled.
Each of these buffers can be assigned to DATA 0 or DATA 1 frames and this will already control the reception (no handling of out-of-sequence data is needed since it can't occur). The firmware's job is to ensure that the DATA flag assigned to each buffer's control word is correct so that the reception operates smoothly (pure bulk endpoints just need to be set up and that was it) and transmission toggles correctly. Data transmission is usually a little more complicated since the firmware can make mistakes in its use of the toggling which could cause the host to completely ignore data when it goes wrong.
The even and odd buffers however are not tied to data 0/data 1 - this is under control of the buffer descriptor's control word and so can be swapped (either intentionally or un-intentionally...).
Regards
Mark
[uTasker project developer for Kinetis and i.MX RT]
Developer of USB for Luminary Micro, Atmel SAM7 and AVR32, STM32, Coldfire, LPC2xxx, LPC17XX, Kinetis and i.MX RT