你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】使用 STM32CubeMX 实现 USB 虚拟串口的环回测试功能

[复制链接]
STMCU小助手 发布时间:2022-3-2 22:17
前言8 P: L- m  a  ~4 z( v4 p* u
客户在 STM32F401RET6 中使用到了 USB 的虚拟串口功能。要求提供一个在 STM32Cube 中实现虚拟串口进行数据环回测试功能的范例程序。因为在我们目前所提供范例程序中,并没有适合客户需求的范例,所以我们在 STM32CubeMX 中创建一个范例程序给客户进行参考。' D; F% I9 \& u4 {( |! C
环境需求如下:, B0 ~' h1 e) r# a+ f$ M& r7 f
硬件环境:STM32F401C-Dicovery: V$ Z* |, F3 y7 I( C7 H
电脑操作系统:window 7.x
* ~8 l4 S+ Y' r5 k# q0 d) `7 s  ?集成开发环境:MDK、IAR
  w7 n/ @" U1 [5 h7 k( b" i; z3 ~驱动:ST VCP Driver
9 N9 e' B  b* Y3 {! MSTM32CubeMX:4.12.08 R# O: t' f1 p; r( q
STM32CubeF4:1.10.0' r- L4 b$ n0 M0 D" A3 v  h
6 U- S& _5 ?  ]  n8 N2 @
STM32CubeMX 中配置流程" ?) C# S4 D" [
实现步骤如下:" S  z& h+ e& ~1 e. ~& ?. m0 s! n
首先,打开 STM32CubeMX 软件,选择 STM32F401C-Discovery 板子。
3 [5 T" E) P* T" p+ m8 H+ G
* T" I4 N% `8 T LLE1CBOM1NGI_MAPLHO(]SU.png & ~/ n! M: Q4 y( d, g0 x) @

( R  Q9 o7 N8 N5 q在 PinOut 列中选择 USB OTG FS 的 Device Only 选项
! Y5 j# ~0 X* a% V  J) q* K; P
' U" s6 ]' v+ l% r 6{FI94UXSRQJN_U)79{V7.png
. V6 s. S  O, _6 @( b) y' B( P; F  i5 j% D
使能 USB 中间件的虚拟串口功能( t6 q8 T* T* I/ ?. X- ^2 ~$ f
" g$ [* A" O' p$ ?
@S7J8]2CR{N$(A81CD{U7XC.png
! [0 j& `( b0 B# T# f4 e
+ @( R9 X2 p; D! B- m$ H$ z3 z- f直接生成代码,这里我们选择 MDK 的 IDE,工程名字叫 VCP_Test
" J* v2 f* `& L- S( I5 k
7 j9 M- }1 t" d- A$ O& A6 i. S# l# ^ FL%Q6[]QXH[VX{_LNUJ9DQC.png $ }9 H/ ?2 f" Y& w3 l5 }7 d/ _
* U9 d7 \9 g  F0 X' {6 B
在生成的工程代码中的 usbd_cdc_if.c 文件中添加如下几行代码:
# P9 e' X" d; n8 @4 R3 G) W8 m
  1. #define APP_RX_DATA_SIZE 1024
    $ f! V2 ]9 N0 C8 i, q! L
  2. #define APP_TX_DATA_SIZE 1024
复制代码

7 H, z& m! _& N* n这个定义本来就有的,只是建议将定义的值修改为 1024,这样效果更好。这个值的默认值是 4,但是在实际的操作中发现,如果你上位机传输的数据大于 4,且并不是 4 的整数倍的时候,会出现丢数据或者数据不返回的问题。其根本原因是因为虚拟串口的数据是以数据流的方式发送出来的,接收数据不知道每次接收到的数据大小是多少,所以使用了循环队列,但是一旦你的循环队列很小,很容易出现溢出的问题。比如你设置这个值为 5,你发一个 123456 的数据给 MCU,那么 MCU 就会返回 123451这六个数据。最后的一个数据就是溢出了,所以只能被第一个数据所代替。
2 C8 B9 Z1 Q/ s: e" j, u% Y
  1. /* USER CODE BEGIN PRIVATE_MACRO */
    + |- X2 W0 I5 ?& r, U* H
  2. USBD_CDC_LineCodingTypeDef LineCoding = {) `: w, n) m! F0 D
  3. 115200, /* baud rate */$ h0 u  |+ E1 H  Y2 {# A
  4. 0x00, /* stop bits-1 */
    ( k- B# t' n, N: b( u0 k9 d
  5. 0x00, /* parity - none */
    4 H2 s  c1 W2 j% X3 P
  6. 0x08 /* nb. of bits 8 */
    0 i8 O3 s' ~, @5 @0 J4 w+ A
  7. };
    , @+ @# x2 S! T8 j- M" `% P$ t2 p
  8. uint32_t UserTxBufPtrIn = 0;
    3 r2 q) P  ^' \& W# L1 ~" {
  9. uint32_t UserTxBufPtrOut = 0;
    0 a1 C8 _9 v- ^- E2 w& e; ]
  10. /* USER CODE END PRIVATE_MACRO */
复制代码
# K0 F, }& [, j
上边这部分代码是用来做串口参数设置的。下边的代码中会用到。$ m( ], K' e2 H$ d
在 CDC_Control_FS 函数中的对应位置添加如下 code' `( l$ k, J$ ^! g5 ^2 C; v
  1. case CDC_SET_LINE_CODING:' G8 U1 V  w: D3 s) t. J" \6 \
  2. pbuf[0] = (uint8_t)(LineCoding.bitrate);
    : l' N1 X" {8 Z3 v
  3. pbuf[1] = (uint8_t)(LineCoding.bitrate >> 8);
    $ z9 N0 L& f! }" ]( a8 y
  4. pbuf[2] = (uint8_t)(LineCoding.bitrate >> 16);( E, ]7 _5 G; @1 S" V0 g; v8 V* n' m
  5. pbuf[3] = (uint8_t)(LineCoding.bitrate >> 24);
    9 H1 i. z& H  Y! k" @+ K* X! M" @
  6. pbuf[4] = LineCoding.format;" ^- o* K; }& B2 `' q! Z3 D# Z
  7. pbuf[5] = LineCoding.paritytype;
    * w+ ]* W& K# o: ?) K: P5 l+ Q, S
  8. pbuf[6] = LineCoding.datatype;( O9 a6 {& T% ^/ R- b
  9. break;
复制代码
/ z1 H, ^8 a# i* b0 g# M9 x! k
在 CDC_Receive_FS 函数中的对应位置添加如下代码
7 [$ b' M/ w. V* B
  1. <blockquote>/* USER CODE BEGIN 6 */
复制代码

0 S5 z0 G7 s6 u7 Y; s2 I这是用来接收 USB 传输的数据,并缓存到 UserTxBufferFS 这个数组中。4 {3 ~+ s* n. T/ k& l$ R4 `
在最后添加如下函数
( e" c4 V. u/ L" ^4 U
  1. /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION *// g7 o- |' \+ T/ _& {* H
  2. //this function is check the application buffer whether has data3 ?3 I7 E# i- a) }; r
  3. void Main_loop(void)! v5 q; t* ~' s; @
  4. {
    2 e1 b! u% n/ F+ A) B1 d
  5. uint32_t buffsize;8 |5 I4 N3 j) m, E+ A

  6. / |" g1 y' L/ m
  7. if(UserTxBufPtrOut != UserTxBufPtrIn)" u& n4 K6 ?' w( `" k
  8. {
    9 ]7 O1 l7 T6 y+ U2 d+ h( V
  9. if(UserTxBufPtrOut > UserTxBufPtrIn) /* Rollback */
    6 [$ y0 P4 k/ g& @6 R
  10. {
    + O$ U. d3 R6 G/ i
  11. buffsize= UserTxBufPtrIn;; x" Z& N& [3 X/ l
  12. }" ^+ E1 W/ c7 X5 P' G8 q
  13. else
    + i  p7 T) y0 G5 Q8 w! f+ N: j
  14. {8 f( _1 Z5 x# ]( U/ s
  15. buffsize = UserTxBufPtrIn - UserTxBufPtrOut;
    6 q" F; I, p, o4 e, R
  16. }
    0 @' I. K4 `& ]& Y& X
  17. : m! y. R  J; V8 b4 s- {+ ~9 q
  18. CDC_Transmit_FS((uint8_t*)(UserTxBufferFS+UserTxBufPtrOut),buffsize);
    , O! f# i, _. }
  19. UserTxBufPtrOut += buffsize;
    6 N' h! K- c& k- e" T, Y, K
  20. USBD_CDC_ReceivePacket(hUsbDevice_0);4 y* U1 Y0 D  U0 n
  21. }
    9 g/ B# ^7 J; ]' x# ~
  22. }+ t. i, ?6 _) Q% E
  23. /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
复制代码
4 A: p* f  t$ R8 a+ Q  C& L
这个函数实现了一个简单的数据队列操作,一旦有数据收到,就将该数据发送到 PC 端,实现数据的环回功能。9 m5 R, f, Z, V: h" c
在 main.c 的主循环中,调用 Main_loop 这个函数即可。
2 ]# W3 |: [) s0 i+ _
  1. /* USER CODE BEGIN WHILE */0 b' x% w4 p9 K3 A& @
  2. while (1)- o3 F  I5 o) M4 ~; V' i( ?6 {
  3. {& v6 ^/ d) g# `- D; \5 A5 E8 m/ P
  4. Main_loop();
    % E  K, w2 o7 I0 V5 ~' ]
  5. /* USER CODE END WHILE */
复制代码

' Z; S. A! P, A) ?" u最后修改一下堆栈的大小:
" m5 W; u5 z( J( K在 MDK 中修改堆栈:, {2 s- S2 P! P' C) D
P62[[Q33_ATYECBZL]Y7B7S.png 3 z. G  Z8 u) R/ U  K& z- y  j7 [
6 l( o% J  _5 I: E
IAR 中配置堆栈大小
, v$ l  a5 O1 o6 \* A1 ]& K9 B- A, x2 m* c, Z9 R+ |
F6)XDA532]G{4M1G$D_G(R4.png 4 b4 [5 c2 I: L) Z/ f

: m3 U6 _- \  `' N7 v* b编译后直接下载到 MCU 中即可以运行查看结果。. J( z' J0 t9 R3 M2 |( B8 `; \" h8 ~
注意:硬件需要一根 Micro USB 线来连接板子的 USB 端口和 PC 机。/ v6 `7 ?& h, m
PC 端需要已经安装好 ST 提供 VCP 驱动程序。3 K. `- E# R/ }7 h

* j6 U2 I' I( _  K( |% p' ~; i+ X: h U~P%L~C[$I2@9FQD94E@_XU.png : T- D. R1 E  L( p

! g: j( S8 y$ g- b  l5 v0 k4 M) @9 `" V UAOUZGD6@8GAN]_BVHD_25H.png ) Z3 D/ J( d5 ~* [
2 S& T8 T. T" h- E4 h' b5 A' T
问题总结:

' w/ b, e: M# P! Z# nSTM32CubeMX 会使 STM32 全系列中的软件移植工作变得更方便。在一些以前看起来比较复杂的程序,在 Cube 中可以很简的完成。
  i( w: b9 Y6 i3 e  R2 E6 h( ?3 \* L1 j* G8 C: L

: N' w4 [: u) u4 N7 X* \$ Q2 I
2 o0 o1 v6 j. @2 |+ t, _7 u3 l' [' F; \" [
赞 收藏 评论0 发布时间:2022-3-2 22:17

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版