STM32F4自定义USB协议高速数据传输,支持Windows,Linux,Max,Android



综述:
本文链接:http://www.embed-net.com/thread-565-1-1.html
本方案测试板购买链接:https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-15987418095.3.57dbe938EmY30m&id=545721383347

基于USB数据传输有非常多的上层协议,每种协议各有优缺点,本文介绍一种自定义USB协议实现的方式。
何为自定义USB协议,简单的说,就是实现最基本的USB双向数据通信,USB通信都是基于端点的,本文除了使用到端点0进行枚举之外,我们使用了两个端点,一个用于将数据从PC传输到单片机,一个用于将数据从单片机传输到PC。
PC端程序主要使用libusb来实现,为了方便使用使用,降低USB程序开发难度,我将libusb进行了再次封装,封装的函数如下所示:

[C] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

int USBScanDevice(int usb_pid,int usb_vid);

bool USBOpenDevice(int DevIndex);

bool USBCloseDevice(int DevIndex);

bool USBCheckDevice(int DevIndex);

 

bool USBBulkWriteData(int DevIndex,int pipenum,char *sendbuffer,int len,int waittime);

int USBBulkReadData(int DevIndex,int pipenum,char *readbuffer,int len,int waittime);

bool USBIntWriteData(int DevIndex,int pipenum,char *sendbuffer,int len,int waittime);

int USBIntReadData(int DevIndex,int pipenum,char *readbuffer,int len,int waittime);

bool USBCtrlData(int DevIndex,int requesttype,int request,int value, int index, char *bytes, int size,int waittime);



在开始传输数据之前,一般需要先调用扫描设备函数,确定设备已经连接,然后调用打开设备函数,激活设备数据传输通道,然后就可以调用USBBulkWriteData,USBBulkReadData,USBIntWriteData,USBIntReadData,USBCtrlData进行数据传输了。


本文提供的固件只需要使用USBBulkWriteData,USBBulkReadData这两个函数即可,USBBulkWriteData函数实现PC向单片机发送数据,USBBulkReadData实现从单片机读取数据。


数据传输:
调用USBBulkWriteData函数后,数据将从OUT端点将数据发送到单片机,单片机接收到USB数据之后将会调用usbd_cdc_DataOut回调函数,然后在该函数里面将USB_ReceivedCount变量设置为接收到的数据字节数,如此,主函数就可以通过判断USB_ReceivedCount的值是否大于0来判断是否接收到了USB数据,接收到的USB数据会自动存放到USB数据接收缓冲区中。

调用USBBulkReadData函数后,主机将发起读数据操作,此时单片机端只需要调用DCD_EP_Tx函数即可将数据返回PC,当单片机将数据成功返回PC之后,usbd_cdc_DataIn函数会被自动调用,然后我们在里面将USB_StatusDataSended变量置1,如此,主函数就可以通过判断该变量是否为1来确定数据是否成功发送到PC端。

示例程序功能:
1,程序首先扫描设备,若设备已经连接,则打开设备;
2,程序发送两个信息给单片机,准备读取的数据包数,每包数据的大小;
3,单片机接收到第2步的两个信息之后,就开始循环发送数据,当然PC端也同步的循环读取数据;
4,数据传输完毕之后,统计数据传输的时间,然后打印输出传输数据的速度。


C/C++版本示例代码:

[C++] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

// USBTransmitTest.cpp : Defines the entry point for the console application.

//

 

#ifdef OS_UNIX

#include <time.h>

#include <sys/time.h>

#include <unistd.h>

#include <stdlib.h>

#else

#include <Windows.h>

#endif

#include <stdio.h>

#include <memory.h>

#include "USBTransmit.h"

 

#define USB_VID     0x0483

#define USB_PID     0x5710

 

int main(int argc, char* argv[])

