首页 > 其他 > 详细

抛砖引玉:一步一步Hal库学习usb虚拟串口功能CDC或者叫VCP

时间:2019-05-13 22:45:58      阅读:148      评论:0      收藏:0      [点我收藏+]

抛砖引玉:一步一步Hal库学习usb虚拟串口功能CDC或者叫VCP

首先你要能调的通stm32cubemx生成的USB虚拟串口程序,具体的前置操作看下面的帖子

历经7天时间总算把USB虚拟串口CDC或者叫VCP完整调试结束,每天的调试过程和调试时候的想法都记录在下面,希望对本站用户调试usb或者其他外设有所帮助!

帖子里面的顺序可能不是正常的我发帖的顺序,因为只要有人点赞就会导致该楼层被置顶,所以,大家查阅的时候注意好每层楼的编号!!

同时希望大家也能将你调试外设的过程或者期间的想法分享出来,也许你的一个想法就能帮助好多人!

注意:
-----------------------------------------------------------
禁止转载!

一定要转载,请注明本站出处!!
-----------------------------------------------------------

下面的连接先别点的,听我说完
1.按照图示的stm32cubemx配置,先设置好硬件外设
      [
]usb相关的参数都不要去改!任它默认就行![/
      ][
]生成工程后,会提示是否要用keil打开,切记选择不打开,后面有地方要改[/
      ][
]找到你刚才生成的工程文件夹,在文件夹上面点[i]
      选择[/i]
的钩钩去掉,应用到所有文件[/
      ][
]现在可以去文件夹中找到工程打开了[/
      ][
]打开后,首先别编译,直接打开找到图片中的文件,打开后,将Heap Size 改为1500[/
    ]
技术分享图片


好了,现在可以去看下面的连接里面的配置了,祝你好运!
http://www.stm32cube.com/question/26

二楼继续!!!!
 
 
这里是二楼!!
额,对了,一楼的程序直接编译后烧写进入片子.
插上usb口,会提示发现硬件,然后再电脑的****里面能看到

技术分享图片


