STM32 study notes USB data receiving and sending process analysis

Publisher:lqs1975Latest update time:2018-09-20 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Now that you have learned USB, you must understand how USB devices communicate with USB host data. Here we mainly talk about the device side, because our code is used for USB devices.

We need to define the USB interrupt. First, in the interrupt vector table of STM32, two interrupts are given to USB. We can find these two interrupts in stm32f10x.h:


USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */

These two interrupts are multiplexed interrupts for USB and CAN. When used for USB, they represent the high-priority and low-priority interrupts of the USB device. In my project, I chose to use the low-priority USB interrupt. The code is as follows:


void USB_LP_CAN1_RX0_IRQHandler(void) { USB_Istr(); }

The interrupt service program is very simple, which is to call the USB_istr() function when an interrupt occurs. We have mentioned the USB_istr() function before, which is defined in usb_istr.c. This function handles the interrupts defined in the ISTR interrupt status register, including: CTR correct transmission interrupt, RESET reset interrupt, DOVR packet buffer overflow interrupt, ERR error interrupt, WAKEUP interrupt, SUSP suspend interrupt, SOF frame start interrupt, ESOF expected frame start interrupt. The focus here is the CTR interrupt. After the USB correctly sends or receives data, the USB module automatically returns the position of the ISTR register to 1, triggering the CTR interrupt. The CTR processing code in USB_istr() is as follows:


#if (IMR_MSK & ISTR_CTR) //Correct transmission interrupt CTR flag if (wIstr & ISTR_CTR & wInterrupt_Mask) //The interrupt flag read out is the CRT interrupt flag, and the CRT interrupt is enabled { CTR_LP(); //Call the correct transmission interrupt service routine #ifdef CTR_CALLBACK CTR_Callback(); //When CTR_CALLBACK is defined, CTR_Callback is called, like a hook function, to do something when a CRT interrupt occurs #endif }

First, let's explain the sentence #if (IMR_MSK & ISTR_CTR).

#define IMR_MSK (CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM | CNTR_SOFM \ | CNTR_ESOFM | CNTR_RESETM )

This is the definition of IMR_MSK, which means a mask containing all interrupts. IMR_MSK & ISTR_CTR means: if ISTR_CTR is the specified interrupt category, compile the code between #if and #endif. Obviously, it complies here. Then, it is determined that the interrupt value read from the CNTR register is a CRT interrupt, and the interrupt has been enabled in CNTR. Then the CTR_LP() function is called for processing. If CTR_CALLBACK is defined, the CTR_Callback() function is called. This function is a hook function that allows the user to do something after receiving the data correctly, such as turning on the light or printing some messages through the serial port.

Here we need to analyze the CTR_LP() function defined in usb_int.c. The code is as follows:


/******************************************************************************* * Function Name : CTR_LP. * Description : Low priority endpoint correct transmission interrupt service routine * Input : None. * Output : None. * Return : None. *******************************************************************************/ void CTR_LP(void) { __IO uint16_t wEPVal = 0; while (((wIstr = _GetISTR()) & ISTR_CTR) != 0) //Read the value of the interrupt status register to see if it is CRT (correct transmission interrupt) { EPindex = (uint8_t)(wIstr & ISTR_EP_ID); //Get the endpoint number that generated the interrupt, if (EPindex == 0) //If endpoint 0 { SaveRState = _GetENDPOINT(ENDP0); //Read the status register of endpoint 0 SaveTState = SaveRState & EPTX_STAT; //Save endpoint 0 send status SaveRState &= EPRX_STAT; //Save endpoint 0 receive status _SetEPRxTxStatus(ENDP0,EP_RX_NAK,EP_TX_NAK); //Set endpoint 0 to respond to all receive and send requests from the host in NAK mode if ((wIstr & ISTR_DIR) == 0) //If it is an IN token { _ClearEP_CTR_TX(ENDP0); //Clear endpoint 0 correct send flag In0_Process(); //Process IN token packet/* before terminate set Tx & Rx status */ _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//Set endpoint 0 receive and send status before transmission return; } else //OUT token { wEPVal = _GetENDPOINT(ENDP0); //Get the value of the endpoint register of endpoint 0 if ((wEPVal &EP_SETUP) != 0) //SETUP packet transmission completion flag { _ClearEP_CTR_RX(ENDP0); //Clear endpoint 0 receive flag Setup0_Process(); //Data processing in endpoint 0 setup phase _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//Set endpoint 0 receive and send flag return; } else if ((wEPVal & EP_CTR_RX) != 0) //Correct receive flag { _ClearEP_CTR_RX(ENDP0); //Clear endpoint 0 correct flag Out0_Process(); //Process OUT token packet _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//Set endpoint 0 receive and send status return; } } }/* if(EPindex == 0) */ else //If non-zero endpoint { wEPVal = _GetENDPOINT(EPindex); //Get the value of the endpoint register of the endpoint if ((wEPVal & EP_CTR_RX) != 0) //Correct receive flag { _ClearEP_CTR_RX(EPindex); //Clear endpoint correct reception flag (*pEpInt_OUT[EPindex-1])(); //Call registered endpoint OUT processing function } /* if((wEPVal & EP_CTR_RX) */ if ((wEPVal & EP_CTR_TX) != 0) //Correct send flag { _ClearEP_CTR_TX(EPindex); //Clear correct send flag (*pEpInt_IN[EPindex-1])(); //Call registered endpoint IN processing function } /* if((wEPVal & EP_CTR_TX) != 0) */ }/* if(EPindex == 0) else */ }/* while(...) */ }

This function will first determine whether there is a real CTR interrupt. If so, it will execute the code in while() and use EPindex to save the endpoint number that generated the interrupt. If EPindex is 0, it means that the interrupt was generated by endpoint 0, indicating that the USB is still in the enumeration stage. If EPindex is not 0, it means that the enumeration has been successful and the USB is in normal working condition.

In the enumeration phase, SaveRState saves the value of endpoint 0 register, followed by SaveTState = SaveRState & EPTX_STAT; and SaveRState &= EPRX_STAT;. SaveTState saves the current state of send endpoint 0, and SaveRState saves the current state of receive endpoint. Then set receive endpoint 0 to NAK state, and send endpoint 0 to NAK state, that is, when the host sends any data, the slave only responds with NAK, and the slave can only send NAK data, that is, data communication is not allowed in the data processing phase. Then determine whether it is input or output. If it is input (note that the input here is relative to the host), clear the EP_CTR_TX flag of the endpoint register, and call the IN token packet processing function In0_Process() (defined in usb_core.c). If it is output (note that the output here is relative to the host), it is also necessary to determine whether the received packet is a SETUP packet or an OUT token packet. If it is a SETUP packet, clear the EP_SETUP bit of the endpoint 0 register, call the SETUP processing function Setup0_Process(), and restore the original state of the receiving and sending endpoints to prepare for the next interrupt processing. If it is an OUT token packet, clear the EP_CRT_RX bit of the endpoint 0 register, call the OUT processing function Out0_Process(), and restore the original state of the connection port to prepare for the next interrupt processing.

In the working phase or the non-enumeration phase, we must first determine whether it is the EP_CTR_RX or EP_CTR_TX flag. If it is the EP_CTR_RX correct reception flag, clear the flag and call the OUT processing function of the corresponding endpoint (*pEpInt_OUT[EPindex-1])() (registered in usb_istr). If it is the EP_CTR_TX flag, clear the flag and call the IN processing function of the corresponding endpoint (*pEpInt_IN[EPindex-1])() (registered in usb_istr).

In usb_istr.c, 7 endpoint input functions and endpoint output functions are registered separately. They are as follows:


/*Define an array of function pointers pointing to pointers. The function pointers point to the 7 endpoint input service programs respectively*/ void (*pEpInt_IN[7])(void) = { EP1_IN_Callback, EP2_IN_Callback, EP3_IN_Callback, EP4_IN_Callback, EP5_IN_Callback, EP6_IN_Callback, EP7_IN_Callback, }; /*Define an array of function pointers pointing to pointers. The function pointers point to the 7 endpoint output service programs respectively*/ void (*pEpInt_OUT[7])(void) = { EP1_OUT_Callback, EP2_OUT_Callback, EP3_OUT_Callback, EP4_OUT_Callback, EP5_OUT_Callback, EP6_OUT_Callback, EP7_OUT_Callback, };

The definitions of these functions are in usb_endp.c. Let’s analyze the EP1_OUT_Callback() function.


/******************************************************************************* * Function Name : EP1_OUT_Callback. * Description : Endpoint 1 output callback function * Input : None. * Output : None. * Return : None. *******************************************************************************/ void EP1_OUT_Callback(void) { PMAToUserBufferCopy(USB_Receive_Buffer, ENDP1_RXADDR, REPORT_COUNT); //Copy the data received by the PMA buffer to the user-defined buffer USB_Receive_Buffer SetEPRxStatus(ENDP1, EP_RX_VALID); //Set the endpoint's receive status to valid, because the endpoint will automatically set the endpoint status to the stop state after receiving the data USB_Received_Flag=1; //Set the received data flag }

The work of this function is very simple. First, because the digital output endpoint is used to receive data, and the data received by the USB module is temporarily stored in the PAM double buffer, the data must be read from the PMA and placed in the user's own buffer. Then set the endpoint receiving state to be valid, because after receiving data, the endpoint will be closed. Finally, set the receive with data flag.

The above is the receiving process of the USB device. Next, let's talk about the sending process. Sending is much simpler than receiving. Just look at the following code to see it.


/** * @brief Send data via USB * @param data Data storage first address * @param dataNum Number of data bytes to be sent * @retval Number of bytes to be sent */ uint32_t USB_SendData(uint8_t *data,uint32_t dataNum) { //Send data via USB UserToPMABufferCopy(data, ENDP2_TXADDR, dataNum); //Copy data to PMA SetEPTxCount(ENDP2, REPORT_COUNT); //Send 64 bytes of data from endpoint 2 SetEPTxValid(ENDP2); //Enable the send status of endpoint 2 return dataNum; }

Copy the data to be sent to PMA, then set the endpoint count, enable the endpoint, and the data will be sent out.

To summarize:

Data sending: UserToPMABufferCopy--->SetEPTxCount--->SetEPTxValid

Data reception: USB_LP_CAN1_RX0_IRQHandler--->USB_Istr---->CTR_LP--->EPx_OUT_Callback


Keywords:STM32 Reference address:STM32 study notes USB data receiving and sending process analysis

Previous article:STM32CubeMX tutorial introduction and basic use
Next article:STM32 routine USB HID bidirectional data transmission

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号