移植 VPC 到 STM32 平台

VCP 是虚拟串口( Virtual COM Port Driver)的简称。通过它可以种将 USB 抽象当做串口使用,使主控端的实现非常简单,但是速度又是 USB 的速度。

环境搭建

硬件平台

软件平台

库版本

问题:

参考:

新建工程

新建本地文件夹目录结构:

最终本地目录树如下:

├─BSP                     //STM32F10x_StdPeriph_Lib_V3.5.0/Libraries
│  ├─CMSIS
│  │  ├─CM3
│  │  │  ├─CoreSupport
│  │  │  └─DeviceSupport
│  │  │      └─ST
│  │  │          └─STM32F10x
│  │  │              └─startup
│  │  │                  ├─arm
│  │  │                  ├─gcc_ride7
│  │  │                  ├─iar
│  │  │                  └─TrueSTUDIO
│  │  └─Documentation
│  └─STM32F10x_StdPeriph_Driver
│      ├─inc
│      └─src
├─LISTING                                  //lst文件
├─OUTPUT                          //输出文件,如BIN或者HEX
├─USBLib                          //STM32_USB-FS-Device_Lib_V4.0.0/Libraries
│  ├─inc
│  └─src
└─USER                //STM32_USB-FS-Device_Lib_V4.0.0\Projects\Virtual_COM_Port
    ├─inc
    └─src

USER根目录新建工程

打开MDK,Create New Project

选择芯片

STM32F103RC

无需将启动文件添加工程,稍后手动添加,以防止版本不对

startup_stm32f10x_hd.s

右键工程名,组织工程的目录结构,将工程目录与本地一一对应

Components, Enviroment and Books

最终工程目录树

Project Tree

点击小魔术棒设置Output

Options for Output

设置Listings

Options for Listings

设置预处理宏和头文件路径,还有一定要加上--c99这样就可以随处定义变量了!!!

Options for C/C++ Header

Options for C/C++

选择调试器和设置Flash的大小,勾选Reset and Run,这样每次下载完程序便可以自动运行,而不是手动复位一次再运行

Flash Download

移植代码

在之前,放在USER中的代码要么与平台相关,要么是可以修改的配置文件,需要针对特定情况进行修改。

先删除掉一些不需要的文件,由于目前针对STM32F1平台,故可以将F2,F3和L1平台的文件删除,如stm32f30x_conf.h,stm32l1xx_conf.h ,stm32f37x_conf.h ,system_stm32f30x.c,system_stm32l1xx.c , system_stm32f37x.c

删除完之后剩下如下文件,大致可以分成 USB 配置相关和平台硬件相关

平台硬件相关

USB 配置相关

:不管Host还是Device的输入和输出均是站在Host的角度描述。

主要工作分为4部分:定义USB 控制脚引脚 USB_DISCONNECT,删除针对原评估板的USART相关代码,关掉USB进入低功耗相关和设置发送和接收实体。

1.注释原评估板的USE_DISCONNECT的GPIO定义,重新定义为开发板上的,如PA10:

#define USB_DISCONNECT                      GPIOA
#define USB_DISCONNECT_PIN                  GPIO_Pin_10
#define RCC_APB2Periph_GPIO_DISCONNECT      RCC_APB2Periph_GPIOA

注:每一款STM32F10x的USBDM为PA11,USBDP为PA12,CTRL为自行设定。

2.删除USART相关的有所有定义个调用,只要与以下函数的相关的初始化,定义和调用均注释掉

void USART_Config_Default(void);
bool USART_Config(void);
void USB_To_USART_Send_Data(uint8_t* data_buffer, uint8_t Nb_bytes);
void USART_To_USB_Send_Data(void);

3.注释掉Suspend函数中的所有内容,关掉睡眠


4.在EP3_OUT_Callback回调接收函数中设置缓存接收数据和发送数据,以下两种方式均可以。

static uint8_t USB_Tx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];

              /* style1 : */
//               while(GetEPTxStatus(ENDP1) != EP_TX_NAK);
//               {
//                   memset(USB_Tx_Buffer,_inc++,VIRTUAL_COM_PORT_DATA_SIZE);
//                   USB_Tx_Buffer[31] = 0xAA;
//                   USB_Tx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE - 10] = 0xBB;
//                   USB_SIL_Write(EP1_IN, USB_Tx_Buffer, VIRTUAL_COM_PORT_DATA_SIZE);
//                   SetEPTxValid(ENDP1);
//               }
//
//               while(GetEPTxStatus(ENDP1) != EP_TX_NAK);
//               {
//                   USB_SIL_Write(EP1_IN, USB_Tx_Buffer, 0);
//                   SetEPTxValid(ENDP1);
//               }

            /* style2 : http://bbs.21ic.com/icview-208240-1-1.html */
            while(GetEPTxStatus(ENDP1) != EP_TX_NAK);
            {
            memset(USB_Tx_Buffer,_inc++,VIRTUAL_COM_PORT_DATA_SIZE);
            USB_Tx_Buffer[3] = 2*_inc;
            UserToPMABufferCopy(USB_Tx_Buffer, ENDP1_TXADDR, VIRTUAL_COM_PORT_DATA_SIZE);
            SetEPTxCount(ENDP1,VIRTUAL_COM_PORT_DATA_SIZE);
            SetEPTxValid(ENDP1);
            }
            // over flag
            while(GetEPTxStatus(ENDP1) != EP_TX_NAK);
            {
            SetEPTxCount(ENDP1,0);
            SetEPTxValid(ENDP1);
            }

以下为对接收到数据进行解析,如果第一个数据位0x03,则回复相对应的数据,完成类似echo的过程,以下是Bus Hound的显示过程。

bus hound

注:USB会在以下三种情况下将bulk方式的数据发出:第一种是其长度小于64;另一种为发送完64长度数据之后发送一帧0长度的数据。以上为后一种。还有就是等驱动的缓冲区满,如2k的数据满了之后再发出。

枚举分析

图解USB总线枚举过程

Paste_Image.png
1. 检测到USB设备后,对USB设备复位,使设备地址变为0x0。发80 06 01 00 00 40 00命令,读取设备描述符命令,由于不知道设备描述符的长度,暂时要求返回数据长度为0x40。
Paste_Image.png
2. 给这个新接上的设备分配地址。
Paste_Image.png
3. 设置地址成功后,对新地址发送获取设备描述符命令,此时已经知道了它的长度,直接按这个长度即可。
Paste_Image.png
4. 在得到设备描述符后,我们再发获取配置描述符命令。
Paste_Image.png
5. 从上一步,我们可以得到设备支持的接口数及端点数,此时再发一次得到配置描述符命令,把数据长度改大,数据长度我们可以从wTotallLength中读取,但我们一般在这里设为0xFF。
Paste_Image.png
6. 如果有字符串描述符,在这里可以发命令读取。接下来再一次发命令完整读取设备描述符和配置描述符
Paste_Image.png
7. 在这里发送设置配置命令,到此,我们已经完整地得到了设备的信息。枚举过程结束。
Paste_Image.png