本帖最后由 creep 于 2017-10-17 14:31 编辑
STM32F769 Discovery (STM32官网)板载有一个 USB OTG HS 接口和一个USB 高速PHY USB3320C-EZK,二者配合可以实现高速的虚拟串口和PC或者其他设备进行通信,下面我们就简单的看下高速的VCP的移植和通信速度,并和其他的板子上的全速VCP进行对比。
1、硬件
USB OTG HS 的外部PHY为USB3320C,和stm32769硬件接口如下,
USB3320C的时钟使用板载的24M晶振,并输出60MHZ的时钟给USB 高速模块,这个全速USB设备的48MHZ时钟有些区别。
移植的时候需要根据STM32F769 Discovery 的原理图确认具体是哪些引脚和USB3320C进行通信。
2、软件移植
ST官方提供了完善的USB库,移植非常简单,只需要根据具体的硬件设置要初始化的引脚即可,此处一定要注意必须根据原理图连接进行初始化化,不同的ST开发板的接法可能不同:
- /* Configure USB HS GPIOs */
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __HAL_RCC_GPIOB_CLK_ENABLE();
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_GPIOH_CLK_ENABLE();
- __HAL_RCC_GPIOI_CLK_ENABLE();
-
- /* CLK */
- GPIO_InitStruct.Pin = GPIO_PIN_5;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- /* D0 */
- GPIO_InitStruct.Pin = GPIO_PIN_3;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- /* D1 D2 D3 D4 D5 D6 D7 */
- GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5 |\
- GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- /* STP */
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- /* NXT */
- GPIO_InitStruct.Pin = GPIO_PIN_4;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
-
- /* DIR */
- GPIO_InitStruct.Pin = GPIO_PIN_11;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
- HAL_GPIO_Init(GPIOI, &GPIO_InitStruct)
复制代码 然后在编译器的预处理定义中选择高速外设:
最后在main函数中初始化USB即可:
- int main(void)
- {
- uint8_t TestBuff[512];
- uint16_t t;
- /* Enable the CPU Cache */
- CPU_CACHE_Enable();
- HAL_Init();
- /* Configure the system clock to 216 MHz */
- SystemClock_Config();
- USART1_Init();
- for(t = 0;t < 512;t++)TestBuff[t] = t;
- /* Init Device Library */
- USBD_Init(&USBD_Device, &VCP_Desc, 0);
- /* Add Supported Class */
- USBD_RegisterClass(&USBD_Device, USBD_CDC_CLASS);
- /* Add CDC Interface Class */
- USBD_CDC_RegisterInterface(&USBD_Device, &USBD_CDC_fops);
- /* Start Device Process */
- USBD_Start(&USBD_Device);
- /* Infinite loop */
- while (1)
- {
- USB2PC(TestBuff,512);
- }
- }
复制代码 下载程序到板子后应该就可以在电脑设备管理器看到虚拟串口
如果是第一次使用VCP就要安装官方的驱动,有的电脑可能不是正版系统会导致安装驱动失败,解决办法可以参考这个帖子:
虚拟串口驱动安装失败的解决方法
基本上常见的串口驱动问题都可以解决,如果驱动安装成功后还提示设备有问题,可以尝试将代码的堆栈修改大一些试下。
3、数据传输
全速的USB VCP一次最多可以发送64个字节,高速的USB VCP 一次可以最多发送512个字节,如果一次发送整个数据包的数据,需要再发送一个数据个数为0的数据包,否则上位机可能没法接收数据。 USB发送数据主要是调用下面的函数:
- //发送数据PC
- uint8_t UsbSendData(uint8_t *pbuff,uint16_t buffsize)
- {
- uint16_t retry = 0;
- USBD_HandleTypeDef *pdev = &USBD_Device;
- 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_LL_Transmit(pdev,
- CDC_IN_EP,
- hcdc->TxBuffer,
- hcdc->TxLength);
-
- }
- else
- {
- return USBD_BUSY;
- }
- }
- //等待发送结束
- while(hcdc->TxState == 1)
- {
- retry++;
- if(retry == 0xfff0)
- {
- return USBD_FAIL;
- }
- }
- return USBD_OK;
- }
复制代码 根据上面USB发送数据包的要求,我们使用下面的函数来调用上面的函数来发送任意大小的数据,这个函数会自动在发送最大数据包后再发送一个空的数据包。USB_PACK_SIZE根据USB模式可能为64或者512
- //可发送任意字节的数据到PC端
- void 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);
- }
- }
复制代码 为了测试发送的最大速度,我在main函数中循环调用发送函数一次发送512字节,发送内容为0X00-0XFF.
因为发送数据量比较大,有些串口助手可能会卡死,我使用SecureCRT 进行接收.接收数据如下,因为SecureCRT显示的ASCII,所以有些显示是乱码,但是数据是对的,
使用监控软件看到发送数据如下,可以每个数据包为512字节,每次发送的内容为0x00-0xFF.数据没有出错。
具体的发送速度如下,多次测试可以看到软件计算的结果有些不同
为了对比我们测试下STM32F746 Discovery上全速的USB发送速度,
- int main(void)
- {
- uint8_t TestBuff[64];
- uint16_t t;
- CPU_CACHE_Enable();
- HAL_Init();
- SystemClock_Config();
- LED_Init();
- /* Init Device Library */
- USBD_Init(&USBD_Device, &VCP_Desc, 0);
-
- /* Add Supported Class */
- USBD_RegisterClass(&USBD_Device, USBD_CDC_CLASS);
-
- /* Add CDC Interface Class */
- USBD_CDC_RegisterInterface(&USBD_Device, &USBD_CDC_fops);
-
- /* Start Device Process */
- USBD_Start(&USBD_Device);
- for(t = 0;t < 64;t++)TestBuff[t] = t;
- while(1)
- {
- USB2PC(TestBuff,64);
- }
- }
复制代码
SecureCRT看到的数据
监控软件检测到的数据,数据包大小为64字节
检测到发送速度
全速USB的理论最大速度为12Mbps,高速USB的为480Mpbs,明显上面测的数据和理论的最大数度有些差距。不同的检测软件可能测试得到的数据不同,通过对比可以看到高速USB VCP和全速USB VCP还是有些速度提升的。测试代码使用keil5.20编译,代码没有进行任何优化。USB库是2016的比较新的版本。感兴趣的同学可以使用优化代码或者其他编译器编译代码试下是不是速度有所提升。
我在测试高速USB VCP的时候因为发送数据量较大,在我电脑上win10安装之后的首次蓝屏,赶紧拍照留念,微软迟早药丸。
测试代码;
STM32F746 Discovery VCP-FS.rar
(951.89 KB, 下载次数: 275)
|
我是根据你“ STM32F769 Discovery VCP-HS.rar ’’的例子改的不知道为什么。
STM32用26M晶振时钟配置如下:
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
HAL_StatusTypeDef ret = HAL_OK;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 26;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
RCC_OscInitStruct.PLL.PLLR = 7;
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret != HAL_OK)
{
while(1) { ; }
}
/* Activate the OverDrive to reach the 216 MHz Frequency */
ret = HAL_PWREx_EnableOverDrive();
if(ret != HAL_OK)
{
while(1) { ; }
}
/* Select PLLSAI output as USB clock source */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLLSAIP;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 432;
PeriphClkInitStruct.PLLSAI.PLLSAIQ = 9;
PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV2;
if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
while(1) { ; }
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
if(ret != HAL_OK)
{
while(1) { ; }
}
usb硬件初始化如下:
/* Configure USB HS GPIOs */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
// __HAL_RCC_GPIOH_CLK_ENABLE();
// __HAL_RCC_GPIOI_CLK_ENABLE();
/* CLK */
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* D0 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* D1 D2 D3 D4 D5 D6 D7 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_5 |\
GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* STP */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* NXT */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* DIR */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
__HAL_RCC_USB_OTG_HS_ULPI_CLK_ENABLE();
我的测试环境 F205,120M运行, Host: XP, AMD 2GHz
我测试的结果,在Host不很繁忙的情况下,F205发往Host的速率可达670K字节以上。
测试的方法是:
循环下列动作:
Host 发送一个传输命令,F205立即发送8K字节的数据到Host.
Host 接收到数据后效验。
10000次循环之后累计数据流量。
其中Host 等待和发送命令有一些开销。每次传输的数据块愈长,这些开销占比例愈小。
那么,在Full speed 下USB Bulk 传输的最大速度是多少呢?
这取决于Host 发出 In 令牌的速率,据我测试 In 令牌大约是15-16个/ms
核算出速率约为 1M Byte/s; 这应该是极限速率。
大神,让您见笑了。
我就是顺便对比下。。。
给有需要的人或者想知道差别的同学一个基本的参考。
微软大法好,升级了win10后,好多年的旧笔记本又满血复活了。
老早之前弄过双缓冲没有搞好,现在的速度用在产品上已经够用了所以就没有折腾,如果您有双缓冲参考代码的话可以分享下。
感谢打赏