datasheet

STM32HAL----USB模拟串口(VPC)

2019-06-18来源: eefocus关键字:STM32HAL  USB  模拟串口  VPC

想要实现的功能是,USB模拟串口收发数据。串口助手发送数据至MCU,MCU接收后返回给串口助手。


当初是想用标准库做这个功能的。但是因为后来了解到STM32CubeMX这个软件,在尝试之后实在是感觉,太方便了。所以,并没有使用标准库,而是直接用STM32CubeMX生成HAL库的代码用了。


(1)先点New Project,然后输入自己的MCU型号


(2)配置引脚与外设


这里我用的是ST-LINK进行DeBug,Tim5提供系统延时节拍,PE5与PB5点亮LED。而SysTick,用在FreeRtos提供系统节拍。

(3)时钟树配置

(4)配置外设


这个页面可以对外设进行功能的设置,比如GPIO的输出类型或者引脚初始电平。在这里主要设置FreeRTOS,创建2个初始任务。其他的比如USB,默认就可以使用了。



(5)Poject Settings


这里注意两个地方,我使用的是MDK。所以IDE选项选择的是MDK-ARM V5。CodeGenerator选项卡下,将Generated files下的第一个选项打上勾,这样就会启用模块化编程,不同的外设封装不同的.c   .h文件。至于Project Name跟Project Location,


自行设置便可。


然后,点击STM32CubeMX主界面的Project,Generate Code。就能在我们指定的文件夹内直接生成工程文件。生成之后软件提示你打开项目,点击打开后,工程内分组如图:

因为使用了RTOS,所以编程主要围绕两个文件,“usbd_cdc_if.c”以及“freertos.c”


“usbd_cdc_if.h”添加一个USB管理结构体的定义,并将“usbd_cdc_if.c”中两个定义移到“usbd_cdc_if.h”


/* USER CODE BEGIN PRIVATE_DEFINES */

/* Define size for the receive and transmit buffer over CDC */

/* It's up to user to redefine and/or remove those define */

#define APP_RX_DATA_SIZE  1000

#define APP_TX_DATA_SIZE  1000

 

typedef struct  

{  

   uint8_t   OutFlag;

   uint8_t   EFlag[2];

   uint8_t   SFlag;  

   uint16_t  ReLen;

}USB_Dev;  

 

/* USER CODE END PRIVATE_DEFINES */

/* USER CODE BEGIN INCLUDE */

“usbd_cdc_if.c”声明USB管理结构体变量并赋值,且修改“CDC_Receive_FS”函数。


/* Private typedef -----------------------------------------------------------*/

USB_Dev  USB_S =

{

0,

{0x0D,0x0A},

0,

0,

};

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)

{

HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5); //开启接收指示灯

//将已接收数据长度赋值给USB_S.ReLen

        USB_S.ReLen += *Len;

//判断是否有结束标志以及接收数据长度是否达到UserRxBufferFS长度上限

        if( USB_S.ReLen

            UserRxBufferFS[USB_S.ReLen-2] != USB_S.EFlag[0] &&

        UserRxBufferFS[USB_S.ReLen-1] != USB_S.EFlag[1]

      ) 

{

  //设置下一次接收数据的位置

        USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS + USB_S.ReLen);

        USBD_CDC_ReceivePacket(&hUsbDeviceFS);   //准备接收数据

}

else  //长度达到,或者检测到标志位,触发数据输出

{

  USB_S.OutFlag = 1;

}

        return (USBD_OK);

}

 


“freertos.c”中,添加头文件“usbd_cdc_if.h”并在对应的任务中添加对应功能:


/* LED_Toggle function */

void LED_Toggle(void const * argument)

{

 

  for(;;)

  {

     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);

     osDelay(500);

  }

 

}

 

extern USB_Dev  USB_S;

extern USBD_HandleTypeDef hUsbDeviceFS;

extern uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];

/* USB_SendMess function */

void USB_SendMess(void const * argument)