{

        int DevNum;

        int DataNum = 5000;//单位为包

        int PacketSize = 61*1024;//每次传输的数据字节数,该参数必须和单片机中的参数完全匹配,该参数不能大于或等于64K

        int DataNumIndex = DataNum;

        int ret;

        char *pReadDataBuffer = (char *)malloc(PacketSize);

        char WriteDataBuffer[8];

        //扫描设备连接数,需要初始化

        DevNum = USBScanDevice(USB_PID,USB_VID);

        printf("设备连接数为:%d\n",DevNum);

        //打开设备0

        if(USBOpenDevice(0)){

                printf("打开设备成功!\n");

        }else{

                printf("打开设备失败!\n");

        getchar();

        return 0;

        }

    DataNumIndex = DataNum;

        //告诉设备即将要读取的数据包数

        WriteDataBuffer[0] = DataNum>>24;

        WriteDataBuffer[1] = DataNum>>16;

        WriteDataBuffer[2] = DataNum>>8;

        WriteDataBuffer[3] = DataNum>>0;

    //高速设备每包数据的长度

        WriteDataBuffer[4] = PacketSize>>24;

        WriteDataBuffer[5] = PacketSize>>16;

        WriteDataBuffer[6] = PacketSize>>8;

        WriteDataBuffer[7] = PacketSize>>0;

        ret = USBBulkWriteData(0,EP1_OUT,WriteDataBuffer,8,100);

        if(ret){

                printf("写数据成功!\n");

        }else{

                printf("写数据失败!\n");

        getchar();

        }

    printf("正在测试读数据速度,请稍候...\n");

        //获取起始时间

#ifdef OS_UNIX

    struct timeval StartTime_t,EndTime_t;

    long long  StartTime,EndTime;

    double dfFreq = CLOCKS_PER_SEC;

    gettimeofday(&StartTime_t,NULL);//开始计时

#else

    LARGE_INTEGER litmp;

    LONGLONG StartTime,EndTime;

    double dfFreq;

    QueryPerformanceFrequency(&litmp);//取得高精度运行计数器的频率f,单位是每秒多少次(n/s),

    dfFreq = (double)litmp.QuadPart;

    QueryPerformanceCounter(&litmp);//取得高精度运行计数器的数值

    StartTime = litmp.QuadPart; //开始计时

#endif

        //循环读取数据

        do{

                ret = USBBulkReadData(0,EP1_IN,pReadDataBuffer,PacketSize,100);

                if(ret != PacketSize){

                        break;

                }else{

                        DataNumIndex--;

                }

        }while(DataNumIndex > 0);

        if(DataNumIndex > 0){

                printf("读数据失败!\n");

        getchar();

        }else{

                printf("读数据成功!\n");

        }

        //获取结束时间并打印输出耗时和速度

#ifdef OS_UNIX

    gettimeofday(&EndTime_t,NULL); //终止计时

    StartTime = StartTime_t.tv_sec*1000000+StartTime_t.tv_usec;

    EndTime = EndTime_t.tv_sec*1000000+EndTime_t.tv_usec;

#else

    QueryPerformanceCounter(&litmp);//取得高精度运行计数器的数值

    EndTime = litmp.QuadPart; //终止计时

#endif

        // Print the write data speed information

        printf("-----------------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------------\n");

        printf("读数据字节数: %d MBytes\n",(DataNum-DataNumIndex)*PacketSize/(1024*1024));

        printf("读数据消耗时间: %f s\n",(EndTime-StartTime)/dfFreq);

        printf("读数据速度: %.3f MByte/s\n",(DataNum-DataNumIndex)*PacketSize/((EndTime-StartTime)/dfFreq)/(1024*1024));

        printf("-----------------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------------\n");

        //释放动态分配的内存空间

        free(pReadDataBuffer);

        //关闭设备0

        ret = USBCloseDevice(0);

        if(ret){

                printf("关闭设备成功!\n");

        }else{

                printf("关闭设备失败!\n");

        }

    getchar();

        return 0;

}



C#版本的示例代码:

