STM32 USB data receiving and data sending process

      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 for USB devices.

We need to define the USB interrupt. Start with two interrupts for USB in the interrupt vector table of STM32, we can find these two interrupts in stm32f10x.h:

 
  1. USB_HP_CAN1_TX_IRQn         = 19,   /*!< USB Device High Priority or CAN1 TX Interrupts  */

  2.   USB_LP_CAN1_RX0_IRQn        = 20,   /*!< USB Device Low Priority or CAN1 RX0 Interrupts  */

These two interrupts are the multiplexed interrupts of USB and CAN. When used as USB, they represent the high priority and low priority interrupts of the USB device. In my project, I choose to use low-priority USB interrupts. code show as below:

 
  1. void USB_LP_CAN1_RX0_IRQHandler(void)

  2. {

  3.   USB_Istr();

  4. }

The interrupt service routine is very simple, that is, calling the USB_istr() function when an interrupt occurs. The function USB_istr(), which we said before, is defined in usb_istr.c. This function handles the interrupts defined in the ITR interrupt status register, including: CTR correct transmission interrupt, RESET reset interrupt, DOVR packet buffer overflow interrupt, ERR error interrupt, WAKEUP interrupt, SUSP suspension interrupt, SOF frame first interrupt, ESOF expected frame first Interrupted. The key here is the CTR interrupt. After the USB sends or receives the data correctly, the USB module automatically returns the bit of the ITR register to 1, triggering the interrupt CTR interrupt. The CTR processing code in USB_istr() is as follows:

 
  1. #if (IMR_MSK & ISTR_CTR) //正确传输中断CTR标志

  2.   if (wIstr & ISTR_CTR & wInterrupt_Mask)//读出的中断标志是CRT中断标志,且CRT中断使能了

  3.   {

  4.     CTR_LP(); //调用正确传输中断服务程序

  5. #ifdef CTR_CALLBACK

  6.     CTR_Callback(); //当定义了CTR_CALLBACK,则调用CTR_Callback,像钩子函数一样,在发生CRT中断时做点什么

  7. #endif

  8.   }

First, we must 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 that the mask contains all interrupts. IMR_MSK & ISTR_CTR means: if ITR_CTR is the specified interrupt category, compile the code between #if and #endif. Obviously it fits here. Then, it is judged that the interrupt value read from the CNTR register is a CRT interrupt, and the interrupt has been enabled in CNTR. Then call the CTR_LP() function to process. If CTR_CALLBACK is defined, call the CTR_Callback() function, which 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. code show as below:

 
  1. /*******************************************************************************

  2. * Function Name  : CTR_LP.

  3. * Description    : 低优先级的端点正确传输中断服务程序

  4. * Input          : None.

  5. * Output         : None.

  6. * Return         : None.

  7. *******************************************************************************/

  8. void CTR_LP(void)

  9. {

  10.   __IO uint16_t wEPVal = 0;

  11.   while (((wIstr = _GetISTR()) & ISTR_CTR) != 0) //读取中断状态寄存器的值,看是否是CRT(正确传输中断)

  12.   {

  13.     EPindex = (uint8_t)(wIstr & ISTR_EP_ID); //获取产生中断的端点号,

  14.     if (EPindex == 0) //如果端点0

  15.     {

  16.     SaveRState = _GetENDPOINT(ENDP0); //读取端点0的状态寄存器

  17.     SaveTState = SaveRState & EPTX_STAT; //保存端点0发送状态

  18.     SaveRState &=  EPRX_STAT; //保存端点0接收状态

  19.  
  20.     _SetEPRxTxStatus(ENDP0,EP_RX_NAK,EP_TX_NAK);//设置端点0对主机以NAK方式响应所有的接收和发送请求

  21.       if ((wIstr & ISTR_DIR) == 0) //如果是IN令牌

  22.       {

  23.         _ClearEP_CTR_TX(ENDP0); //清除端点0正确发送标志位

  24.         In0_Process(); //处理IN令牌包

  25.  
  26.            /* before terminate set Tx & Rx status */

  27.  
  28.             _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//在传输之前设置端点0接收发送状态位

  29.   return;

  30.       }

  31.       else //OUT令牌

  32.       {

  33.         wEPVal = _GetENDPOINT(ENDP0); //获取端点0的端点寄存器的值

  34.         

  35.         if ((wEPVal &EP_SETUP) != 0) //SETUP分组传输完成标志位

  36.         {

  37.           _ClearEP_CTR_RX(ENDP0);  //清除端点0的接收标志位

  38.           Setup0_Process(); //端点0建立阶段的数据处理

  39.  
  40.       _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//设置端点0阶接收发送标志位

  41.           return;

  42.         }

  43.  
  44.         else if ((wEPVal & EP_CTR_RX) != 0) //正确接收标志位

  45.         {

  46.           _ClearEP_CTR_RX(ENDP0); //清除端点0正确标志位

  47.           Out0_Process(); //处理OUT令牌包

  48.      

  49.      _SetEPRxTxStatus(ENDP0,SaveRState,SaveTState);//设置端点0的接收发送状态

  50.           return;

  51.         }

  52.       }

  53.     }/* if(EPindex == 0) */

  54.     else //如果非0端点

  55.     {

  56.       wEPVal = _GetENDPOINT(EPindex); //获取该端点的端点寄存器的值

  57.       if ((wEPVal & EP_CTR_RX) != 0) //正确接收标志

  58.       {

  59.         _ClearEP_CTR_RX(EPindex); //清除端点正确接收标志

  60.  
  61.         (*pEpInt_OUT[EPindex-1])(); //调用注册过的端点OUT处理函数

  62.  
  63.       } /* if((wEPVal & EP_CTR_RX) */

  64.  
  65.       if ((wEPVal & EP_CTR_TX) != 0) //正确发送标志

  66.       {

  67.         _ClearEP_CTR_TX(EPindex); //清除正确发送标志

  68.  
  69.         (*pEpInt_IN[EPindex-1])(); //调用注册过的端点IN处理函数

  70.       } /* if((wEPVal & EP_CTR_TX) != 0) */

  71.  
  72.     }/* if(EPindex == 0) else */

  73.  
  74.   }/* while(...) */

  75. }

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

