基于STM32F103的USB学习笔记15 - USB中断处理

STM32F103的USB中断状态寄存器(USB_ISTR)有8个类型的中断,这8个中断都会让MCU产生一个USB_LP_CAN1_RX0_IRQHandler中断,这8个中断源的标志位于ISTR寄存器的高8bit,定义如下:

#define ISTR_CTR    (0x8000) /* Correct TRansfer (clear-only bit) */
#define ISTR_DOVR   (0x4000) /* DMA OVeR/underrun (clear-only bit) */
#define ISTR_ERR    (0x2000) /* ERRor (clear-only bit) */
#define ISTR_WKUP   (0x1000) /* WaKe UP (clear-only bit) */
#define ISTR_SUSP   (0x0800) /* SUSPend (clear-only bit) */
#define ISTR_RESET  (0x0400) /* RESET (clear-only bit) */
#define ISTR_SOF    (0x0200) /* Start Of Frame (clear-only bit) */
#define ISTR_ESOF   (0x0100) /* Expected Start Of Frame (clear-only bit) */

依次为

1. CTR: 传输正确中断,只读。端点完成一个正常的传输后由硬件置位。USB的数据通信就是通过这个中断实现的,USB库中的函数CTR_LP就是处理这个中断的。

2. PMAOVR:分组缓冲区溢出,读写,写0有效,写1无效。正常情况不会产生这个中断。目前的做法只是清掉这个中断标志位,不做其他处理。应该符合出错了不需要回信息给主机,主机等待超时即可。

        _SetISTR((uint16_t)CLR_DOVR);

3. ERR:出错,读写,写0有效,写1无效。程序可以不处理这个中断,仅作为调试使用(原因与PMAOVR的处理相同)。

        _SetISTR((uint16_t)CLR_ERR);

4. WKUP:唤醒请求中断,读写,写0有效,写1无效。

        _SetISTR((uint16_t)CLR_WKUP);
        Resume(RESUME_EXTERNAL);

        这里调用了Resume(RESUME_EXTERNAL),Resume对应Suspend,即从Suspend恢复。参数RESUME_EXTERNAL表示Resume的类型,查看代码可以看出,只有USB发送了唤醒请求产生WKUP中断才会有此类型。

        if (remotewakeupon ==0)
        {
                Resume_Init();
                ResumeS.eState = RESUME_OFF;
        }
        else /* RESUME detected during the RemoteWAkeup signalling => keep RemoteWakeup handling*/
        {
                ResumeS.eState = RESUME_ON;
        }

        对于Joystick例程来说,remotewakeupon一直为0,所以都是调用Resume_Init,ResumeS.eState由RESUME_EXTERNAL变为RESUME_OFF。

        void Resume_Init(void)
        {
                uint16_t wCNTR;
                wCNTR = _GetCNTR();
                wCNTR &= (~CNTR_LPMODE);
                _SetCNTR(wCNTR);    
                _SetCNTR(IMR_MSK);

        }

即退出低功耗模式(CNTR_LPMODE位清0),然后设置中断使能(Joystick例程没有调用Leave_LowPowerMode)。

5. SUSP:挂起请求中断,读写,写0有效,写1无效。USB总线上超过3ms没有信号传输时由硬件置位。

        /* check if SUSPEND is possible */
        if (fSuspendEnabled)
        {
                Suspend();
        }
        else
        {
                /* if not possible then resume after xx ms */
                Resume(RESUME_LATER);
        }
        /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
        _SetISTR((uint16_t)CLR_SUSP);

fSuspendEnabled是一个全局变量,控制是否支持Suspend(实际上Joystick例程一直是TRUE)。

void Suspend(void)
{
       uint32_t i =0;
       uint16_t wCNTR;

       wCNTR = _GetCNTR();  
       for (i=0;i<8;i++) EP[i] = _GetENDPOINT(i);


       wCNTR|=CNTR_RESETM;
       wCNTR|=CNTR_FRES;
       _SetCNTR(wCNTR);
       wCNTR&=~CNTR_FRES;
       _SetCNTR(wCNTR);
       while((_GetISTR()&ISTR_RESET) == 0);
       _SetISTR((uint16_t)CLR_RESET);


       for (i=0;i<8;i++)
              _SetENDPOINT(i, EP[i]);


       wCNTR |= CNTR_FSUSP;
       _SetCNTR(wCNTR);
       wCNTR = _GetCNTR();
       wCNTR |= CNTR_LPMODE;
       _SetCNTR(wCNTR);
}

Suspend分4个步骤,首先是保存寄存器CNTR和8个端点寄存器;第二步是使能复位,然后强制复位USB IP,复位完等待复位结束;第三步恢复8个端点寄存器;最后一步设置强制挂起(CNTR_FSUSP),并且进入低功耗模式(CNTR_LPMODE)。

6. RESET:复位请求中断,读写,写0有效,写1无效。

       _SetISTR((uint16_t)CLR_RESET);
       Device_Property.Reset();