[C#] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

 

namespace USBTransmitTest

{

    class Program

    {

        static void Main(string[] args)

        {

            Int32 DevNum, ret;

            int USB_VID = 0x0483;

            int USB_PID = 0x5710;

            int DataNum = 5000;//单位为包

            int PacketSize = 61 * 1024;//每次传输的数据字节数,该参数必须和单片机中的参数完全匹配,该参数不能大于或等于64K

            int DataNumIndex = DataNum;

            bool State;

            byte[] WriteDataBuffer = new byte[8];

            byte[] ReadDataBuffer = new byte[PacketSize];

            //扫描查找设备

            DevNum = USBTransmit.USBScanDevice(USB_PID, USB_VID);

            Console.WriteLine("设备连接数为:{0}", DevNum);

            if (DevNum <= 0)

            {

                Console.ReadLine();

                return;

            }

            //打开设备

            State = USBTransmit.USBOpenDevice(0);

            if (State)

            {

                Console.WriteLine("打开设备成功!");

            }

            else

            {

                Console.WriteLine("打开设备失败!");

                Console.ReadLine();

                return;

            }

            //准备发送数据

            DataNumIndex = DataNum;

            //告诉设备即将要读取的数据包数

            WriteDataBuffer[0] = (byte)(DataNum >> 24);

            WriteDataBuffer[1] = (byte)(DataNum >> 16);

            WriteDataBuffer[2] = (byte)(DataNum >> 8);

            WriteDataBuffer[3] = (byte)(DataNum >> 0);

            //高速设备每包数据的长度

            WriteDataBuffer[4] = (byte)(PacketSize >> 24);

            WriteDataBuffer[5] = (byte)(PacketSize >> 16);

            WriteDataBuffer[6] = (byte)(PacketSize >> 8);

            WriteDataBuffer[7] = (byte)(PacketSize >> 0);

            State = USBTransmit.USBBulkWriteData(0, USBTransmit.EP1_OUT, WriteDataBuffer, 8, 100);

            if (State)

            {

                Console.WriteLine("写数据成功!");

            }

            else

            {

                Console.WriteLine("写数据失败!");

                Console.ReadLine();

            }

            Console.WriteLine("正在测试读数据速度,请稍候...");

            //开启读数据时间测试

            Stopwatch sw = new Stopwatch();

            sw.Start();

            //循环读取数据

            do

            {

                ret = USBTransmit.USBBulkReadData(0, USBTransmit.EP1_IN, ReadDataBuffer, PacketSize, 100);

                if (ret != PacketSize)

                {

                    break;

                }

                else

                {

                    DataNumIndex--;

                }

            } while (DataNumIndex > 0);

            //计时结束

            sw.Stop();

            //打印接收数据的时间和速度

            Console.WriteLine("-----------------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------------");

            Console.WriteLine("读数据字节数: {0} MBytes", (DataNum - DataNumIndex) * PacketSize / (1024 * 1024));

            Console.WriteLine("读数据消耗时间: {0:F3} s", sw.ElapsedMilliseconds / 1000.0);

            Console.WriteLine("读数据速度: {0:F3} MByte/s", ((DataNum - DataNumIndex) * PacketSize/(1024 * 1024.0)) / (sw.ElapsedMilliseconds / 1000.0));

            Console.WriteLine("-----------------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------------");

 

            if (DataNumIndex > 0)

            {

                Console.WriteLine("读数据失败!");

                Console.ReadLine();

            }

            else

            {

                Console.WriteLine("读数据成功!");

            }

            //关闭设备

            State = USBTransmit.USBCloseDevice(0);

            if (State)

            {

                Console.WriteLine("关闭设备成功!");

            }

            else

            {

                Console.WriteLine("关闭设备失败!");

            }

            Console.ReadLine();

        }

    }

}



Android版本的示例代码:

[Java] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

package com.usbxyz.usbtransmittest;

 

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

 

import com.usbxyz.usbtransmit.USBTransmit;

 

public class MainActivity extends AppCompatActivity {

    USBTransmit mUSBTransmit;

    TextView textView;

    public class ConnectStateChanged implements USBTransmit.DeviceConnectStateChanged{

        @Override

        public void stateChanged(boolean connected) {

            if(connected){

                Toast.makeText(MainActivity.this, "设备已连接", Toast.LENGTH_SHORT).show();

            }else{

                Toast.makeText(MainActivity.this, "设备断开连接", Toast.LENGTH_SHORT).show();

            }

        }

    }

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        textView =(TextView) findViewById(R.id.textView);

        Button startButton =(Button)findViewById(R.id.button);

        //以下两种方式均可,第二种方式可以实时监测设备连接状态

        //mUSBTransmit = new USBTransmit(this);//不监视设备插拔时间

        mUSBTransmit = new USBTransmit(this,new ConnectStateChanged());//需要监视设备插拔事件

        startButton.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                textView.setText(null);

                int devIndex = 0;

                int DataNum = 5000;//单位为包

                int PacketSize = 16*1024;//每次传输的数据字节数,该参数必须和单片机中的参数完全匹配,该参数不能大于或等于64K

                int DataNumIndex = DataNum;

                int ret;

                boolean State;

                byte[] WriteDataBuffer = new byte[8];

                //扫描设备连接数

                int devNum = mUSBTransmit.usbDevice.USBScanDevice();

                if(devNum <= 0){

                    textView.setText("无设备连接!\n");

                    return;

                }else{

                    textView.append("设备连接数为:"+String.format("%d",devNum)+"\n");

                }

                //打开设备

                if(!mUSBTransmit.usbDevice.USBOpenDevice(devIndex)){

                    textView.append("打开设备失败!\n");

                    return;

                }else{

                    textView.append("打开设备成功!\n");

                }

                //准备发送数据

                DataNumIndex = DataNum;

                //告诉设备即将要读取的数据包数

                WriteDataBuffer[0] = (byte)(DataNum >> 24);

                WriteDataBuffer[1] = (byte)(DataNum >> 16);

                WriteDataBuffer[2] = (byte)(DataNum >> 8);

                WriteDataBuffer[3] = (byte)(DataNum >> 0);

                //高速设备每包数据的长度

                WriteDataBuffer[4] = (byte)(PacketSize >> 24);

                WriteDataBuffer[5] = (byte)(PacketSize >> 16);

                WriteDataBuffer[6] = (byte)(PacketSize >> 8);

                WriteDataBuffer[7] = (byte)(PacketSize >> 0);

                State = mUSBTransmit.usbDevice.USBBulkWriteData(devIndex, mUSBTransmit.EP1_OUT, WriteDataBuffer,8,100);

                if(State){

                    textView.append("写数据成功!\n");

                }else{

                    textView.append("写数据失败!\n");

                    return;

                }

                textView.append("正在测试读数据速度,请稍候...\n");

                textView.invalidate();

                //循环读取数据

                byte[] ReadDataBuffer = new byte[PacketSize];

                long startTime = System.nanoTime();  //開始時間

                do{

                    ret = mUSBTransmit.usbDevice.USBBulkReadData(devIndex, mUSBTransmit.EP1_IN, ReadDataBuffer, PacketSize,1000);

                    if(ret != PacketSize){

                        break;

                    }else{

                        DataNumIndex--;

                    }

                }while(DataNumIndex > 0);

                long consumingTime = (System.nanoTime() - startTime)/(1000*1000); //消耗時間

                System.out.println("consumingTime = "+consumingTime+"ms");

                // Print the write data speed information

                textView.append("-----------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------\n");

                textView.append(String.format("读数据字节数: %d MBytes\n", (DataNum - DataNumIndex) * PacketSize / (1024 * 1024)));

                textView.append(String.format("读数据消耗时间: %f s\n", consumingTime/1000.0));

                textView.append(String.format("读数据速度: %.3f MByte/s\n", (DataNum - DataNumIndex) * PacketSize / (consumingTime/1000.0) / (1024 * 1024.0)));

                textView.append("------------------[url=http://www.embed-net.com]www.embed-net.com[/url]-----------------\n");

                if(DataNumIndex > 0){

                    textView.append("读数据失败!\n");

                } else {

                    textView.append("读数据成功!\n");

                }

            }

        });

    }

}




测试效果如下所示:
 

猜你喜欢

转载自blog.csdn.net/manshq163com/article/details/83183428
今日推荐