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