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
Previous article:STM32CubeMX tutorial introduction and basic use
Next article:STM32 routine USB HID bidirectional data transmission
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- CGD and Qorvo to jointly revolutionize motor control solutions
- CGD and Qorvo to jointly revolutionize motor control solutions
- Keysight Technologies FieldFox handheld analyzer with VDI spread spectrum module to achieve millimeter wave analysis function
- Infineon's PASCO2V15 XENSIV PAS CO2 5V Sensor Now Available at Mouser for Accurate CO2 Level Measurement
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- A new chapter in Great Wall Motors R&D: solid-state battery technology leads the future
- Naxin Micro provides full-scenario GaN driver IC solutions
- Interpreting Huawei’s new solid-state battery patent, will it challenge CATL in 2030?
- Are pure electric/plug-in hybrid vehicles going crazy? A Chinese company has launched the world's first -40℃ dischargeable hybrid battery that is not afraid of cold
- [Analysis of the topic of the college electronic competition] —— 2020 TI Cup E topic "Amplifier nonlinear distortion research device"
- DSP C6000 Peripherals
- Can 5 GHz iFEM really speed up the launch of Wi-Fi 6 home mesh products?
- Operational Amplifier
- [RVB2601 Creative Application Development] Environmental Monitoring Terminal 05- Temperature and Humidity Collection and Display
- Principles of Radar (5th Edition)
- Current Sensing Using Nanopower Op Amps
- Looking for a microcontroller model
- Analysis of the problem that the program cannot run after F28004x online debugging reset
- Is the STM32 library function HAL_UART_Receive blocking?