Closed EmbeddedMagic closed 2 years ago
Hi,
Despite the limited help and example code, this is a very good USB solution that is small. Some of the other so called small USB stacks are completely out of control in size and complexity.
I never used CDC but perhaps I should play with it. I went immediately to "raw" HID, but maybe some of this will be helpful?
For reception I take the data from OUT endpoint and enqueue it in a buffer that is later examined by the main application. My protocol (packet) has a length byte at position [0] and then the rest of the packet contains that amount of data (0 to endpointsize-1). This is the job of UnLoadEp1OutFifo(). It does nothing else. After the ISR is complete, the host may again send new data.
For transmission, when the host has taken the data we get the interrupt again and I set the flag. The endpoint will NAK until I give it more data. See the second code block.
/*
.------------------------------------------------------------------------------
| void USBD_Ep1EventCb(usbd_device *dev, uint8_t event, uint8_t ep)
|
| Endpoint1 IN and/or OUT callback (endpoint transfer complete).
|
| Generic USB device event callback for events and endpoints processing.
| Endpoints with SAME indexes i.e. 0x01 and 0x81 share the SAME callback.
| (invoked in function usbd_process_evt() in usbd_core.c, line 367.
|
| Registration of dev->endpoint[] callback: (except endpoint 0)
| usbd_reg_endpoint(dev, HID_RPT_EP, USBD_Ep1EventCb);
`------------------------------------------------------------------------------
*/
void USBD_Ep1EventCb(usbd_device *dev, uint8_t event, uint8_t ep)
{
switch(event)
{
case usbd_evt_sof: // We never hit this with a breakpoint.
break; // Not implemented by the core.
case usbd_evt_eptx: // 4 = Data packet transmitted.
if(ep == IN_EP1)
{
if(bLoadEp1 == 0) // Tell main code that we are ready for
bLoadEp1 = 1; // more In_Packet[] data.
}
break;
case usbd_evt_eprx: // 5 = Data packet received.
if(ep == OUT_EP1)
{
usbd_ep_read(dev, ep, Out_Packet, EP1_PACKET_SIZE);
if(Out_Packet[0])
UnLoadEp1OutFifo();
}
break;
default: // We never hit this with a breakpoint.
break;
}
}
Main level code for sending
// Load up our data for the host.
if(bLoadEp1)
{
// Returns 0 if data is placed into the In_Packet[]. If we have no data then returns 1.
if(LoadEp1InFifo() == 0)
{
// If there is data to move, clear the flag, else try again next pass.
bLoadEp1 = 0;
usbd_ep_write(&udev, BL_IN_EP, In_Packet, EP1_PACKET_SIZE);
}
}
The other difficulty is making sure the endpoints are started correctly.
/*
.------------------------------------------------------------------------------
| usbd_respond USBD_SetConfigCb(usbd_device *dev, uint8_t cfg)
|
| Set Configuration callback.
|
| Callback for SET_CONFIG control request
| (invoked in function usbd_configure() in usbd_core.c, line 66.
|
| Registration of dev->config_callback:
| usbd_reg_config(&udev, USBD_SetConfigCb); // was cdc_setconf
`------------------------------------------------------------------------------
*/
usbd_respond USBD_SetConfigCb(usbd_device *dev, uint8_t cfg)
{
UsbDevConfig = cfg; // dev->status.device_state
switch(cfg)
{
case 0: /* deconfiguring device */
usbd_ep_deconfig(dev, BL_IN_EP);
usbd_ep_deconfig(dev, BL_OUT_EP);
usbd_reg_endpoint(dev, BL_IN_EP, 0); // IN/OUT on same index share one callback.
usbd_reg_endpoint(dev, BL_OUT_EP, 0); // IN/OUT on same index share one callback.
return usbd_ack;
case 1: /* configuring device */
usbd_ep_config(dev, BL_IN_EP, USB_EPTYPE_INTERRUPT, EP1_PACKET_SIZE);
usbd_ep_config(dev, BL_OUT_EP, USB_EPTYPE_INTERRUPT, EP2_PACKET_SIZE);
usbd_reg_endpoint(dev, BL_IN_EP, USBD_Ep1EventCb); // IN/OUT on same index share one callback.
usbd_reg_endpoint(dev, BL_OUT_EP, USBD_Ep1EventCb);
usbd_ep_write(dev, BL_IN_EP, 0, 0); // An ACK with no data. <---Do ONLY ONE of these!---.
// bLoadEp1 = 1; // Arm ability to load regular tx data. <------------'
return usbd_ack; // Then will be NAKing until data.
default:
return usbd_fail;
}
}
Thanks GrantMTG, this was eye opening! It also answersmy other questions how to get informed about other events like wakeup, suspend, ...didn't see that. Hope they are implemented, but I will find out. Yes this library is well made and works out of the box in Windows, too. A tiny API description would be anyway for the impatient very nice :-) I am not able to do that right now...maybe later.
Hi, guys. Sorry for the late reply. Some sort of hell goes around me. Anyway, I will try to explain to you how it works.
void usbd_poll(usbd_device *dev)
is the main entry point of this stack. It can be used both inside the main loop and in the USB interrupt handler. Depending on the internal state it will issue callbacks. Most of the common things described in the CH9 are implemented in the core. There are two types of common typedef void (*usbd_evt_callback)(usbd_device *dev, uint8_t event, uint8_t ep)
callbacks: general events (reset, suspend, SOF, TX completed, RX completed) and endpoint events (TX completed, RX completed, setup packet received). Some of them may be issued twice as general and as an endpoint. Also, there are special callbacks issued by the core, usbd_respond (*usbd_ctl_callback)(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
will be issued when the control packet will be completely received by EP0 (mostly used for the class-specific control requests) If the return code from this callback is usbd_fail
the core continue processing it in a standard way, otherwise answer or data will be sent to host without any processing and after that usbd_rqc_callback
will be issued. The usbd_dsc_callback
and usbd_cfg_callback
was designed to help to serve GET_DESCRIPTOR and SET_CONFIGURATION control requests.
Of course, you can read or write endpoint outside callbacks but make sure that the device is in the configured state.
There are some other limitations.
PS. The core is thread-safe and can serve as many USB controllers as you have.
Hi, I was successfully trying out the cdc_loopback(), works really nice. I enabled interrupt mode. My USB knowledge is very limited, therefore I would like to explain what I want to achieve:
int writeUsb(uint8 txBuffer[], uint8 len) { return usbd_ep_write(&udev, CDC_TXD_EP, &txBuffer[0], len); }
uint8_t _t = dev->driver->frame_no(); memset(fifo, _t, CDC_DATA_SZ);