Device_Property.Reset会初始化Feature,初始化端点,初始化USB地址。

7. SOF:帧首标志中断,读写,写0有效,写1无效。此位在USB模块检测到总线上的SOF分组时由硬件置位,标志一个新的USB帧的开始。

       _SetISTR((uint16_t)CLR_SOF);
       bIntPackSOF++;

这里只是记录有多少个SOF,但是并没有实际处理,这里不是很理解!!!

8. ESOF:期望帧首标识中断,读写,写0有效,写1无效。此位在USB模块未收到期望的SOF分组时由硬件置位。如果连续发生3次ESOF中断,也就是连续3次未收到SOF分组,将产生SUSP中断。

       _SetISTR((uint16_t)CLR_ESOF);
       if ((_GetFNR()&FNR_RXDP) != 0)
       {
              /* increment ESOF counter */
              esof_counter ++;
      
              /* test if we enter in ESOF more than 3 times with FSUSP =0 and RXDP =1=>> possible missing SUSP flag*/
              if ((esof_counter >3)&&((_GetCNTR()&CNTR_FSUSP)==0))
              {           
                     /* this a sequence to apply a force RESET*/
                     /*Store CNTR value */
                     wCNTR = _GetCNTR(); 
      
                     /*Store endpoints registers status */
                     for (i=0;i<8;i++) EP[i] = _GetENDPOINT(i);
      
                     /*apply FRES */
                     wCNTR|=CNTR_FRES;
                     _SetCNTR(wCNTR);
 
                     /*clear FRES*/
                     wCNTR&=~CNTR_FRES;
                     _SetCNTR(wCNTR);
      
                      /*poll for RESET flag in ISTR*/
                      while((_GetISTR()&ISTR_RESET) == 0);
  
                      /* clear RESET flag in ISTR */
                     _SetISTR((uint16_t)CLR_RESET);
   
                     /*restore Enpoints*/
                     for (i=0;i<8;i++)
                            _SetENDPOINT(i, EP[i]);
      
                     esof_counter = 0;
              }
       }
       else
       {
              esof_counter = 0;
       }
    
       /* resume handling timing is made with ESOFs */
       Resume(RESUME_ESOF); /* request without change of the machine state */

if ((_GetFNR()&FNR_RXDP) != 0)是判断DP的状态,此位用于观察USB D+数据线的状态,猜测非0表示DP已经上拉,然后通过esof_counter计数3次,如果计数3次后设备不是在Suspend状态就进入强制复位USB IP。

最后Resume,参数为RESUME_ESOF表示更新Resume State状态为RESUME_OFF,即已经完成RESUME。

 

在USB中断函数中添加这个中断的打印信息:

    wIstr = _GetISTR();
    //USB_ISTR_DEBUG(Printf("istr:%x\r\n", wIstr));
    
    #if (IMR_MSK & ISTR_CTR)
    if (wIstr & ISTR_CTR & wInterrupt_Mask)
    {
        USB_ISTR_DEBUG(Printf("Transfer EP%d %s ok\r\n"), (wIstr & 0x0F), ((wIstr & 0x08) > 0) ? "IN" : "OUT");
    }
    #endif 
    
    #if (IMR_MSK & ISTR_DOVR)
    if (wIstr & ISTR_DOVR & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_DOVR);
        USB_ISTR_DEBUG(Printf("USB Underrun\r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_ERR)
    if (wIstr & ISTR_ERR & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_ERR);
        USB_ISTR_DEBUG(Printf("USB Error r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_WKUP)
    if (wIstr & ISTR_WKUP & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_WKUP);
        USB_ISTR_DEBUG(Printf("USB Wakeup\r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_SUSP)
    if (wIstr & ISTR_SUSP & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_SUSP);
        USB_ISTR_DEBUG(Printf("USB Suspend\r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_RESET )
    if (wIstr & ISTR_RESET  & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_RESET);
        USB_ISTR_DEBUG(Printf("USB Reset\r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_SOF)
    if (wIstr & ISTR_SOF & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_SOF);
        USB_ISTR_DEBUG(Printf("USB SOF\r\n"));
    }
    #endif
    
    #if (IMR_MSK & ISTR_ESOF)
    if (wIstr & ISTR_ESOF & wInterrupt_Mask)
    {
        _SetISTR((uint16_t)CLR_ESOF);
        USB_ISTR_DEBUG(Printf("USB ESOF\r\n"));
    }
    #endif

可以看到打印信息如下:

USB ESOF
USB ESOF
USB Suspend
USB ESOF
USB ESOF
USB ESOF
USB ESOF
USB Suspend

即USB中断先发生的是ESOF中断。可以按照例程将这个中断处理函数补齐,可以通过串口打印观察中断内容变化

USB ESOF
USB ESOF
USB Suspend
USB ESOF
USB Wakeup
USB Reset

 

 

你可能感兴趣的:(MCU编程,USB)