看到这里就说明你这个VCP成功了...现在就是缺个驱动安装了.
安装驱动要分32位电脑和64位电脑,他们的驱动不同,看看下面的连接进行选择
http://www.stm32cube.com/article/40
切记:下载的压缩包里面有一个实用文档,一步一步来一定会成功的!
成功之后就是下面的图片
!(http://www.stm32cube.com/uploads/article/20141216/dc54173806fb51163f0c9955762aa4b5.png)
技术分享图片

 
 
这里是三楼!!
上面的如果实现了,那么恭喜你,可以进行下步了!
这个帖子的目的是研究USB虚拟串口,所以帖子的相关程序不适用于实际使用!
===================================================
二楼的程序是根据前篇我转载的一个国外的VCP的帖子来的,声明不是原创!

实验目的:串口发送到片子数据,片子返回你发送的这些数据

打开工程文件中的usbd_cdc_if.c
这个文件英文名应该是interface,所以是VCP的接口函数定义文件,大部分函数设置都在这里进行
首先:定义一个结构体,存放接收到的数据相关参数

static struct
{
	uint8_t Buffer;
	int Position, Size;
	char ReadDone;
} s_RxBuffer;

第一个是数据缓存,第二个是读写位置和长度,第三个是是否可读标志
在定义一个初始化标志,用来说明初始化完成

char g_VCPInitialized;	

我们来初始化CDC,因为是接收在直接返回的,所以只需初始化接收部分
找到static int8_t CDC_Init_FS(void)这个函数,将里面修改为

static int8_t CDC_Init_FS(void)
{
  hUsbDevice_0 = &hUsbDeviceFS;
  /[i] USER CODE BEGIN 3 [/i]/ 
  /[i] Set Application Buffers [/i]/
//  USBD_CDC_SetTxBuffer(hUsbDevice_0, UserTxBufferFS, 0);
  USBD_CDC_SetRxBuffer(hUsbDevice_0, s_RxBuffer.Buffer);
	g_VCPInitialized = 1;
  return (USBD_OK);
  /[i] USER CODE END 3 [/i]/ 
}

以上是设置USB_CDC的接收缓存为s_RxBuffer.Buffer
初始化结束后,接下来对接收函数进行改造!

static int8_t CDC_Receive_FS (uint8_t[i] Buf, uint32_t [/i]Len)
{
  /[i] USER CODE BEGIN 6 [/i]/
	s_RxBuffer.Position = 0;
	s_RxBuffer.Size = *Len;
	s_RxBuffer.ReadDone = 1;
  return (USBD_OK);
  /[i] USER CODE END 6 [/i]/ 
}

上面的意思是,接收到一帧数据就将s_RxBuffer这个数据相关的读写位置复位,长度记录,数据可读标志置位

以上,若发送数据下来,那么数据会存储到缓存buffer,并且提醒片子可读了

以下继续构造两个函数,供main中大循环调用

int VCP_read(void *pBuffer, int size)
{
	if (!s_RxBuffer.ReadDone)
			return 0;

	int remaining = s_RxBuffer.Size - s_RxBuffer.Position;
	int todo = MIN(remaining, size);
	if (todo <= 0)
			return 0;

	memcpy(pBuffer, s_RxBuffer.Buffer + s_RxBuffer.Position, todo);
	s_RxBuffer.Position += todo;
	if (s_RxBuffer.Position >= s_RxBuffer.Size)
	{
			s_RxBuffer.ReadDone = 0;
			USBD_CDC_ReceivePacket(&hUsbDeviceFS);
	}

return todo;
}

int VCP_write(const void *pBuffer, int size)
{
	if (size > CDC_DATA_FS_OUT_PACKET_SIZE)
	{
			int offset;
			for (offset = 0; offset < size; offset++)
			{
					int todo = MIN(CDC_DATA_FS_OUT_PACKET_SIZE, 
												 size - offset);
					int done = VCP_write(((char *)pBuffer) + offset, todo);
					if (done != todo)
							return offset + done;
			}

			return size;
	}

	USBD_CDC_HandleTypeDef *pCDC = 
					(USBD_CDC_HandleTypeDef *)hUsbDeviceFS.pClassData;
	while(pCDC->TxState) { } //Wait for previous transfer

	USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t *)pBuffer, size);
	if (USBD_CDC_TransmitPacket(&hUsbDeviceFS) != USBD_OK)
			return 0;

	while(pCDC->TxState) { } //Wait until transfer is done
	return size;
}

我先不解释了,先把所有需要改的都写完,然后统一解释吧
打开usbd_cdc_if.h文件
添加

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
int VCP_read(void *pBuffer, int size);
int VCP_write(const void *pBuffer, int size);
extern char g_VCPInitialized;


打开main.c文件:
添加

#include "usbd_cdc_if.h"

大循环中写入

 /[i] USER CODE BEGIN 2 [/i]/
	char byte;
  /[i] USER CODE END 2 [/i]/

  /[i] Infinite loop [/i]/
  /[i] USER CODE BEGIN WHILE [/i]/
  while (1)
  {
  /[i] USER CODE END WHILE [/i]/

  /[i] USER CODE BEGIN 3 [/i]/

		if (VCP_read(&byte, 1) != 1)
				continue;
		VCP_write("\r\nYou typed ", 12);
		VCP_write(&byte, 1);
		VCP_write("\r\n", 2);		
		
  }
  /[i] USER CODE END 3 [/i]/


好了,所有要改造的都改好后,编译,烧写,打开串口
随便发数据,就能看到效果了.

技术分享图片

图上是我按照10进制发送的34567这个数,它会告诉你你发送了哪些数据.

四楼继续解释!!!!
 
这里是四楼!!!
其实我觉得完全么有必要一个一个解释......
先说

int VCP_read(void *pBuffer, int size)
{
	if (!s_RxBuffer.ReadDone)
			return 0;
//没有数据接收,或者没接收完成,就返回0继续查询可读状态标识
	int remaining = s_RxBuffer.Size - s_RxBuffer.Position;
//剩余量=接收到的缓存大小-读取的位置
	int todo = MIN(remaining, size);
//取最小值
	if (todo <= 0)
			return 0;
//如果读取完毕所有数据那么返回0
	memcpy(pBuffer, s_RxBuffer.Buffer + s_RxBuffer.Position, todo);
//复制一个字节数据到*Pbuffer
	s_RxBuffer.Position += todo;
//读取位置加1
	if (s_RxBuffer.Position >= s_RxBuffer.Size)
//如果读完数据
	{
			s_RxBuffer.ReadDone = 0;
//将可读属性清0
			USBD_CDC_ReceivePacket(&hUsbDeviceFS);
//开启下一个接收
	}

return todo;
//返回读取的字节长度
}

然后再看看

int VCP_write(const void *pBuffer, int size)
{
	if (size > CDC_DATA_FS_OUT_PACKET_SIZE)
//若要发送的数据超过FS的最大尺寸64字节
	{
			int offset;
			for (offset = 0; offset < size; offset++)
			{
					int todo = MIN(CDC_DATA_FS_OUT_PACKET_SIZE, 
												 size - offset);
//取得多出来的偏移量
					int done = VCP_write(((char *)pBuffer) + offset, todo);
//先把多出来的偏移量数据长度给发送出去
					if (done != todo)
							return offset + done;
			}

			return size;
	}

	USBD_CDC_HandleTypeDef *pCDC = 
					(USBD_CDC_HandleTypeDef *)hUsbDeviceFS.pClassData;
	while(pCDC->TxState) { } //Wait for previous transfer

	USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t *)pBuffer, size);
	if (USBD_CDC_TransmitPacket(&hUsbDeviceFS) != USBD_OK)
			return 0;

	while(pCDC->TxState) { } //Wait until transfer is done
	return size;
}

再来看看while的吧

		if (VCP_read(&byte, 1) != 1)
				continue;
//有数据可读就继续下面程序,没有就返回while继续查询
		VCP_write("\r\nYou typed ", 12);
//发送You typed 这12个字符到调试助手
		VCP_write(&byte, 1);
//发送数据中的一个字节到助手
		VCP_write("\r\n", 2);
//发送换行和回车这两个字符到助手		

ok解释完了,

五楼继续研究,改造!!
技术分享图片

admin

赞同来自: 春水长天

这里是5楼!!
接上面的程序分析,根据分析来看,如果将上述代码中的发送改成一个数组,一次发送多个应该也是没有问题的,
于是,将

char byte;

改为

char byte;

使byte变量成为一个数组的首地址
这样若是想要发送多个数据,那么while中改为

		if (VCP_read(byte, 3) != 3)
				continue;
		VCP_write("\r\nYou typed ", 12);
		VCP_write(byte, 3);
		VCP_write("\r\n", 2);		

以上代码意思是:
读取usb接收到的数据的前3个给数组byte,此时byte数组中的中就是usb接受到的数据的前三个数据了.
注意,此时若是发送的数据超过了3个,那么串口调试助手返回的数据只是前三个,后面的数据不会显示.
若此时下发的数据少于3个,那么串口调试助手不会被返回任何数据.
技术分享图片

admin

赞同来自: 春水长天

这里是6楼!!
以上的VCP程序,函数vcp_write没有任何问题,因为发送的时候是可以知道发送字节的长度的
但是,接收数据方面用

uint8_t Buffer;

而CDC_DATA_FS_OUT_PACKET_SIZE=64
这样就限制了我们每次接收的数据和发送的数据必须小于64字节
这是其一,
其二,假如我们数据处理的速度跟不上接收的速度,那么数据就会错乱...
于是考虑到利用环形缓存来试试

环形队列的话,usb端口接收的数组不变,每次接收完数据立即取出到外部环形队列中,主程序就根据通信协议特征码进行数据的读取和相应的操作

例如:前面我们用到串口屏的通信程序既是如此!不过那个时候用的是硬件串口,而现在用的事usb虚拟串口

于是,我们先移植串口屏的那个程序出来

7楼继续!
这里是7楼!!
串口屏的那个程序先不移植的...先弄个环形缓冲实验下

为了使用方便去移植了RTos里面的一个环形缓冲代码如下:
新建文件 ringbuffer.c

#include "ringbuffer.h"

