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

STM32CubeIDE的USB从设备串口驱动经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 17:47
一、USB_OTG简介/ W7 v' m9 y! o# m1 L
        USB_OTG(OTG,ON THE GO)是一款双角色设备(DRD) 控制器,同时支持从机(USB DEVICE)功能和主机(USB HOST)功能。在主机模式下,OTG 支持全速(OTG_FS,12 Mb/s)和低速(OTG_LS,1.5 Mb/s)收发器,而从机模式下则仅支持全速(FS,12 Mb/s)收发器。主机模式下需要的唯一外部设备是提供VBUS的电荷泵。在驱动实现层面,USB OTG是USB Device和USB Host 的基础。在实际使用,USB OTG是USB Device和USB Host 的底层驱动。
# T5 e0 ?0 R1 I2 F$ S4 u" \

5 e8 D5 F' F" O" Q        stm32芯片的通常USB物理接口管理两个引脚:$ ~0 ~3 S2 G/ Y4 a6 L- k6 e6 _  _
. D# ?0 f6 y. X
            DP/DM:内置上下拉,由控制器来设置不同类型的需求
6 f3 x# b, b7 A2 D+ O* ^, i* ]

. N5 B$ b/ S! k" j0 I& [        OTG实现时,额外配置ID引脚:
/ t) {- j  c" B$ {. h5 H2 B4 m            ID:检测插入的线是B端还是A端,用于区分A类和B类设备,! J5 t* v3 M4 P. _) t
" \9 V# v9 k" `3 s' x7 q+ Y3 f& d
        USB Host 模式下,需加配置Vbus引脚:, B, v2 m4 k. l" M- n) e/ g
            Vbus:内置检测器,用于测试UBUS的有效性
( q) Q7 B2 z; L! ?5 i. V

5 F# `( q* S1 Z# f/ p# T; Z: y        额外地需要支持SOF和NOF时,需要加配置SOF和NOF引脚。' V* U7 X8 \0 P! Y+ [% J4 @' S0 N0 V
8 G& }/ m6 F8 M* E: b  q0 V
        例如本文采用的STM32L496VGT3芯片,其配置支持到的引脚如下:
5 F& M/ w! L( i7 m0 l3 r1 ^. @$ a
946bf813d73844edb8f5d5b509d33692.png
$ _# p3 o2 z# {; _+ [* s" |
# `. y2 j( W+ B9 x& A: t& F8 N
USB协议栈的层次划分
# L$ M4 w$ [2 e: q* F    一个Host可能有一个或者多个Device
1 Q$ x  J  C, T, ^# V    一个Device可能有一个或者多个Interface
7 ]! T& X- o0 J& ]5 h    一个Interface可能有一个或者多个Endpoint
7 K! H( d+ c* m9 b& c        Host(主机)连的是Device(设备),这一层是走物理连接的,也就是信号线实际连接两台设备。
& j- w. [' ^2 s/ i" ^        Device(设备)下可能有多个Interfece(接口),从这开始是逻辑概念,一个Interface,就是一个独立的功能接口,每个Interface模拟一个设备功能,比如集成了键盘和鼠标的USB设备,里面就是两个interface,一个是键盘,另一个是鼠标。Interface之间通常是隔离的,互相不干扰。
+ q; Y3 }/ Z0 q& E; ~2 s0 f        每个Interface(接口)下面有一个或者多个Endpoint(端点),这也是逻辑概念,例如控制信号端口、数据信号端口等。端点是USB设备通信的基本单位,所有通信几乎都是从端点发起的。3 T3 t) o3 A; e% j6 O' J

1 _6 r- U/ ^. |' z

