据说USB虚拟串口的最高速度可以达到40MB/s.
于是兴奋的买了块微雪的开发板. 单片机是STM32F407IGT6, USB采用高速USB3300芯片, (于是就掉坑里了. 差点没爬上来, 爬了2个多月的业余时间. 目前基本上算是爬上来了.)

这个USB通信有三个大问题.
第一个问题是: 电脑无法识别USB虚拟出来的串口. 时不时的掉线.不识别偶尔又能识别.
第二个问题是: 在调用CDC_Transmit_HS这个函数的时候,总是会卡在return USBD_BUSY ;代码在下面
第三个问题是: 如果快速的重复调用CDC_Transmit_HS 还是会卡死…

 uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len)
{uint8_t result = USBD_OK;/* USER CODE BEGIN 12 */USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData;if (hcdc->TxState != 0){return USBD_BUSY; //总是会卡在这里}//解决发 字节是64整数倍的bugif((Len % 64) ==0){USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len);result = USBD_CDC_TransmitPacket(&hUsbDeviceHS);while(hcdc->TxState !=0);USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, 0);result = USBD_CDC_TransmitPacket(&hUsbDeviceHS);}else{USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len);result = USBD_CDC_TransmitPacket(&hUsbDeviceHS);}//下面部分的确保全部发完再继续, 但是实际测试没啥效果.uint8_t time = HAL_GetTick();while(hcdc->TxState != 0){if(HAL_GetTick() - time > 1000){return result;}}/* USER CODE END 12 */return result;
}

针对第一个问题: 电脑无法识别USB虚拟出来的串口.

第一次启动偶尔是OK的.
经过插插拔拔的无数次实验
最后总结出一条规律,
纯软件重启单片机不行.
按板子上的Rest按钮也不行,
开发IDE工具里面重启也不行.
拔掉USB数据线也不行.
重新烧录也不行.
唯一可行的方法就是烧写之后, 拔掉烧录的数据线,重新插上就可以了.

目前我唯一的解决方案是,先编译,下载烧录好代码过后,再重新插拔一下JTAG烧录调试线. 再打开上位机就可以正常打开端口进行通信了.
一天来来回回几百次. USB口都松了.

网上有人说,初始化USB的时候强制重置一下USB的时钟就可以了. 这个实在不太懂怎么重置USB的时钟. 我直觉告诉我这应该是解决方案. 可我不会初始化USB时钟啊. 哪位神秘的大哥也没说怎么重置USB的时钟(后来证明不是这个问题)

总结(吐槽)一下 坑太多. 而自己又没有掌握太多跳出坑的方法(经验太少).差点放弃.
单片机的坑比纯软件开发的坑要难得多.
因为纯软件开发顺着数据流来理就能理清楚. 很容易找到问题点. 而单片机开发, 不单单有数据流, 还有硬件,电子, 干扰,静电,接口不牢固,底层库有bug, 别人的代码看不懂, 整套环境不理解. C语言不精通. 这些问题纠缠在一起 没个1年半载的想很顺利的排除问题,掌握它,真的是非常困难. 并且这些问题的纠缠不是串行的是并行的. 看似完全不相干的东西也会相互干扰…(经验的价值随之体现)

后来我发现,USB能识别了,但是虚拟串口却时不时的打不开. 再后来在参考了网上的几篇文章之后,发现端口总是打不开的最大原因是
在usbd_conf.h中 开启了内存动态分配

/** Alias for memory allocation. */
#define USBD_malloc         malloc //就是这个宏分配内存的./** Alias for memory release. */
#define USBD_free           free/** Alias for memory set. */
#define USBD_memset         memset/** Alias for memory copy. */
#define USBD_memcpy         memcpy/** Alias for delay. */
#define USBD_Delay          HAL_Delay/* DEBUG macros */

参考文章如下.
https://blog.csdn.net/a136498491/article/details/85304905

思路:偶然间看到有人说在USB库的初始化代码那里,malloc函数返回值那里打个断电。果然,多次插拔USB会发生malloc失败。具体原因没有深究,我早就看不顺heap了,决定去掉heap,使用静态分配。

过程:

1、USBD_CDC_Init中找到USBD_malloc调用的地方,改成静态分配;

2、USBD_CDC_DeInit中注释掉USBD_free函数的调用;

3、IAR(我用的IAR)配置中,General Options->Library Options 2 -> Heap
selection中选择No-free heap;

4、Linker中把heap设置为0.

然后上电插拔打开测试30次,问题消失。

既然跟内存动态分配有关,干脆直接去掉动态分配,改成静态的.
另外这篇文章里写了如何将USBD_malloc改成静态的. 多谢前辈的文章.
https://blog.csdn.net/anobodykey/article/details/50700304

方法知道了我这边的做法很简单
在usbd_conf.c文件中加入如下代码即可
放在 /* USER CODE BEGIN Includes */中即可

.....
.....
.....
/* USER CODE BEGIN Includes */
//#include "MyUsbd_conf.h"void * USBD_static_malloc(uint32_t size)
{//size 参数= sizeof (USBD_CDC_HandleTypeDef);//因为是静态分配的,无法知道sizeof (USBD_CDC_HandleTypeDef)的大小初始值. 这里的540是调试出来的, 每种单片机都需要调试一下看看传进来的size是多少,然后改成这个数值.static uint32_t mem[540];return mem;
}
void USBD_static_free(void *p)
{}#define USBD_malloc   (uint32_t *)USBD_static_malloc     //#define USBD_malloc         malloc 被替换掉#define USBD_free    USBD_static_free   //#define USBD_free   free 被替换掉/* USER CODE END Includes */
.....
.....
.....

注意替换里面的 540 参数…
要调试一次才知道具体的数值.
我估计改成动态的也是因为程序员头疼怎么实现这个效果了… 因为根本就无法在 设计的时候知道运行时size…
设计成这种 动态分配内存也是无奈之举…

搞成静态分配以后虚拟串口确实稳定多了. 也不会识别不了接口了.无法打开端口的问题就没有了 经过我长达10几天的测试基本上可以说第一个问题已经彻底解决了,USB识别妥妥的.虚拟的串口也能识别了.

针对第二个问题: 在调用CDC_Transmit_HS这个函数的时候,总是会卡在return USBD_BUSY ;

最后找到了.这篇文章.
http://news.eeworld.com.cn/mcu/2018/ic-news092041381.html
大概意思是USB库为了确保数据能完整发出去, 做了个循环检测, 如果数据没发送完,后面的数据是无法再发送的, 也就是说的FIFO (先进先出). (纯粹我自己瞎猜, 没看懂代码.) 如果强行修改此处的逻辑就无法保证收到的数据是完整的. 或者被覆盖的.

我感觉应该是上位机处理的速度太慢了吧. 导致数据发送不完. 堆在那里.
所以根本的解决之道是提高上位机的处理速度. 或者能加个缓冲什么的. 或者把算法放一部分到单片机里面做.压缩或减少要发送的数据…

下面的修改要慎重.我纯粹是瞎猜的, (7天后证明下面的修改无效). 然后就在要放弃的时候, 我找到了另外一篇文章.全靠百度啊. 算是彻底解决了USB发送端的问题.

找到 PCD_WriteEmptyTxFifo函数. 找不到就全局搜索喽…我的是在stm32f4xx_hal_pcd.c文件中.

/*** @brief  Check FIFO for the next packet to be loaded.* @param  hpcd PCD handle* @param  epnum endpoint number* @retval HAL status*/
static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum)
{USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;uint32_t USBx_BASE = (uint32_t)USBx;USB_OTG_EPTypeDef *ep;uint32_t len;uint32_t len32b;uint32_t fifoemptymsk;ep = &hpcd->IN_ep[epnum];if (ep->xfer_count > ep->xfer_len){return HAL_ERROR;}len = ep->xfer_len - ep->xfer_count;if (len > ep->maxpacket){len = ep->maxpacket;}len32b = (len + 3U) / 4U;while (((USBx_INEP(epnum)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= len32b) &&(ep->xfer_count < ep->xfer_len) && (ep->xfer_len != 0U)){/* Write the FIFO */len = ep->xfer_len - ep->xfer_count;if (len > ep->maxpacket){len = ep->maxpacket;}len32b = (len + 3U) / 4U;(void)USB_WritePacket(USBx, ep->xfer_buff, (uint8_t)epnum, (uint16_t)len,(uint8_t)hpcd->Init.dma_enable);ep->xfer_buff  += len;ep->xfer_count += len;
/**加入这一行代码解决发送慢的问题 start 事实证明没啥用*/if (ep->xfer_len <= ep->xfer_count){fifoemptymsk = (uint32_t)(0x1UL << (epnum & EP_ADDR_MSK));USBx_DEVICE->DIEPEMPMSK &= ~fifoemptymsk;break;}
/**加入这一行代码解决发送慢的问题,  end 事实证明没啥用*/}
/*上面加入的代码其实跟下面这句话是一样的. 我纯粹是瞎猜的.*/if (ep->xfer_len <= ep->xfer_count){fifoemptymsk = (uint32_t)(0x1UL << (epnum & EP_ADDR_MSK));USBx_DEVICE->DIEPEMPMSK &= ~fifoemptymsk;}return HAL_OK;
}

真正解决第二个问题的是自己写了一个函数(百度来的).


/*全速的USB VCP一次最多可以发送64个字节,高速的USB VCP 一次可以最多发送512个字节,* 如果一次发送整个数据包的数据,需要再发送一个数据个数为0的数据包,否则上位机可能没法接收数据。
* USB发送数据主要是调用下面的函数:
* 根据上面USB发送数据包的要求,我们使用下面的函数来调用上面的函数来发送任意大小的数据,这个函数会自动在发送最大数据包后再发送一个空的数据包。
* USB_PACK_SIZE根据USB模式可能为64或者512
* 可发送任意字节的数据到PC端* *///建议使用 直接使用 USB2PC 发送数据到 PC
uint8_t _UsbSendData(uint8_t *pbuff,uint16_t buffsize)
{uint16_t retry = 0;USBD_HandleTypeDef *pdev = &hUsbDeviceHS;USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;hcdc->TxBuffer = pbuff;hcdc->TxLength = buffsize;if (pdev->pClassData != NULL) {if (hcdc->TxState == 0) {/* Tx Transfer in progress */hcdc->TxState = 1;/* Transmit next packet */USBD_StatusTypeDef usb_status = USBD_LL_Transmit(pdev, CDC_IN_EP,hcdc->TxBuffer, hcdc->TxLength);//return usb_status;} else {return USBD_BUSY;}}//等待发送结束while (hcdc->TxState == 1) {retry++;if (retry == 0xfff0) {return USBD_FAIL;}}return USBD_OK;
}#define USB_PACK_SIZE 512void USB2PC(uint8_t *str,uint16_t len)
{uint16_t j = 0;if(len < USB_PACK_SIZE){_UsbSendData(str,len);}else{for (j = 0;j < len/USB_PACK_SIZE;j++){_UsbSendData((str+USB_PACK_SIZE*j),USB_PACK_SIZE);}_UsbSendData((str+USB_PACK_SIZE*j),len%USB_PACK_SIZE);}
}uint16_t TestADC_Values[1];
void main()
{........//测试用变量, 逐个递增.测试上位机收到的数据有没有丢失的现象uint16_t v = 0; while (1){v++;TestADC_Values[0] = v; //int size = sizeof(TestADC_Values);USB2PC(&TestADC_Values, size) ;}
}

上位机主要代码 ,上位机用的是C# 和 LibUsbDotNet 接收USB数据的. 至于如何使用LibUsbDotNet. 我就不详细讲解了. 这篇文章主旨是解决问题, 不是长篇大论 .

 ............if (MyUsbDevice != null){writer = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);reader = MyUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);reader.ReadBufferSize = 2;reader.DataReceived += (onDataReceived);reader.DataReceivedEnabled = true;}............List<int> datas = new List<int>(); //这里只接收数据private void onDataReceived(object sender, EndpointDataEventArgs e){var helper = new ByteHelper(e.Buffer, e.Count); while (helper.HasNextInt16()){datas.Add(helper.ReadUint16()); }return;}//然后加个定时器1秒计算一次看看是否有丢失数据的情况//判断逻辑,最后一个数字是(最大的)减去第一个数字(最小的),的差应该等于收到的数字个数. 也就是说数字是一个一个递增过来的, 中间没有丢失过数据.private void timer1_Tick(object sender, EventArgs e){ var temp = this.datas;this.datas = new List<int>(); this.label1.Text = this.dataCount.ToString();if (temp.Count > 1){          var maxcha = temp.Last() - temp.First(); this.label2.Text = maxcha == temp.Count - 1 ? "未丢失数据" : "丢失数据" + (maxcha - temp.Count - 1) + "最大值为:" + temp.Max(); //事实证明最大值达到65535 会溢出一次识别为丢失数据.其它正常 也就是未丢失数据.   }this.dataCount = 0;}

之前用Nlog 写日志到文件里面发现数字总是断断续续的不连续, 我都快要怀疑人生了. 后来怀疑可能是Nlog的问题, 然后就将数据直接写到内存里.每秒判断一次是否丢失过数据. 事实证明最大值达到65535 会溢出一次识别为丢失数据.其它正常 也就是未丢失数据.证明 USB2PC 这个函数是稳定的,可靠的, 而且是不卡的. 比官方HAL库提供的CDC_Transmit_HS 要稳定的多. 也简单高效的多. 看来HAL还不是很成熟啊. 毕竟刚开始,有点BUG也能理解. 还希望早日弥补这个bug… 不要浪费更多人的时间. 感谢USB2PC 的作者. 参考连接
http://www.stmcu.org.cn/module/forum/forum.php?mod=viewthread&tid=609496&highlight=usb%2B%E9%80%9F%E5%B%20…

ByteHelper.cs 主要代码

public class ByteHelper{public int Index = 0;private byte[] buffer;private int dataLength = 0;public ByteHelper(  byte[] buffer,int datalength){this.buffer = buffer;this.dataLength = datalength;Index = 0;}public byte ReadByte(){var b = buffer[Index];Index = Index + 1;return b;}public UInt16  ReadUint16( ){ var b =  BitConverter.ToUInt16(buffer, Index);Index = Index + 2;return b;}public Int16 ReadInt16(){var b = BitConverter.ToInt16(buffer, Index);Index = Index + 2;return b;}public UInt32 ReadUint32( ){ var b = BitConverter.ToUInt32(buffer, Index);Index = Index + 4;return b;}public int ReadInt32(){var b = BitConverter.ToInt32(buffer, Index);Index = Index + 4;return b;}public Int64 ReadInt64(){var b = BitConverter.ToInt64(buffer, Index);Index = Index + 8;return b;}public UInt64 ReadUInt64(){var b = BitConverter.ToUInt64(buffer, Index);Index = Index + 8;return b;}public bool HasNextShort(){return Index + 1 <= dataLength;}public bool HasNextInt16(){return Index + 2 <= dataLength;}public bool HasNextByte(){return Index + 1 <= dataLength;}public bool HasNextInt32(){return Index + 4 <= dataLength;}public bool HasNextInt64(){return Index + 8 <= dataLength;}}

STM32CUBEIDE USB下载总是连接不上 总是USBD_BUSY相关推荐

  1. STM32单片机下载程序时,ST-LINK下载器连接不上单片机解决方案

    由于烧写程序等原因,导致下载器连接不上单片机,以下方法实测,问题得到解决. 1.硬件连接 ST-LINK复位引脚与单片机复位引脚连接 2.软件设置 选择复位连接 具体接线和设置见图.

  2. 【已解决】(魅族)手机usb调试模式连接不上电脑

    参考:http://blog.csdn.net/caoguangguang/article/details/45894317 http://jingyan.baidu.com/article/ce09 ...

  3. 【日志】罗技鼠标m590 usb或蓝牙连接不上 无法使用

    m590模式为usb连接,插上优联连接器,灯1快速闪烁,无法连接. 解决方法: (1)首先查看蓝牙设备,看是否识别为罗技优联连接器.否则下载Logitech官网固件更新工具. (2)若是仍不可连接使用 ...

  4. android连不上电脑,安卓手机USB数据线连接不上电脑的最佳解决方法

    许多用户都喜欢将手机连接电脑来解决一些问题或者上传一些图片到电脑中存储,不过近日有使用安卓手机的用户在将usb数据线连接电脑的时候,发现无法连接,导致无法上传手机照片图片到电脑上和传送apk手机软件到 ...

  5. 手机usb口连接不上计算机,为什么手机USB连接不上电脑

    刚买的新手机,手机USB为什么不能正常连接到电脑上呢?如何解决手机usb连不上电脑的问题呢? 手机USB连接不上电脑的解决方法 硬件问题 第一步:检查手机数据线是否正常,可以拿其他人的手机进行检查,千 ...

  6. 华为nova5pro计算机,华为nova5Pro用usb数据线连接电脑的时候连接不上

    华为nova5Pro手机在用这个usb数据线连接电脑的时候连接不上,连接不成功,在电脑下载了华为手机助手,手机上也去华为市场下载了手机助手,按照提示操作依然连不上,看不到华为手机的盘符,打开设备管理器 ...

  7. Arduino环境下ESP32另外的下载方式(用USB转TTL连接TX0和RX0下载)

    Arduino ESP32另外的下载方式(用USB转TTL连接TX和RX下载) 今天意外摸索出了另一种下载方式,感觉非常有用,特此记录: 正常的下载方式大概就是把数据线插上,安装好驱动,理论上来说就可 ...

  8. 手机usb口连接不上计算机,手机连接不上电脑,教您手机USB连接不上电脑怎么办...

    在生活中,把手机和电脑用usb连接后可以传送信息,十分方便.但在使用手机连接电脑的过程中,常常会产生手机usb调试已经打开但却连接不上电脑的情况,这无疑会给用户造成麻烦,为此,小编就给大家带来处理手机 ...

  9. android usb win7,安卓手机USB数据线连接不上win7系统电脑的解决方法

    许多用户都喜欢将手机连接电脑来解决一些问题或者上传一些图片到电脑中存储,不过近日有使用安卓手机的用户在将usb数据线连接电脑的时候,发现无法连接,导致无法上传手机照片图片到电脑上和传送apk手机软件到 ...

  10. android usb连接不到手机,为什么手机USB连接不上电脑

    刚买的新手机,手机USB为什么不能正常连接到电脑上呢?如何解决手机usb连不上电脑的问题呢? 手机USB连接不上电脑的解决方法 硬件问题 第一步:检查手机数据线是否正常,可以拿其他人的手机进行检查,千 ...

最新文章

  1. java电台数据解析_Java数据解析---SAX
  2. Oracle中判断空游标的方法
  3. tim计时器读出为0_高中物理 必修1 (12) 第一章 运动的描述 课时4 实验:用打点计时器测速度(1)...
  4. 改变select元素原来的事件属性,并加快捷键
  5. ImportError: No module named 'keras.utils.visualize_util'
  6. RxHttp 一条链发送请求之强大的Param类(三)
  7. MyCat双机HA高可用集群搭建_Keepalived安装和配置---MyCat分布式数据库集群架构工作笔记0029
  8. 重新认识fprint,sprint
  9. 前后端分离式分布式微服务架构项目 学成在线开发项目 源码 视频 文档 工具 合集百度云下载地址
  10. bootbox.js中confirm()方法的使用
  11. 笔记:《高效能人士的七个习惯》第十一章 再论由内而外造就自己
  12. sklearn.metrics.multilabel_confusion_matrix
  13. BDBR和BD-PSNR
  14. Win10怎么安装Apache服务
  15. 怎样修改图片的大小和格式
  16. 从《道德经》看项目经理修炼的7条法则,你做到了几条?
  17. about a day and a story
  18. python画五环图_对Python安装及绘制五环图的初步认识,初识,pythonpython,与
  19. cf_332b - Maximum Absurdity
  20. 武汉纺织大学计算机科学校区在哪,武汉纺织大学是一本吗 重点专业是什么 有几个校区及校区地址...

热门文章

  1. pscc显示无法访问adobe服务器,Adobe Photoshop 提示无法加载扩展,因为它未经正确签署错误提示解决方案...
  2. Dorado7常用JS以及常见错误
  3. 【专题5: 硬件设计】 之 【62.案例四:简易空气净化器,完整原理图】
  4. 英语单词-每天10个
  5. 写好数学建模竞赛论文的那些事
  6. 图解JanusGraph内部数据存储结构
  7. Mac连接路由器后没有反应_路由器安全技术——黑白名设置三步法
  8. 日本向日葵8号卫星数据下载
  9. 用友U8打开起初采购入库单报错
  10. dns服务器功能作用,DNS服务的用途介绍