/[i] 初始化环形buffer,size指的是buffer的大小 注:这里并没对数据地址对齐做处理 [/i]/
void rb_init(struct rb[i] rb, uint8_t [/i]pool, uint16_t size)
{

    /[i] 对读写指针清零 [/i]/
    rb->read_index = rb->write_index = 0;

    /[i] 设置环形buffer的内存数据块 [/i]/
    rb->buffer_ptr = pool;
    rb->buffer_size = size;
}
extern	uint16_t bl1,bl2,bl3,bl4,bl5;
/[i] 向环形buffer中写入数据 [/i]/
uint8_t rb_put(struct rb[i] rb, const uint8_t [/i]ptr, uint16_t length)
{
    uint16_t size;
	
    /[i] 判断是否有足够的剩余空间 [/i]/
    if (rb->read_index > rb->write_index)
        size = rb->read_index - rb->write_index;
    else
        size = rb->buffer_size - rb->write_index + rb->read_index;

    /[i] 没有多余的空间 [/i]/
    if (size < length) return 0;

    if (rb->read_index > rb->write_index)
    {
        /[i] read_index - write_index 即为总的剩余空间 [/i]/
        memcpy(&rb->buffer_ptr, ptr, length);
        rb->write_index += length;
    }
    else
    {
        if (rb->buffer_size - rb->write_index > length)
        {
            /[i] write_index 后面的剩余空间有足够的长度 [/i]/
            memcpy(&rb->buffer_ptr, ptr, length);
            rb->write_index += length;
        }
        else
        {
            /*
             * write_index 后面剩余的空间不存在足够的长度,需要把部分数据复制到
             * 前面的剩余空间中
             */
            memcpy(&rb->buffer_ptr, ptr,
                   rb->buffer_size - rb->write_index);
            memcpy(&rb->buffer_ptr, &ptr,
                   length - (rb->buffer_size - rb->write_index));
            rb->write_index = length - (rb->buffer_size - rb->write_index);
        }
    }

    return 1;
}

/[i] 从环形buffer中读出数据 [/i]/
uint8_t rb_get(struct rb[i] rb, uint8_t [/i]ptr, uint16_t length)
{
    uint16_t size;

    /[i] 判断是否有足够的数据 [/i]/
    if (rb->read_index > rb->write_index)
        size = rb->buffer_size - rb->read_index + rb->write_index;
    else
        size = rb->write_index - rb->read_index;

    /[i] 没有足够的数据 [/i]/
    if (size < length) return 0;

    if (rb->read_index > rb->write_index)
    {
        if (rb->buffer_size - rb->read_index > length)
        {
            /[i] read_index的数据足够多,直接复制 [/i]/
            memcpy(ptr, &rb->buffer_ptr, length);
            rb->read_index += length;
        }
        else
        {
            /[i] read_index的数据不够,需要分段复制 [/i]/
            memcpy(ptr, &rb->buffer_ptr,
                   rb->buffer_size - rb->read_index);
            memcpy(&ptr, &rb->buffer_ptr,
                   length - rb->buffer_size + rb->read_index);
            rb->read_index = length - rb->buffer_size + rb->read_index;
        }
    }
    else
    {
        /*
         * read_index要比write_index小,总的数据量够(前面已经有总数据量的判断),
         * 直接复制出数据
         */
        memcpy(ptr, &rb->buffer_ptr, length);
        rb->read_index += length;
    }

    return 1;
}

新建文件 ringbuffer.h

#ifndef __RINGBUFFER__H__
#define __RINGBUFFER__H__
#ifdef __cplusplus
 extern "C" {
#endif
/[i] Includes ------------------------------------------------------------------[/i]/
#include "stm32f4xx_hal.h"
#include "usbd_cdc_if.h"

/[i] 环形buffer的内存块(用数组体现出来) [/i]/
#define BUFFER_SIZE        256
#define BUFFER_ITEM        32	 
	 
/[i] 一个环形buffer的实现 [/i]/
struct rb
{
    uint16_t read_index, write_index;
    uint8_t *buffer_ptr;
    uint16_t buffer_size;
};


extern uint8_t working_buffer;
extern struct rb working_rb;

/[i] 初始化环形buffer,size指的是buffer的大小 注:这里并没对数据地址对齐做处理 [/i]/
void rb_init(struct rb[i] rb, uint8_t [/i]pool, uint16_t size);	
/[i] 向环形buffer中写入数据 [/i]/
uint8_t rb_put(struct rb[i] rb, const uint8_t [/i]ptr, uint16_t length);
/[i] 从环形buffer中读出数据 [/i]/
uint8_t rb_get(struct rb[i] rb, uint8_t [/i]ptr, uint16_t length);	
 
#ifdef __cplusplus
}
#endif

