1、STM32F USB主机
本文介绍STM32F USB OTG FS主机的中断过程及时序,关于USB通信的原理与报文不在本文的介绍,请参考其他文档。本文根据实际的使用USB主机详细的描述的USB主机中断发生的过程及顺序,是全网少有的文章。本文介绍所涉及的USB主机程序请参考rt thread操作系统的USB主机协议栈与USB主机驱动。
2、USB主机OUT传输中断
应用程序调用USB主机驱动发送数据到USB设备,这个过程会在USB总线上面产生OUT传输过程。即应用程序调用usb主机rt_usb_hcd_pipe_xfer函数,此函数会对发送的数据长度按端点传输包的大小进行分包,每次发送一个分包,发送多个分包后才完成本次的数据传输。rt_usb_hcd_pipe_xfer最后调用usb底层驱动函数drv_pipe_xfer,调用HAL_HCD_HC_SubmitRequest函数来向USB主机发出一个OUT的URB请求,让usb主机在usb总线上输出一个数据分包。usb主机会在usb总线上发出一个OUT令牌,再发出一个OUT数据包,USB设备成功接收后,发出ACK给主机此时主机产生OUT ACK中断,再产生OUT XFRC中断(传输完成),最后再产生OUT CHH中断(通信停止),此时向驱动程序drv_pipe_xfer发送传输完成信号量,通知驱动完成此次分包传输。当发送的数据长度大于一个端点数据包长度时,会再进行多次如上的操作。具体过程如下图所示。
int rt_usb_hcd_pipe_xfer(uhcd_t hcd, upipe_t pipe, void* buffer, int nbytes, int timeout)
{
rt_size_t remain_size;
rt_size_t send_size;
remain_size = nbytes;
int len = 0;
if(pipe->ep.bEndpointAddress & 0x80)
{
/*control ep*/
len = hcd->ops->pipe_xfer(pipe, USBH_PID_DATA, buffer, nbytes, timeout);
if((len >=0) && (len <= nbytes))
{
return len;
}
else
{
return 0;
}
}
else
{
/*OUT 传输在这里进行了分包*/
rt_uint8_t * pbuffer = (rt_uint8_t *)buffer;
do
{
RT_DEBUG_LOG(RT_DEBUG_USB,("pipe transform remain size,: %d\n", remain_size));
send_size = (remain_size > pipe->ep.wMaxPacketSize) ? pipe->ep.wMaxPacketSize : remain_size;
if(hcd->ops->pipe_xfer(pipe, USBH_PID_DATA, pbuffer, send_size, timeout) == send_size)
{
remain_size -= send_size;
pbuffer += send_size;
}
else
{
return 0;
}
}while(remain_size > 0);
return nbytes;
}
}
3、USB主机IN传输中断
USB主机要从设备中接收一段数据时,应用程序会调用rt_usb_hcd_pipe_xfer函数,此函数把接收数据的缓冲区地址和长度传输给底层驱动drv_pipe_xfer函数,清除信号量,调用HAL_HCD_HC_SubmitRequest函数来向USB主机发出一个IN的URB请求。USB主机在USB总线上发出IN令牌,当usb设备有无数据传输时返回NAK,并产生USB主机IN NAK中断;当USB设备有数据传输时,发生IN RXQLVL中断(接收水平),USB设备根据要传输的数据总长按端点最大数据包长度进行分包发送,发生IN RXQLVL的次数就是分包的数量,所以说主机IN传输的分包是由USB控制器来分包的,与OUT传输不一样。USB设备发送数据 完成后,USB主机发出IN ACK中断,再发生IN XFRC中断,IN CHH中断,此时发送信号量通知驱动程序已经接收到数据。
如果应用程序想一直等待设备发送出数据,驱动程序中等待接收的信号量超时时间无线长就可以了,USB主机会在总线完全空闲时不断的发出IN令牌,直到USB设备有数据发送后停止此次IN传输。