{

   uint16_t timeout = 0xffff;

   uint8_t temp;

    for(;;)

   {

timeout = 0xffff;

if(USB_S.OutFlag)

{

    temp = !(USB_S.ReLen%64);  //判断长度是否为64整数倍

    while( CDC_Transmit_FS(UserRxBufferFS, USB_S.ReLen - temp) != USBD_OK && timeout--);

 

          if(temp)  //当发送数据为64整数倍时,无法发送成功,故分成2次发送

    {

       while( CDC_Transmit_FS(UserRxBufferFS + USB_S.ReLen -1, temp) != USBD_OK && timeout--);

    }

    USB_S.ReLen = 0;

    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);

            USBD_CDC_ReceivePacket(&hUsbDeviceFS);

            USB_S.OutFlag = 0;

            HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET); //接收指示灯关闭

}

    osDelay(1);

  }

}

 


启动文件:需将堆的容量调大,因为USB缓存使用的是堆空间。我将它调为4K:


Heap_Size      EQU     0x1000

 


至此,代码创建并修改完毕。


 


下载代码到板子,下载完毕后关闭重上电,然后打开串口助手对应的串口。波特率之类全都不需设置。


发送一篇长文章,MCU接收后返回给串口助手


现象如图:


写在最后

(1)MCU端接收数据问题。


        USB传输一次最多64字节。所以,如果想一次传输大量数据给MCU。需制定协议。如图的串口助手,在发送完毕数据之后,会补上2字节结束标志:0x0D,0x0A。MCU端可根据结束标志判断数据是否接收完毕。需注意的是,不同的串口助手协议不一定相同。有的是根据数据包长度判断是否接收完毕。


(2)连续输出的问题。


        1、需要轮询发送函数返回值是否是“USBD_OK”


        2、输出字符串,如果不去掉字符串最后的结束符。某些上位机软件,只能显示1次发送的数据,第二次


             发送的数据不能正常显示。(本博客使用的串口助手就是如此。。。。)  ,但使用过另外一款串口


             助手,并未发现此情况。    


        3、轮询返回值有可能导致程序卡死,因为主机若是没有接收MCU发送的数据,MCU会一直轮询


             直到主机接收完数据为止,解决办法是加个timeout--到while里面,到时间跳出循环。


(3)WINDOWS下不能识别的串口有黄色感叹号。


        这个有可能是堆设置不够大。我在F4下遇到了这问题,将堆空间“Heap_Size      EQU     0x200”


        设置为“Heap_Size      EQU     0x1000”,黄色感叹号消失


  (4)通讯速度问题


       参考“http://bbs.21ic.com/icview-811704-1-1.html”,附件中有测试速度的软件。


              串口助手当通讯速度40KB/S左右,接收不到数据。当然,如果是连续发送'0',串口助手上窗口不


       显示字符,是可以接收到数据的,但当速度超过500KB/S,依旧不能接收数据。所以,网上很多反映


       VCP速度只有几十KB/S的,估计是上位机软件问题。


              使用附件中的软件,可以成功测出速度。STM32CubeMX生成的程序,经测试发送到主机的速度可以


       达到1000KB/S以上。


      


(5)“CDC_Receive_FS”函数的解析


      这个函数的作用是两个,一是设置下一次接收数据的Buff,二是处理接收端点。


      这个函数是在MCU接收完数据之后才调用的,而不是进入这个函数才开始接收数据。比方MCU接收到64byte的数据,


      接收完成后进入这个函数,设置下一次接收数据的Buff。然后调用“USBD_CDC_ReceivePacket”处理接收端点。


      如果不调用“USBD_CDC_ReceivePacket”,是无法进行下一次的接收的,但发送是可以的。


(6)CDC_Transmit_FS 发送64整数倍字节数的数据出错


      当我调用“CDC_Transmit_FS”发送64字节的数据时,串口助手并不能接收到数据。


      网上有帖子解释了这种情况:http://bbs.21ic.com/icview-159300-1-1.html


 


      总的来说,USB的bulk协议以发送小于64字节或者是字长为0的数据包作为结束动作。


      发送64字节的包的时候,只需要末尾再发送个字长为0的包即可。