#endif //__RINGBUFFER__H__

/**
  * @}
  */ 

/**
  * @}
  */ 
/*******************[i][i][b][/i] (C) COPYRIGHT STMicroelectronics [/b][b][i]END OF FILE[/b][/i][/i]/

将新建的那个c文件添加到工程中.

打开文件 usbd_cdc_if.c
添加包含文件代码

#include "ringbuffer.h"

修改CDC_Receive_FS函数代码

static int8_t CDC_Receive_FS (uint8_t[i] Buf, uint32_t [/i]Len)
{
  /[i] USER CODE BEGIN 6 [/i]/

//--------------------------------------------------------------------	
  /[i] 将数据放入环形buffer中 [/i]/
	rb_put(&working_rb, Buf, *Len);
  //复位OUT端点接收缓冲区(必须有)
	USBD_CDC_ReceivePacket(hUsbDevice_0);
//---------------------------------------------------------------------	
  return (USBD_OK);
  /[i] USER CODE END 6 [/i]/ 
}

其他的内容如楼上所写,不用更改
此时来修改main.c文件
添加包含文件

#include "ringbuffer.h"

添加全局变量供调用

uint8_t working_buffer;
struct rb working_rb;

在usb初始化之后添加环形缓存的初始化函数

rb_init(&working_rb, working_buffer, BUFFER_SIZE);

修改while内容如下:

  while (1)
  {
  /[i] USER CODE END WHILE [/i]/
	
		if(rb_get(&working_rb, byte, 1) != 0)
		{
			VCP_write("\r\nYou typed ", 12);
			VCP_write(byte, 1);
			VCP_write("\r\n", 2);
		HAL_Delay(300);
		}
  }


上面的意思,其实和5楼说的功能是一样的,不过这里利用了环形缓存而已
现象就是,串口调试助手打入什么,usb就返回什么
此时如果用debug调试查看数据,可以看到头尾的变化

注意:
此代码按照上面写的时候,串口调试助手可一次写入小于256个数据,然后数据依次缓慢读出到助手
如果usb正在向串口调试助手输出数据期间,用串口助手下发数据,那么usb调试助手就会死机
所以以上程序还需改进

请看8楼!!
技术分享图片

admin

赞同来自: 春水长天

这里是8楼!!
其实从上面的代码里面的注释就可以看出问题所在.
VCP_write()函数中死等造成的冲突
于是我们删除这个函数,直接利用Hal库提供的函数来发送
于是,我们注释掉usbd_cdc_if.c文件中的函数定义
VCP_write和VCP_read,并删除main.c文件中的此函数
于是改为:

  while (1)
  {
  /[i] USER CODE END WHILE [/i]/
	
		if(rb_get(&working_rb, byte, 1) != 0)
		{
			CDC_Transmit_FS(byte,1);
		        HAL_Delay(300);
		}
  }

调试查看助手的输出,此时,如果usb正在输出,同时下发数据,不会导致串口助手死机,而且上发的数据完全按照我们输入的数据先后输出,即FIFO

通过以上实验,已经初步完成环形缓存的写入,那么写入缓存后,应该怎么使用呢?

通常的做法是定义好数据的协议,通过协议来判断功能
方法一,定义一个定长的数据列,例如20个字节的数据列
如果下发数据长度不足20个字节,自动填充至20字节,然后下发
这样的好处就是:数据处理更加简单,不需要去判断一帧数据的长度,每次使用的时候,只需20个字节取一次,然后按照协议分解数据,判断功能,但是缺点也是明显,当程序既定的时候,缓存大小为固定值, 然而每次需下发的有效数据并不一定需要20字节长度,有时5个字节足够了,那么剩下的15个字节空间就浪费了,这个就是所谓的以空间换时间!
方法二,定义一个协议的头和尾,来判断是否是一帧完整数据
如上面提到的串口屏协议既是如此
它定义了数据 0xEE为数据的头,定义0xFF 0xFC 0xFF 0xFF四个字节为数据尾,当中的数据为有效数据位
这样的好处是,数据协议长度可变,能够稍微充分的利用空间,缺点也很明显,头和尾已经占用了5个字节位置,
每次数据下发每个有效数据前后需添加他们,这样如果是短数据传输,那么显然浪费了很多通信时间

比较两种方式,第一种,假如数据传输过程中丢失了一个数据,那么导致的后果很严重!

因为每次系统启动后从第0个数据开始每次取20个作为一个有效数据,那么丢失其中任何一个字节数据,会导致后面所有的数据错乱!

所以这种方式最不可取!!!

当然,usb虚拟串口是利用 帧数据传输,按道理上时不会丢失里面的一个字节的,丢失的话也是整个数据包丢失,哈

那么第二种方式的优点就体现出来了,即使数据中的某个字节丢失了,那么在程序中我们可以判断,去除这个错误的数据

具体的程序参考9楼
技术分享图片

admin

赞同来自: 春水长天lion187

这里是9楼!!
先来说说楼上说的第一种方式.
这个就比较简单了,主要问题再你每次下发数据的时候记得不能超过定义的协议长度例如20字节.
当下发数据不足20字节,自己需补全20字节,保证下发的数据每帧都是20字节即可.
单片机处理的时候,每次用

if(rb_get(&working_rb, byte, 20) != 0)
    {
//--------------------------------------
//你的协议分解代码
//--------------------------------------
    }

这句来一次读出20字节然后进行分解,得到你想要的协议操作,再去执行相应操作即可.
这里不再对定长协议作分析了,下面为了简单起见示例移植大彩的串口屏的协议程序:
大彩的串口屏分两部分,
一部分是串口接收入栈部分,一部分是出栈部分
入栈部分我们用上面提到的

rb_put();

函数即可.
出栈部分就不能在使用

rb_get();

函数了,需要对出栈函数进行简单的修改.
在 ringbuffer.c文件中添加一个新的函数:

uint8_t rb_get_cmd(struct rb[i] rb, uint8_t [/i]ptr)

定义其内容如下:

/[i] 从环形buffer中读出一帧协议 [/i]/
uint8_t rb_get_cmd(struct rb[i] rb, uint8_t [/i]ptr)
{
    uint8_t result;
		uint8_t buff={0};
		uint16_t cmd_pose = 0;
		uint16_t cmd_state = 0;
		uint16_t cmd_size = 0;
		do
		{
			//取出一字节长度
			result = rb_get(rb,buff,1);
			if(result == 0) return 0;
			if((cmd_pose == 0) && (*buff != 0xEE))
				continue;
			if(cmd_pose < BUFFER_SIZE)
			{
				ptr = *buff;
			}
			if(*buff == 0xFF)
			{
				switch (cmd_state)
				{			
					case 2:cmd_state = 3;break; //FF FC FF ??
					case 3:cmd_state = 4;break; //FF FC FF FF
					default:cmd_state = 1;break; //FF ?? ?? ??
				}
			}
			else if(*buff==0xFC)
			{
				switch (cmd_state)
				{
					case 1:cmd_state = 2;break; //FF FC ?? ??
					case 3:cmd_state = 2;break; //FF FC FF FC
					default:cmd_state = 0;break; //?? ?? ?? ??
				}
			}
			else
			cmd_state = 0;
			if(cmd_state==4)
			{
				cmd_size = cmd_pose;
				cmd_state = 0;
				cmd_pose = 0;
				return cmd_size;
			}	
			
		}while(result);
		return 0;
		
}


以上程序,如果缓冲区有完整协议那么就返回一个协议字节长度,否则返回0
这样我们需要在大循环中判断是否获取到了一帧协议来确定是否对协议进行解析.
注意在ringbuffer.h文件中添加这个函数的外部引用
然后,去修改main.c里面的大循环while();
我们修改原来的main()函数中初始化之后变量的定义如下:

	uint8_t byte = {0};
	uint8_t size;

修改了byte的长度,和定义了一个size来存放取得的协议的长度
之后修改while如下:

  while (1)
  {
  /[i] USER CODE END WHILE [/i]/
		size = rb_get_cmd(&working_rb, byte);
		if(size != 0)
		{
			CDC_Transmit_FS(byte,size);
		}
  }

意思是:大循环中连续查询是否有协议存在,如果有那么size会有一个协议长度值,我们根据长度值来返回这个取得的协议给串口调试助手!

技术分享图片

下面这幅图片显示的是测试以上协议的结果,
协议的格式是
如果没有按照这样的协议发送数据那么该帧数据不会被返回,
 
由于所用串口调试助手问题,最快速度只能在10ms发送一帧数据,
1.测试27个协议数据在10ms连续发送,长时间发送接收无错.
      [
]测试协议数据大于64字节发现不能返回数据,减少一个字节后,可以正常接收和返回数据.[/
    ]
究其原因归结为usb端点的最大字节64的问题,但是为毛64字节不行呢,非要63字节??? 有知道的吗?

ok,usb的cdc测试结束!

下图是63字节以10ms发送的结果,发送和接收字节数相同,此时的速率应该是100*63=6300波特率
还没有达到最低的9600,有谁的串口调试助手速度更快的可以测试下!
希望测试的人能给个回复,看看这样做的最大不丢帧速度是多少,谢谢

技术分享图片

lion187 - 好吧!80后IT男

确实抛砖引玉,帮我解决了程序中的问题,非常感谢。
想问下大大,我使用http://www.stm32cube.com/uploa ... a.png直接生成代码,我的usb引脚是PA11和PA12,查遍整个工程,都没看到对这两个引脚的初始化,是不是还要添加什么东西?
@lion187:有,但是里面只有中断的初始化,其他没有,我在想是不是这个原因,我这个引脚选不了,不知道还有设置什么
技术分享图片
 
请问楼主的stm32是什么型号的?
”这个帖子的目的是研究USB虚拟串口,所以帖子的相关程序不适用于实际使用!“这句话的意思是什么?是说不能用板子进行实验么?   求解~~谢谢
技术分享图片

ZJZTO - 各种打酱油

有两个问题请教楼主,看图好了:

技术分享图片

上图中的byte ,是 byte[1],or ....?
这个问题主要是为了下面的情况发送长度为1时,返回值都是第一个字节,当然发送长度匹配后返回值就对了。请问示例的实际效果是不是也像下图这样的?
技术分享图片

 
这个是设置了byte[6]发送刚好6字节。
技术分享图片

 
 
技术分享图片

ZJZTO - 各种打酱油

楼主还不在,都等了好几天了,

uint8_t rb_get_cmd(struct rb *rb, uint8_t *ptr)
 
函数有问题吧,
ptr = *buff;
 
ptr是不是没啥用咧
技术分享图片

ZJZTO - 各种打酱油

依然卡在uint8_t rb_get_cmd(struct rb *rb, uint8_t *ptr) 
不知道是不是数组\指针定义有问题

技术分享图片


buff 里面的数据一直不变,楼主吞代码的编辑器太坑,有没有打包的原函数参考?
 
技术分享图片

赵振林 - 90后,,,

电脑端的驱动想找个最新的,能支持win8的,官方网站找了半天没找到,能买帮忙提供一个官方下载地址吗?技术分享图片

有哪位同仁尝试过应用UBS虚拟串口实际应用控制中吗?类似USART控制指令对灯的点亮等

你好高手,请教你个问题,我用STM32做了个USB 4串口的程序,在WINDOS XP下可以正常使用,到了WIN7,或WIN10都不能使用,WIN10无法正常驱动,而WIN7 一插入USB就立刻蓝屏,根本没法找问题的机会,电脑都死了,请问什么问题??期待回复,谢谢!

你好,我想请教一个问题。上位机串口助手对串口参数的设置,是如何在单片机的串口端实现的?例如上位机修改了波特率为9600,那么单片机是如何获得波特率变动并重新初始化串口。

抛砖引玉:一步一步Hal库学习usb虚拟串口功能CDC或者叫VCP

原文:https://www.cnblogs.com/tianqiang/p/10859259.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!