* e3 R+ W6 w: f二、创建工程及USB_Device配置
. W% l) Q. F5 O& r! x3 N4 J4 ]6 Q        本博文基于STM32L496VGTX3芯片实现
+ `9 \, S) e, N' W$ w2 W4 j/ V/ Q0 E$ ]/ O: n
        基于该工程的(.ioc)文件创建了新工程stm32L496VGTx_USB,并移植了相关源码,实现了lpusart通信以及lcd显示字体。
  }9 F: a* R; j  h# l
, ^, M1 W7 G! ^' Y$ w6 u" r, l/ A        现打开工程的(.ioc)配置文件,进入cubeMX配置界面,开启USB_OTG_FS的USB Device功能,参数保持默认配置。2 p7 X& A/ L! i& `
! }- ~5 Y3 R* L. O9 l
0e2ab1db00174f8ca243e394bec74d24.png . I3 Q! W) k0 [8 \1 `0 ?
2 f- M: i$ O3 v  ]9 Q
        确保USB_OTG_FS的中断功能已经开启。
* K: k+ X7 o, O% F8 a( t# j, _# s, r6 t* z
cbcb43dc403e4cc781ae49e349c631db.png . h6 \6 e( o+ a

! M3 N$ X( t4 U# K- X3 r, v         开启USB_OTG_FS的USB Device选项后,Middleware栏目可以去配置USB_Device信息,本文MCU作为USB_Device与笔记本电脑USB_HOST相连接,实现串口通信收发数据,因此选择通信类型虚拟串口功能,如下图所示,参数保持默认。
, a. h  A3 ^/ J, v+ v/ U2 y/ g% N* I" N3 I9 e" I
0ea91a79221747838f2cc568f27da59a.png 7 I( p7 q0 j$ z' d/ t; a7 c
0 |4 M  \! V! g& [" C- V$ A. D" @
         设备描述符按默认设置:9 @, i4 {0 X, {' ^

' }5 n8 P! K* ]2 t4 U! v& s) x4 F
7c5c236592f0408092b2ca0601aeea69.png : f7 y# [* R' }2 u2 o$ W* x; Y$ n
, j; U  e. F7 _# V6 S0 i4 f; e- Z9 H
         由于USB驱动引入了中间件代码,并比较复杂,需要更多缓存支持,现进入工程配置页面,调整min heap size和min stack size为合适的值,防止因数值较少而出现无法编译、编译异常、运行错误等情况出现。
: j, K$ m5 n& Q
; g* T) J$ g; E( I' [( [$ T' E( U6 n% e
75294d6ff0ab476182e634d01bde6f2f.png
% B# b. h1 r  h1 U* d" n# g' V2 [* L2 y8 m1 p: {
         点击保存输出生成代码,关于USB_Device驱动相关代码如下,其中usb_cdc_if.h/c源码是用户可修改源文件:
! u0 m9 \4 W6 a; G3 k- J6 d& D/ K& P3 p" Z3 n. x5 o' b3 L
38ca521a5d8e4882838bd57d4050b72f.png
/ b3 Q. i2 ~9 B# `6 X& I7 p2 _0 u. f1 Y% t1 Q2 U
三、USB_Device驱动实现设计
' w: C0 Q6 o' K3 q        【1】在usb_cdc_if.h中,添加USB相关全局变量(接收缓存数组、最大长度、接收标记及长度)5 y4 ?$ [8 C( p( A4 D1 \
  1. /* USER CODE BEGIN INCLUDE */* n1 b8 _2 K! o2 X+ j" p( g
  2. #define USB_REC_LEN   256//定义USB串口最大接收字节数& h" h3 }7 R0 P( l, ~+ g% Y* r
  3. extern uint8_t USB_RX_BUF[USB_REC_LEN];//接收缓冲,最大USB_REC_LEN个字节.末字节为换行符1 I' z7 s: [' k/ _( U
  4. extern uint16_t USB_RX_STA;//接收状态标记(接收到的有效字节数量)% ~! `/ l; n" l1 d/ B9 [3 ^7 |
  5. /* USER CODE END INCLUDE */
复制代码
2 S9 D! `( f$ N" r6 l. `; Z- m' J, G
        【2】在usb_cdc_if.h中,添加USB打印输出函数USB_printf声明# p; s& a$ L- b' ?9 J; @* A" C
  1. /* USER CODE BEGIN EXPORTED_VARIABLES */
    9 ^+ l2 \+ d; \, P; \
  2. void USB_printf(const char *format,  ...);//USB模拟串口的打印函数3 R$ E' g+ s9 p/ |2 s
  3. /* USER CODE END EXPORTED_VARIABLES */
复制代码

: [+ j& A' p2 H2 q* \* u

/ ~+ d) c$ e2 w        【3】在usb_cdc_if.c中,调整USB接收函数

; R  I  e2 P! ?
  1. static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
    , a8 K0 W$ v2 n; u3 Y
  2. {( @9 D: |: N7 Q4 R
  3.   /* USER CODE BEGIN 6 */6 `4 A, u  q- H+ n3 `& e& \2 i
  4.   //新增代码开始处* c) S* G6 j( l4 {4 w3 j
  5.         if(*Len<USB_REC_LEN)//判断收到数据量是否小于寄存器上限' U: A5 H  ^, S0 w: X3 U# f: x4 s
  6.     {9 I3 K8 _7 _; A  L
  7.        uint16_t i;
    ' |) A4 Q; e$ m) ^/ o. S5 M
  8.        USB_RX_STA = *Len;//将数据量值放入标志位
    3 x! F$ ~7 p' L0 s
  9.        for(i=0;i<*Len;i++)//循环(循环次数=数据数量)" x  ]' x6 g3 _: G- A( I
  10.            USB_RX_BUF[i] = Buf[i];//将数据内容放入数据寄存器  V' z- ?$ g( i7 o0 [" u! f: l
  11.     }
    ! S+ ^8 E1 U9 z7 s9 `  L
  12.   //新增代码结束处
    + k: H( e$ N8 G$ x
  13.   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
    $ U7 a! F' ]- g% h2 {3 F
  14.   USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    . I& e1 t" D  d# y/ D+ A
  15.   return (USBD_OK);1 B6 q, B' M4 n- {
  16.   /* USER CODE END 6 */1 b% E9 v5 m* M( _2 u
  17. }
复制代码

; [: D. d2 ?. I/ s4 c        【4】在usb_cdc_if.c中,调整USB发送函数/ c; f& o) Y- o) d
  1. uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)+ |! v7 A$ M# q, q* _
  2. {
    ; F" @& H9 E/ S8 Z+ _$ r, T8 ]
  3.   uint8_t result = USBD_OK;
    * j; S  {! Q( p) I% n
  4.   /* USER CODE BEGIN 7 */" G" o( `; _2 a) _
  5.   //注释旧代码; b0 |1 ^* L3 \( T$ V. ^5 g  O3 u4 n. w
  6. //  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;1 I7 ?, m4 K% f$ k
  7. //  if (hcdc->TxState != 0){( Z* |7 i2 N! h9 o, c
  8. //    return USBD_BUSY;3 G) w! T% ]. [& m. z  B$ Z5 c6 L
  9. //  }
    6 Q& G' G7 d& B  w
  10. //  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);% X9 D1 }- L- I' i8 p7 e
  11. //  result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);8 j& X; @) p% h* q, h% P
  12.   //新增新代码
    2 R3 ^& e; A0 W6 S
  13.   uint32_t TimeStart = HAL_GetTick();/ R3 S6 J, \. D0 ~
  14.   USBD_CDC_HandleTypeDef *hcdc =  (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
    ) q! G5 h. s& s/ U4 r! l& Y. W/ Y
  15.   while(hcdc->TxState)
    , [" ?+ A* }9 H5 d
  16.   {
    + G# m& I5 z; ]- c' }, |- A
  17.      if(HAL_GetTick()-TimeStart > 10)2 a: w( l) `& I0 i& _$ v" e  L
  18.              return USBD_BUSY;. `2 B2 i8 k( b7 R1 s
  19.      else
    4 u* v5 J4 b5 i/ }# d
  20.              break;
    8 ]" P9 x, Q. x0 \/ j
  21.   }
    ( P# g4 @2 P( P" O
  22.   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf,  Len);
    # p$ C, Z8 d; z- B
  23.   result =  USBD_CDC_TransmitPacket(&hUsbDeviceFS);2 @& y! [* `4 u
  24.   TimeStart = HAL_GetTick();1 E2 T6 P. L: S$ l& ?$ b% \
  25.   while(hcdc->TxState)
    : g0 X5 R3 H. [7 Y3 ~
  26.   {
    ! q$ y& [' \2 Z! ^8 }1 D
  27.      if(HAL_GetTick()-TimeStart > 10)( ~( M! b5 D% E+ I- n+ y/ }- V' @
  28.        return USBD_BUSY;9 f% N' y: c, L
  29.   }
    / m, U: _( O0 L9 C, k
  30.   /* USER CODE END 7 */* Z' I; M+ L, H
  31.   return result;
    0 X4 i) i  }& V. b/ h
  32. }
复制代码

+ s# h! _" B& v% g  a: N* T    【5】在usb_cdc_if.c中,实现USB打印输出函数USB_printf定义. }" J  Y) M3 n8 Q) V7 t4 E
  1. /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
    * c4 H9 I" M7 [4 G, e3 l& d3 C" U: Y8 @3 `
  2. #include <stdarg.h>
    . q. q; G1 I5 B9 D( b" w4 T9 V& n
  3. void USB_printf(const char *format, ...)//USB模拟串口的打印函数
    % g- F. N, Y6 Y& Q3 Y) O/ J* ~
  4. {
    # m# m, e  r! K, O7 B+ {
  5.     va_list args;' e( D! P! I& [
  6.     uint32_t length;& M- O% h: _- D9 z
  7.     va_start(args, format);
    & u2 K9 a, Z) \9 K# @% H, G# ?" i
  8.     length = vsnprintf((char  *)UserTxBufferFS, APP_TX_DATA_SIZE, (char  *)format, args);' a3 O& Z1 P- ]% `5 x3 q
  9.     va_end(args);
      w+ K& i# G. c& `0 T
  10.     CDC_Transmit_FS(UserTxBufferFS, length);2 i/ e! b3 S  X1 `0 J( p$ A6 q
  11. }
    / O* V( ]0 `2 N+ p( T  R
  12. /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
复制代码
: D: e5 Q) u4 M* c
        【6】在main.c文件中,添加USB驱动头文件支持# |7 Z6 n( x, q5 C9 Q9 P
  1. /* USER CODE BEGIN Includes */# \5 w3 e! p5 J3 n* R7 u' [4 m
  2. #include "../../ICore/led/led.h"
    3 D1 T4 z: {; y  B  G* f( b8 y
  3. #include "../../ICore/oled/oled.h"
    5 v# }2 ~+ R4 T+ @
  4. #include "../../USB_DEVICE/App/usbd_cdc_if.h"
    8 _! }0 s+ [8 ^  U  f: c5 ?) U
  5. /* USER CODE END Includes */
复制代码
0 T" ^  ^5 y% t! {3 f, c
        【7】在main主函数初始化处
. I' a  B: s6 K% G+ G+ o
  1.   /* USER CODE BEGIN 2 */; H7 g7 N5 @' p9 i/ l2 M7 C* D
  2.   OLED_init();: R5 g0 A" t2 a6 ?
  3.   //设置OLED蓝色背景显示
    ' C0 C; J: `0 n/ W
  4.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    , E. W( d. S0 O! T" M
  5.   printf("OLED_Clear_DMA\r\n");+ l; C% a9 |! v3 m
  6.   /* USER CODE END 2 */
复制代码

, p7 Z' @1 @1 x- \3 k# Q) o【8】在main主函数循环体内实现8 l- `6 E, ^9 [" G
  1.   /* USER CODE BEGIN WHILE */) O8 U5 p' _( w) ]1 n* E
  2.   while (1)2 ~* \; r( P. r+ O1 J
  3.   {6 g2 S6 N" _) u  s" G, N
  4.           //USB模拟串口的查寻接收处理(其编程原理与USART1串口收发相同)* i$ i* e0 I5 {* O
  5.           if(USB_RX_STA!=0)//判断是否有数据3 ?5 q" ^8 w2 F' J* v6 ~9 P  y$ M
  6.           {3 R& R- c# S6 t. B
  7.                   OLED_printf(10,42,"%.*s",USB_RX_STA, USB_RX_BUF);
    ) _+ S9 C3 B% e( w" Y
  8.                   Toggle_led1();% a: y) e# n/ b) O+ {# H
  9.                   USB_RX_STA=0;//数据标志位清0
    6 c5 m8 D. k/ }( }! X2 D; f8 l6 N
  10.                   memset(USB_RX_BUF,0,sizeof(USB_RX_BUF));//USB串口数据寄存器清0( ^% l. V2 w  G* R6 p" X
  11.                   HAL_Delay(10);//等待; u7 I( F# X8 y7 m6 j, O
  12.           }
    . S2 D( M, r+ q. W" o3 N
  13.     /* USER CODE END WHILE */
复制代码
2 N+ F# m% G$ J. o
四、编译及下载+ s# g. g6 i( c- Y# a
        编译下载,该开发板原理框图,需要将跳线帽重新插拔设置,相当于原理的USB-ST-LINK连接到USB_MCU上,即原理lpusart通信及ST-LINK下载的USB接口改为连接到USB_MCU的DM/DP引脚上实现通信。% M  L5 s: `* \3 t

# k; M: l" f5 N6 I- n) J
59a80db11f4f4dbb80fdd21b3a5c67e8.png   z- L6 |% P2 E7 g; b

- n$ t6 L* j! j5 D7 R         更改调线帽后,打开串口工具,发送数据,顺利在点(10,42)位置绘制输入文字,LED1等会切换状态。
% ~# j/ Q4 x; \( h( T, V- O% x( j8 z5 B$ R) U+ Y1 r
97a35a8bc13b42b39461355775943d6e.png 4 {2 z, Q3 B1 t# x
0 v. b8 E6 C# b5 P" @, J- H
————————————————
# t9 x* ~2 l' X  e9 y* `: b版权声明:py_free-物联智能- B6 \* {# f* y2 x  H
如有侵权请联系删除' t) E5 p) g# b  A% @# l
/ u. t8 C7 F9 G( s/ i

6 j. n2 [3 P- K0 i( \
收藏 评论0 发布时间:2023-4-6 17:47

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版