2012-06-01 06:43 AM
Hello,
I am working on virtual serial port to communicate between computer and device through usb. So i downloaded ''STM32F105/7,STM32F2 and STM32F4 USB on-the-go Host and device library.'' from http://www.st.com/stonline/stappl/resourceSelector/app?page=resourceSelector&doctype=FIRMWARE&ClassID=1734 And i selected vcp from project lists, and start reading the code. I found that the main application only contains initialization of the hardware and the interrupts. So the code that is sending data is working in the interrupt, but i cannot find the portion that is responsible to send data... Please if any one can explain me the flow of the program. Note that i read the manuals from ST concerning this project but i didn't get the point, because they wrote 2 papers only about this application. Regards, Hisham2012-06-01 07:35 AM
I work with STM32F103RE. IAR EW+FreeRTOS environment. The device communicates with PC using Virtual Com Port.
Since USB data pins, CAN are multiplexed, for communication we use CAN interrupt event.void
USB_Interrupts_Config(
void
)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
Below is the code for the interrupt handler. FreeRTOS related code is inside written inside handler.
//This function handles USB Low Priority or CAN RX0 interrupts requests.
void
USB_LP_CAN1_RX0_IRQHandler(
void
)
{
#ifdef _VCOM
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
USB_Istr();
// Responsible for reading data (usb_istr.c)
if
(count_out) {
xQueueSendFromISR(s_usbQueue, &count_out, &xHigherPriorityTaskWoken);
count_out=0;
}
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
From the above function, briefly you can understand how to get read the data.
Below is the function to write the data to Virtual Com buffer in order to send to PC.void
Virtual_Com_Write_Buffer(u8* data_buffer, u8 Nb_bytes)
{
/* The USB cable is plugged into the USB-Mini connector */
u8 b_size = Nb_bytes;
u8 b_index = 0;
u8 actual_input_size;
while
(b_index < Nb_bytes)
{
if
(b_size < VIRTUAL_COM_PORT_DATA_SIZE)
{
actual_input_size = b_size;
}
else
{
actual_input_size = VIRTUAL_COM_PORT_DATA_SIZE;
}
/* wait until the data transmission is finished */
while
(GetEPTxStatus(ENDP1) == EP_TX_VALID);
//0x30
/* Write data to the virtual UART through USB port */
UserToPMABufferCopy(&(data_buffer[b_index]), ENDP1_TXADDR,actual_input_size);
SetEPTxCount(ENDP1,actual_input_size);
SetEPTxValid(ENDP1);
b_size = b_size - actual_input_size;
b_index= b_index + actual_input_size;
}
}
Hope you got an idea how the communication goes between Device and PC.
2012-06-01 11:54 AM
Hi Harinath,
Is your Virtual_Com_Write_Buffer() a simplified one?This routine doesn't append ZLP (Zero-Length Packet), when the last transaction goes in full-size packet (usually 64 bytes, equals to wMaxPacketSize of the endpoint).For example, when just 64 bytes (or 128, 192, ...) is sent over your Virtual_Com_Write_Buffer(), the transactions should complete on the device, but it doesn't appear on the PC application. It's because PC host controller still waiting for next transaction. To terminate the transfer, send ZLP after the last full-size packet.Tsuneo2012-06-01 06:56 PM
Find the firmware source here:
It has complete firmware for the iNEMO v2 board based on STM32.2012-06-02 12:14 PM
2012-06-02 12:45 PM
Harinath,
You are referring to this source of virtual_com iNEMO GUI and firmware http://www.st.com/internet/com/SOFTWARE_RESOURCES/SW_COMPONENT/SW_FUNCTION/inemo_gui_2_0_0.zip C:\Program Files\STMicroelectronics\iNEMO Suite\Firmware\iNEMO_Project\iNEMO_Project\virtual_com\src\virtual_com.c At quick glance of this source files, this application likely sends less than 64 bytes to host. And then, the author wasn't aware of above flaw. As of STM32_USB-Host-Device_Lib_V2.1.0, On the UART side, at UART RX interrupt (EVAL_COM_IRQHandler()), VCP_DataTx() is called. In this routine, received data is stacked on APP_Rx_Buffer[] On the USB side, (STM32_USB-Host-Device_Lib_V2.1.0\Libraries\STM32_USB_Device_Library\Class\cdc\src\usbd_cdc_core.c) at SOF interrupt (usbd_cdc_SOF()), Handle_USBAsynchXfer() is called at every CDC_IN_FRAME_INTERVAL. In this routine, the first packet is passed from APP_Rx_Buffer[] to the bulk IN endpoint, calling DCD_EP_Tx(). At the completion of the IN transaction, endpoint interrupt fires. The stack calls usbd_cdc_DataIn() for next packet. This routine puts the next packet calling DCD_EP_Tx(), if any. In this way, usbd_cdc_DataIn() is called until all data on APP_Rx_Buffer[] is sent out. This implementation doesn't count in ZLP for the last full-size packet, too. CDC_IN_FRAME_INTERVAL seems to be for a latency timer, but wrongly implemented. A latency timer is coded in this way. 1) The USB side waits until 64 bytes or more data stacks on a buffer. This condition is polled at both of SOF and endpoint interrupt. When enough data stacks, a full-size packet is sent over the bulk IN endpoint. 2) When specified interval (latency timer) is expired after the last UART RX, all of data on the buffer is sent out to host, even if it is less than full-size. OR 2') When specified interval is expired after the last bulk IN transaction, ditto. Anyway, the concept of transfer split to transactions, and transfer termination, are the first hurdle to learn USB. I've seen many ones have fallen in this pitfall. I explained on this issue in this post. Hope this help you. http://www.cygnal.org/ubb/Forum9/HTML/001627.html Tsuneo2012-06-02 01:17 PM
Hi Hugh,
> Is that requirement specific to VCOM, or general to ''IN'' packets? It's general to IN transfers. As of PC CDC driver (Windows, Linux and Mac), the driver requests 4K Bytes bulk IN transfer to PC host controller (HC). HC splits this request into IN transactions to the CDC device. Until the device terminates this transfer by a short transaction (less than 64 bytes, including ZLP), HC doesn't complete the transfer. Therefore, PC CDC driver couldn't receive any data from HC, if the CDC device wouldn't send short transaction (packet). Tsuneo2012-06-04 03:37 AM
Hello, Thanks you all for your support.
I want to share my conclusion about the code. First USB_OTG_BSP_EnableInterrupt function enables these interrupts OTG_HS_EP1_OUT_IQRHandler, and OTG_HS_EP1_IN_IRQHandler from usb_bsp.c source code. Where OTG_HS_EP1_IN_IRQHandler handles data recieved over USB, and OTG_HS_EP1_IN_IRQHandler handles data sent over USB as follows.
#ifdef USB_OTG_HS_DEDICATED_EP1_ENABLED
/**
* @brief This function handles EP1_IN Handler.
* @param None
* @retval None
*/
void OTG_HS_EP1_IN_IRQHandler(void)
{
USBD_OTG_EP1IN_ISR_Handler (&USB_OTG_dev);
}
/**
* @brief This function handles EP1_OUT Handler.
* @param None
* @retval None
*/
void OTG_HS_EP1_OUT_IRQHandler(void)
{
USBD_OTG_EP1OUT_ISR_Handler (&USB_OTG_dev);
}
#endif
I will start with the flow of
OTG_HS_EP1_OUT_IQRHandler.
1- Calls
USBD_OTG_EP1OUT_ISR_Handler
that is defined in file:
usb_dcd_int.c
2-
USBD_OTG_EP1OUT_ISR_Handler
calls
DataOutStage that is defined in file:
usbd_core.c. 3-
DataOutStage calls
DataRx that is defined in file:
usbd_cdc_vcp.c.
4-
DataRx function is used to proccess data recieved, in this case it sends data to serial port.
So if we want to modify the way we want to process data received we just have to modify
DataRx function.
Now i will explain the flow of
OTG_HS_EP1_IN_IQRHandler.
1- Calls
USBD_OTG_EP1IN_ISR_Handler
which is defined in
usb_dcd_int.c.
2-
USBD_OTG_EP1IN_ISR_Handler
Calls
DataInStage function which is defined
usbd_core.c.
3-
DataInStage Calls
DataIn function which is defined in
usbd_cd_core.c.
4-
DataIn Calls
DCD_EP_TX function which is defined in
usb_dcd.c. And the last function is responsible to send data through usb.
In this example which is VCP, what happens is the following:
When data is received over serial port an interrupt is generated and the function
DataTx is fired, which is defined in
usbd_cdc_vcp.c. This function takes the data received to serial port, and put them is transmit buffer, which is then send over usb when OTG_HS_EP1_IQRHandler interrupt is fired.
Please if am wrong with something inform me.
Regards,
Hisham
2012-06-04 01:05 PM
> and put them is transmit buffer, which is then send over usb when OTG_HS_EP1_IQRHandler interrupt is fired.
Please note, outside of endpoint ISR (USBD_OTG_EP1OUT_ISR_Handler()), the first packet is put to the USB IN endpoint by DCD_EP_Tx(). In this example, its Handle_USBAsynchXfer(), called by SOF interrupt. The endpoint ISR is called just after an IN transaction completes successfully. And then, the endpoint ISR puts the second and latter packets to the IN endpoint.The IN endpoint interrupt doesn't occur, unless the IN endpoint is filled outside of its ISR. Many ones have often asked here, why doesn't IN transfer start. Naturally it's because they modify just endpoint ISR.Tsuneo2012-06-06 12:21 AM
Sorry tsuneo but i didn't get your point, can you share your ideas on the code please.
And yes you are right, i don't know when IN endpoint interrupt occur, so in case i want to write data from usb to pc i should fire this interrupt right?