In the enumeration phase, SaveRState saves the value of the endpoint 0 register, then SaveTState = SaveRState & EPTX_STAT; and SaveRState &= EPRX_STAT; these two sentences, SaveTState saves the current sending endpoint 0 state, SaveRState saves the current receiving endpoint state. Then set the receiving endpoint 0 to NAk state, and the sending endpoint 0 to NAK state. That is to say, when the host sends any data, the slave only responds with NAK, and the slave can only send NAK data, which is not allowed in the data processing stage Perform data communication. Then determine whether it is input or output. If it is an input (note that the input here is relative to the host), clear the EP_CTR_TX flag bit 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 must be judged whether it 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 and call the SETUP processing function Setup0_Process(), and at the same time restore the original state of the receiving and sending endpoints, ready to process 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 at the same time restore the state of the original connection port, ready to process the next interrupt processing.

In the working phase or non-enumeration phase, first determine whether it is EP_CTR_RX or EP_CTR_TX flag. If EP_CTR_RX receives the flag correctly, then clear the flag and call the OUT processing function of the corresponding endpoint

Number (*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).

There are 7 endpoint input functions and endpoint output functions registered in usb_istr.c. as follows:

 
  1. /*定义指向指针的函数指针数组,函数指针分别指向7个端点输入服务程序*/

  2. void (*pEpInt_IN[7])(void) =

  3.   {

  4.     EP1_IN_Callback,

  5.     EP2_IN_Callback,

  6.     EP3_IN_Callback,

  7.     EP4_IN_Callback,

  8.     EP5_IN_Callback,

  9.     EP6_IN_Callback,

  10.     EP7_IN_Callback,

  11.   };

  12.  
  13. /*定义指向指针的函数指针数组,函数指针分别指向7个端点输出服务程序*/

  14. void (*pEpInt_OUT[7])(void) =

  15.   {

  16.     EP1_OUT_Callback,

  17.     EP2_OUT_Callback,

  18.     EP3_OUT_Callback,

  19.     EP4_OUT_Callback,

  20.     EP5_OUT_Callback,

  21.     EP6_OUT_Callback,

  22.     EP7_OUT_Callback,

  23.   };

And the definition of these functions is in usb_endp.c, we take EP1_OUT_Callback() function to analyze.

 
  1. /*******************************************************************************

  2. * Function Name  : EP1_OUT_Callback.

  3. * Description    : 端点1输出回调函数

  4. * Input          : None.

  5. * Output         : None.

  6. * Return         : None.

  7. *******************************************************************************/

  8. void EP1_OUT_Callback(void)

  9. {

  10. PMAToUserBufferCopy(USB_Receive_Buffer, ENDP1_RXADDR, REPORT_COUNT);  //PMA缓冲区接收到的数据拷贝到用户自定义缓冲区USB_Receive_Buffer中

  11.    SetEPRxStatus(ENDP1, EP_RX_VALID); //设置端点的接收状态为有效,因为端点接收到数据后会端点状态自动设置成停止状态

  12.   USB_Received_Flag=1; //设置接收到数据标志位

  13. }

The work of this function is very simple. First of all, because the data output endpoint is receiving data, and the data received by the USB module is temporarily stored in the PAM double buffer, it is necessary to read the data from the PMA and put it to the user. Own buffer. Then set the endpoint receiving state to be valid, because when the data is received, the endpoint will be closed. Finally set the receive with data flag.

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

 
  1. /**

  2.   * @brief  通过USB发送数据

  3.   * @param  data 数据存储首地址

  4.   * @param  dataNum 发送的数据字节数

  5.   * @retval 发送的字节数

  6.   */

  7. uint32_t USB_SendData(uint8_t *data,uint32_t dataNum)  

  8. {

  9. //将数据通过USB发送出去

  10. UserToPMABufferCopy(data, ENDP2_TXADDR, dataNum);//拷贝数据到PMA中

  11. SetEPTxCount(ENDP2, REPORT_COUNT); //从端点2发送64字节数据

  12. SetEPTxValid(ENDP2);    //使能端点2的发送状态

  13. return dataNum;  

  14. }

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

To summarize:

Data transmission: UserToPMABufferCopy--->SetEPTxCount--->SetEPTxValid

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

Guess you like

Origin blog.csdn.net/qq_25479231/article/details/106043731