我正在使用带有STM32_USB-FS-Device_Lib_V3.2.1 USB库的STM32F105微控制器,并根据我们的目的调整了VCP示例(与RTOS和串行API集成)。
问题是如果连接了USB电缆,但Windows主机上没有打开端口,几分钟后设备会永久重新进入USB ISR,直到端口打开然后全部启动正常工作。
我已经检测了中断处理程序并且可以看到当故障发生时,ISR处理程序退出然后立即重新进入。发生这种情况是因为在退出中断时,OTG_FS_GINTSTS中的IEPINT标志不清楚。此时OTG_FS_DAINT包含0x00000002(IEPINT1设置),而DIEPINT1包含0x00000080(TXFE)。调用清除TXFE的OTGD_FS_Handle_InEP_ISR()中的行,但该位不清除或立即重新置位。当主机上的COM端口重新打开时,中断结束时OTG_FS_GINTSTS和OTG_FS_DAINT的状态始终为零,并且以正常速率发生进一步的中断。请注意,只有在输出数据但主机没有打开端口时才会出现此问题。如果端口打开或没有输出数据,系统将无限期运行。我相信输出的数据越多,问题就越早出现,但目前这是轶事。
VCP代码有一个状态变量,它接受以下枚举值:
UNCONNECTED,
ATTACHED,
POWERED,
SUSPENDED,
ADDRESSED,
CONFIGURED
我们使用CONFIGURED状态来确定是否将数据放入驱动程序缓冲区以进行发送。但是,当连接电缆而不是主机打开端口并连接应用程序时,将设置CONFIGURED状态。我看到当Windows打开端口时,会出现一连串的中断,所以似乎在这个事件上发生了一些通信;我想知道是否有可能检测主机端口是否打开,
我可能需要两件事之一:
答案 0 :(得分:7)
第(1)部分 - 防止中断锁定 - 来自ST支持的USB库错误修复;它没有正确清除TxEmpty中断。
经过ST支持的一些研究和帮助,我已经确定了第(2)部分的解决方案 - 检测主机端口是否打开。通常,当端口打开时,DTR调制解调器控制线被断言。此信息将传递给CDC类设备,因此我可以使用它来实现我的目标。应用程序可能会更改DTR的行为,但在这种情况下,任何可能连接到此设备的客户端应用程序都不会发生这种情况。但是,如果设置了线路编码(波特,帧),则会有一个备用计划隐含地假定端口是开放的。在这种情况下,没有办法检测关闭,但至少它不会阻止非常规应用程序使用我的设备,即使它在断开连接时导致它崩溃。
关于ST的VCP示例代码,我特别对usb_prop.c进行了以下更改:
1)增加了以下功能:
#include <stdbool.h>
static bool host_port_open = false ;
bool Virtual_Com_Port_IsHostPortOpen()
{
return bDeviceState == CONFIGURED && host_port_open ;
}
2)因此改进了对SET_CONTROL_LINE_STATE的Virtual_Com_Port_NoData_Setup()处理:
else if (RequestNo == SET_CONTROL_LINE_STATE)
{
// Test DTR state to determine if host port is open
host_port_open = (pInformation->USBwValues.bw.bb0 & 0x01) != 0 ;
return USB_SUCCESS;
}
3)为了允许使用不按常规操作DTR的应用程序,我还修改了对SET_LINE_CODING的Virtual_Com_Port_Data_Setup()处理:
else if (RequestNo == SET_LINE_CODING)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
{
CopyRoutine = Virtual_Com_Port_SetLineCoding;
// If line coding is set the port is implicitly open
// regardless of host's DTR control. Note: if this is
// the only indicator of port open, there will be no indication
// of closure, but this will at least allow applications that
// do not assert DTR to connect.
host_port_open = true ;
}
Request = SET_LINE_CODING;
}
答案 1 :(得分:1)
我可能需要两件事之一:
- 防止USB代码在第一时间卡在ISR中
- 确定主机是否从设备端打开端口,并且仅在打开时推送数据以进行发送。
醇>
您应该尝试执行选项1而不是2.在Windows和Linux上,可以打开COM端口并在不设置控制信号的情况下使用它,这意味着没有万无一失的跨平台方式检测到COM端口是否打开。
一个编程良好的设备不会因为USB主机停止轮询数据而让自己停止运行;这是正常的事情应该妥善处理。例如,您可以更改代码,以便只有在端点可用的缓冲区空间时才排队要发送到USB主机的数据。如果没有空闲缓冲区空间,则可能会有一些特殊的错误处理代码。
答案 2 :(得分:1)
我通过采用CDC_Transmit_FS找到了另一种解决方案。 它现在可以通过覆盖_write函数用作printf的输出。
首先检查连接状态,然后尝试通过USB端口在忙碌循环中发送,如果USB忙,则重复发送。
我发现如果dev_state不是USBD_STATE_CONFIGURED则USB插头已断开连接。如果插头已连接但没有通过PuTTY或白蚁打开VCP端口,则第二次检查失败。
对于RTOS和CubeMX HAL应用程序,此实现适用于我。繁忙循环不再阻止低优先级线程。
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
// Check if USB interface is online and VCP connection is open.
// prior to send:
if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
|| (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
{
// The physical connection fails.
// Or: The phycical connection is open, but no VCP link up.
result = USBD_FAIL;
}
else
{
USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);
// Busy wait if USB is busy or exit on success or disconnection happens
while(1)
{
//Check if USB went offline while retrying
if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
|| (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
{
result = USBD_FAIL;
break;
}
// Try send
result = USBD_CDC_TransmitPacket(hUsbDevice_0);
if(result == USBD_OK)
{
break;
}
else if(result == USBD_BUSY)
{
// Retry until USB device free.
}
else
{
// Any other failure
result = USBD_FAIL;
break;
}
}
}
return result;
}
CDC_Transmit_FS由_write:
使用// This function is used by printf and puts.
int _write(int file, char *ptr, int len)
{
(void) file; // Ignore file descriptor
uint8_t result;
result = CDC_Transmit_FS((uint8_t*)ptr, len);
if(result == USBD_OK)
{
return (int)len;
}
else
{
return EOF;
}
}
此致 哈德
答案 3 :(得分:0)
我有同样的要求来检测PC端口打开/关闭。我看到它实现如下:
检测到开启:
检测到关闭:
这似乎运作得相当好,但需要进行更全面的测试才能确认其运行稳健。
答案 4 :(得分:0)
免责声明:我使用Cube生成的代码,因此它可以与HAL驱动程序配合使用。之前提出的解决方案,不适合我,所以我找到了一个。它不好,但可以用于某些目的。
当您尝试通过CDC_Transmit_FS传输数据包,然后等到TxState设置为0时,会出现未打开端口的间接符号之一。如果端口未打开,则永远不会发生。所以我的解决方案是修复一些超时:
uint16_t count = 0;
USBD_CDC_HandleTypeDef *hcdc =
(USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;
while (hcdc->TxState != 0) {
if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
//here it's clear that port is not opened
}
}
问题还在于,如果尝试打开端口,在设备尝试发送数据包后,就无法完成。因此我使用的整个程序:
uint8_t waitForTransferCompletion(void) {
uint16_t count = 0;
USBD_CDC_HandleTypeDef *hcdc =
(USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;
while (hcdc->TxState != 0) {
if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
USBD_Stop(&USBD_Device); // stop and
MX_USB_DEVICE_Init(); // init device again
HAL_Delay(RESET_DELAY); // give a chance to open port
return USBD_FAIL; // return fail, to send last packet again
}
}
return USBD_OK;
}
问题是,超时有多大,而不是在端口打开时中断传输。我将BUSY_TIMEOUT设置为3000,现在可以正常工作。
答案 5 :(得分:0)
我通过检查变量 SELECT
CASE
WHEN ac.actor_id < te.actor_id THEN ac.first_name
ELSE te.first_name
END
, CASE
WHEN ac.actor_id > te.actor_id THEN te.first_name
ELSE ac.first_name
END
, fi.title
, fi.film_id
FROM Film_Actor as fa
INNER JOIN Actor as ac ON fa.actor_id = ac.actor_id
INNER JOIN Film as fi ON fa.film_id = fi.film_id
INNER JOIN ( SELECT a_c.first_name, a_c.last_name, a_c.actor_id FROM
Film_Actor as f_a
INNER JOIN Actor as a_c ON f_a.actor_id = a_c.actor_id) te
ON te.actor_id=fa.actor_id
来修复它。
如果连接则等于5,如果未连接或断开则等于4。
但。 HAL存在一些问题。程序启动时它等于5。
接下来的步骤在程序开始时将其修复
hUsbDeviceFS.ep0_state
我没有任何想要学习HAL的愿望 - 我希望开发人员能够看到这篇文章,他们将修复HAL。 它帮助我解决了我的问题。