关键字:STM32HAL  USB  模拟串口  VPC

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic465084.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32 HAL库串口发送多字节数据
下一篇:STM32启动模式详解

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32Hal库学习(一)CubeMx学习点亮LED灯

cubemx安装:MDK5安装+破解+pack言归正传新建工程选择是STM32型号,进入芯片页面1.首先必备的SYS和RCC选择外部晶振、配置LED引脚,因板子而异PE5 PE62.在clock configuration中配置HCLK 72MHz3.配置configuration中的GPIO4.在project中配置路径名称,编译工具生成工程文件,打开工程,编译文件,创建.hex文件烧录此时LED点亮,再编写一个流水灯程序感觉直接生成的代码框架有点混乱,之前用的都是标准库,这个就感觉不熟悉还是先创建一个文件夹放自己的程序,然后编写自己的my_system.cpp,my_system.h,LED.c,LED.h。采用c/c++
发表于 2019-07-18
STM32Hal库学习(一)CubeMx学习点亮LED灯

STM32开发笔记10: HAL_Init函数

单片机型号:STM32CubeF0    本文介绍HAL_Init函数。    在STM32CubeF0架构中,进入main函数后,首先执行的就是HAL_Init初始化函数,它主要完成以下工作。    1、配置Flash预取功能,这个功能宏在stm32f0xx_hal_conf.h中定义,并已经使能如下图所示。FLASH预取功能在其芯片的参考手册中有如下介绍:The Flash interface implements instruction access and data access based on the AHB protocol. It implements
发表于 2019-07-17
STM32开发笔记10: HAL_Init函数

STM32Cube HAL库串口总结

;HAL_UART_Receive_IT通过设置接收缓冲区和需要接收的数据个数。当数据接收达到设定个数后引发一次中断调用回调函数HAL_UART_RxCpltCallback。由于只引发一次中断,如果需要连续接收,则需要在HAL_UART_RxCpltCallback再调用HAL_UART_Receive_IT。这种定长的接收可能并不是想要的,往往传输的数据都是不定长的,我想这需要将HAL_UART_Receive_IT长度设置为1,然后自己根据接收的数据判断。 由于回调函数没有指明是哪个串口引发的中断,因此有必要在回调函数中做判断,如if(huart==&huart1){} 具体怎么使用STM32CubeMX产生代码就不介绍
发表于 2019-07-09

使用STM32hal库usart的接收中断分析及出现部分问题的解决

;   HAL_UART_RxCpltCallback(huart); /*    省略一部分代码   */}也就是说最后会调用到  HAL_UART_RxCpltCallback(huart);,我们在这个函数内部做自己的需求就好了。(二) 为了效率出了问题由上面的分析,我们可以知道,hal库的中断接收虽然很方便,但是是非常没有效率的,兜兜转转绕了一个大弯才能处理中断接收的数据。尤其是我使用的是STM32F0的芯片,频率只有48M,不比那些动不动就上百兆的芯片,这样的中断处理显然不能接受。所以,借鉴于标准库,我想直接在串口中断函数(void
发表于 2019-07-09

STM32 HAL I2C库读指定寄存器的数据

Support for "repeated start" in STM32 HAL I2C libraryQ:I am working with a slave I2C device (Kionix KX022 acccelerometer)  which uses a "repeated start" mechanism to send data requested by the master (STM32F405), e.g. to read two bytes of data (from register N and register N+1) the
发表于 2019-07-09
STM32 HAL I2C库读指定寄存器的数据

stm32HAL库学习——UART学习笔记

, UART_FLAG_RXNE)){  if(RxConter == 100)    RxConter = 0;    aRxBuffer[RxConter++] = USART1->DR;}else if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)){     clear = USART1->SR;    clear = USART1->DR;    ReceiveState = 1;}3、在主程序中添加如下代码即可完成接受任意字节
发表于 2019-07-09

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved