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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-6 17:47
一、USB_OTG简介
( E6 Y9 w. V' V$ O- {( O3 ?        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 的底层驱动。, T4 j& U+ P: ]" l6 D) E

: }" `9 |+ M# P7 `# @5 x1 A        stm32芯片的通常USB物理接口管理两个引脚:4 D7 s/ r- w8 e. _% b
5 k$ J+ m8 q7 P9 x+ P) a
            DP/DM:内置上下拉,由控制器来设置不同类型的需求8 l# T: T% A) B( t2 T1 i  ^, b: i7 b
* }: n% o, S8 |8 k. n
        OTG实现时,额外配置ID引脚:
$ U1 i' m# l' D  }1 k, ]4 n- {            ID:检测插入的线是B端还是A端,用于区分A类和B类设备,
6 L$ |1 [4 ?9 Z/ J/ A) M% ~- R: o! }6 J' E7 z+ ~2 q3 k
        USB Host 模式下,需加配置Vbus引脚:9 ~2 W! P: I/ W
            Vbus:内置检测器,用于测试UBUS的有效性
  Q; V" a# ~4 j# z7 N4 T0 `
/ X% q% I& i) j- k7 ?
        额外地需要支持SOF和NOF时,需要加配置SOF和NOF引脚。6 f1 @# [) |: o) s! g$ l

& [. `: I; `; ]: ^; \4 F        例如本文采用的STM32L496VGT3芯片,其配置支持到的引脚如下:
, Y; d7 \+ A. \1 @8 t5 x4 ~/ ?8 E5 ]4 x
946bf813d73844edb8f5d5b509d33692.png
8 s- n3 N3 Y3 j/ Y- {6 t0 D* p
# r! K9 O, ]8 r7 w0 {, C. D
USB协议栈的层次划分0 x5 R3 S* s" L( `' [' ^
    一个Host可能有一个或者多个Device
* Y5 ]# j5 v# n8 g" \& y& \3 z    一个Device可能有一个或者多个Interface
8 Z' ^! ~. D6 s1 A  r- V# F4 N    一个Interface可能有一个或者多个Endpoint" }* V/ p2 E* M8 @
        Host(主机)连的是Device(设备),这一层是走物理连接的,也就是信号线实际连接两台设备。) ?6 I3 Y8 @; l0 g; D. L2 c: s
        Device(设备)下可能有多个Interfece(接口),从这开始是逻辑概念,一个Interface,就是一个独立的功能接口,每个Interface模拟一个设备功能,比如集成了键盘和鼠标的USB设备,里面就是两个interface,一个是键盘,另一个是鼠标。Interface之间通常是隔离的,互相不干扰。
  o3 q' w4 V9 M5 Y# m! Z        每个Interface(接口)下面有一个或者多个Endpoint(端点),这也是逻辑概念,例如控制信号端口、数据信号端口等。端点是USB设备通信的基本单位,所有通信几乎都是从端点发起的。
! e$ a, b, a3 Q" G' p0 ]$ ?& X6 B# f# _; ]/ Z* T; ?
* I, Z# U1 b1 C& A' N6 v
二、创建工程及USB_Device配置( l7 |0 u' K" m3 w5 Q! P- d
        本博文基于STM32L496VGTX3芯片实现
% E: x; S! m5 _- v  C9 E# }4 ~7 O
        基于该工程的(.ioc)文件创建了新工程stm32L496VGTx_USB,并移植了相关源码,实现了lpusart通信以及lcd显示字体。; K: `- e: a3 b& d, D& h9 q: {2 q

- [: c  R8 k8 C7 w5 p6 D/ o4 t9 v        现打开工程的(.ioc)配置文件,进入cubeMX配置界面,开启USB_OTG_FS的USB Device功能,参数保持默认配置。
* a) b  j6 g/ ~: D( P
4 i% k; m/ {( l/ I( B7 o- e1 E. j
0e2ab1db00174f8ca243e394bec74d24.png . u3 z" n: r1 b; p. A9 G, a) t  S; C
. Q/ t/ r+ o# q; ^
        确保USB_OTG_FS的中断功能已经开启。
2 H  p7 N3 q' `# ^1 _. ^4 u: @  S* F2 e4 U5 b9 W) N& ?
cbcb43dc403e4cc781ae49e349c631db.png
6 `+ t% J* h8 T( Q- }+ p
3 S' T: }  W/ b% h1 u, Z( V
         开启USB_OTG_FS的USB Device选项后,Middleware栏目可以去配置USB_Device信息,本文MCU作为USB_Device与笔记本电脑USB_HOST相连接,实现串口通信收发数据,因此选择通信类型虚拟串口功能,如下图所示,参数保持默认。
: m. ?' B7 ~5 Q* l6 a! C4 d; t. d% x! N
0ea91a79221747838f2cc568f27da59a.png ' F  W" t8 a( Z9 l
7 X) W! h9 g, u1 _9 t5 n4 C9 M
         设备描述符按默认设置:
4 k( c& r& e3 a4 F- N
9 x, m! G8 I/ b) O' I
7c5c236592f0408092b2ca0601aeea69.png / _  A/ G8 O5 V% l
" J: b( k& A" N; J
         由于USB驱动引入了中间件代码,并比较复杂,需要更多缓存支持,现进入工程配置页面,调整min heap size和min stack size为合适的值,防止因数值较少而出现无法编译、编译异常、运行错误等情况出现。) O  e  M3 W5 A0 ~, T& O

$ `1 F4 v$ e6 |) U( e* n
75294d6ff0ab476182e634d01bde6f2f.png
) X$ \$ {3 b' h) R
3 O* }1 j0 T0 k* H         点击保存输出生成代码,关于USB_Device驱动相关代码如下,其中usb_cdc_if.h/c源码是用户可修改源文件:4 N; R/ N5 @) q/ R  j# l! T

! |0 w% [/ |5 O/ a
38ca521a5d8e4882838bd57d4050b72f.png 6 i$ @8 k! c3 S
0 f, L, K/ |& K0 c9 E5 [% x
三、USB_Device驱动实现设计
7 Z5 {' ]# v8 B9 f& @2 R( B        【1】在usb_cdc_if.h中,添加USB相关全局变量(接收缓存数组、最大长度、接收标记及长度)
6 S8 B7 q$ [) s7 [/ ^
  1. /* USER CODE BEGIN INCLUDE */
    ( J" `, U# H+ p9 S8 A3 g* \# ?: N  B
  2. #define USB_REC_LEN   256//定义USB串口最大接收字节数
    4 @% E4 }5 G" M+ d; g7 z0 I
  3. extern uint8_t USB_RX_BUF[USB_REC_LEN];//接收缓冲,最大USB_REC_LEN个字节.末字节为换行符( d) O! h3 g" n
  4. extern uint16_t USB_RX_STA;//接收状态标记(接收到的有效字节数量)  h. f2 w  E0 M- V# I0 N
  5. /* USER CODE END INCLUDE */
复制代码

+ N6 x. _' l' l3 l1 Y        【2】在usb_cdc_if.h中,添加USB打印输出函数USB_printf声明
, A% d8 U  m- q+ S2 @& v1 l$ X4 c
  1. /* USER CODE BEGIN EXPORTED_VARIABLES */* I& x# r* I  ]: J; X/ c$ C
  2. void USB_printf(const char *format,  ...);//USB模拟串口的打印函数
    $ k' A1 }$ d) U! l
  3. /* USER CODE END EXPORTED_VARIABLES */
复制代码
% |: z; A+ B* I7 U2 q/ X  Y- s+ ~$ b

2 @: u$ u# Z: ~( @) B; L0 r        【3】在usb_cdc_if.c中,调整USB接收函数

! Y/ t$ ]# v0 {5 i- i
  1. static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
    ! q  y+ t3 q# U# g: @# Z
  2. {
    1 W. {) v, U$ X
  3.   /* USER CODE BEGIN 6 */
    7 ]- |* H: ]7 v# H6 A, x
  4.   //新增代码开始处3 O: ~& l& b1 [( |; M/ e' x* h* k
  5.         if(*Len<USB_REC_LEN)//判断收到数据量是否小于寄存器上限6 z7 t& v3 n% u. |# D& x6 r1 e
  6.     {6 H+ ^( Y- j" k0 z; ^5 E
  7.        uint16_t i;  x8 {. @) s9 E
  8.        USB_RX_STA = *Len;//将数据量值放入标志位
    4 w( E* g2 X3 y* I% Q7 T) `
  9.        for(i=0;i<*Len;i++)//循环(循环次数=数据数量)
    7 c+ |: x4 J$ N( U
  10.            USB_RX_BUF[i] = Buf[i];//将数据内容放入数据寄存器
    8 I  j% M0 s3 m1 |; {
  11.     }( Z$ P+ G1 [& o3 l2 T# H
  12.   //新增代码结束处, b6 s4 ]1 ?6 C  o
  13.   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);) ~' z6 u, N9 ~# z# B
  14.   USBD_CDC_ReceivePacket(&hUsbDeviceFS);9 g; t) @3 q- p8 b% x: A
  15.   return (USBD_OK);
    5 ~8 {6 |) X) V6 X) F3 g
  16.   /* USER CODE END 6 */; `$ S- E& C1 ?# u- X
  17. }
复制代码
5 g1 g5 V  k5 _& t5 r) z9 d: P
        【4】在usb_cdc_if.c中,调整USB发送函数
3 \" c- z8 I( r9 A' }  m! N
  1. uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
    ( z5 G, t+ @5 G' T6 c9 R
  2. {* I( D$ V- A1 A$ o( o0 K# o
  3.   uint8_t result = USBD_OK;
    % i+ c3 @2 m; H- ?5 {
  4.   /* USER CODE BEGIN 7 */
    ' ]8 l3 D# D2 n) P- ^' \9 S/ l
  5.   //注释旧代码1 V; t5 _) k& i5 z
  6. //  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
    % l/ w% R9 n* a
  7. //  if (hcdc->TxState != 0){7 j* O! q! H4 `6 h6 Z
  8. //    return USBD_BUSY;
    - C0 {) O- `/ T: c. M
  9. //  }, ~- E6 B9 F8 x9 \1 p/ ]4 n+ S% ]
  10. //  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
    + l( X1 |- x+ b
  11. //  result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
    : m: _: g" G& ]% D% x+ {; ?
  12.   //新增新代码
    & E, p2 P( A* s, |$ ]4 y2 y
  13.   uint32_t TimeStart = HAL_GetTick();
    8 i- c: l+ a. z
  14.   USBD_CDC_HandleTypeDef *hcdc =  (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;/ B& f3 s. {5 r" k% {1 c
  15.   while(hcdc->TxState): X0 C" c4 K1 j) [
  16.   {2 A  @' T' v7 b
  17.      if(HAL_GetTick()-TimeStart > 10)
    6 a# L3 @1 ]5 g3 F$ H- U+ |
  18.              return USBD_BUSY;5 c; S, c. N# q/ l% r
  19.      else
    8 S6 R8 O; r3 K  c4 X* `
  20.              break;
    / |. \6 X3 |4 Y& V0 r
  21.   }
    - O6 H" F+ P; w3 s
  22.   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf,  Len);
    . Q; n4 ~7 w1 @5 d8 Y4 @4 K
  23.   result =  USBD_CDC_TransmitPacket(&hUsbDeviceFS);
    4 }" c; D5 y- V) `# z* E
  24.   TimeStart = HAL_GetTick();/ R6 W- F. z9 r# M1 a
  25.   while(hcdc->TxState)
    ) ]  e/ N7 p" I" X! r7 F
  26.   {
    % S  Z/ L" r# c
  27.      if(HAL_GetTick()-TimeStart > 10)4 J) o! `0 `9 S, p1 l7 d0 |# u
  28.        return USBD_BUSY;
    5 K1 m4 U8 m3 s4 a: n/ s
  29.   }+ c: J0 E$ d4 w7 j& ~# ]
  30.   /* USER CODE END 7 */% Q& V- w! k/ V, C4 U* o+ R
  31.   return result;
    3 }. @$ t* |& U# p) T/ I. W
  32. }
复制代码
+ k! n1 q2 G6 g& }4 ?" _# x
    【5】在usb_cdc_if.c中,实现USB打印输出函数USB_printf定义
2 J4 c  y$ B$ i% S5 h0 u$ [
  1. /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */; c% z8 n# V; Q1 \8 e+ k( U
  2. #include <stdarg.h>* g6 Y/ d" v: z: D
  3. void USB_printf(const char *format, ...)//USB模拟串口的打印函数
    * `9 \4 J% Q% P2 g* ?! D
  4. {% p/ `& M+ B" I. r( \( A
  5.     va_list args;2 U! p$ \  y# @/ \5 p$ U5 `% E0 b+ a; k. m
  6.     uint32_t length;! o. q0 g  D) [+ i, E
  7.     va_start(args, format);1 n. n3 K! d9 H# x
  8.     length = vsnprintf((char  *)UserTxBufferFS, APP_TX_DATA_SIZE, (char  *)format, args);
    ' @7 C! e' e% k3 F! s$ ^/ P) R
  9.     va_end(args);
    ) E* K8 X, O9 j: I+ }
  10.     CDC_Transmit_FS(UserTxBufferFS, length);
    # v7 _' A7 Q, f7 N
  11. }
    9 P: @0 g/ R. P/ y5 e
  12. /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
复制代码

$ R! d1 l8 c+ g+ h1 t. [1 Q. d" d        【6】在main.c文件中,添加USB驱动头文件支持
& A( D! C9 S& Q! ^8 \) {
  1. /* USER CODE BEGIN Includes */
      z  d2 a- S6 z3 b" u
  2. #include "../../ICore/led/led.h"
    , q7 g& k1 {: e, J
  3. #include "../../ICore/oled/oled.h"
    ! d  s9 s% ~# {0 b+ Y+ X% B
  4. #include "../../USB_DEVICE/App/usbd_cdc_if.h"* I! y  i* Q' I% w# x5 Z
  5. /* USER CODE END Includes */
复制代码

9 a, r# b6 {7 u/ {        【7】在main主函数初始化处5 E2 q/ v5 q6 T; l4 ^# ~0 O
  1.   /* USER CODE BEGIN 2 */
    " k2 e% u3 c6 p2 d
  2.   OLED_init();
    . H3 Q/ ?2 C& J. k9 }
  3.   //设置OLED蓝色背景显示. y# F# T! {1 N5 c
  4.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    ; X1 Y4 c5 L# M% ?3 _- |
  5.   printf("OLED_Clear_DMA\r\n");/ y$ X6 l) U' i+ F0 K
  6.   /* USER CODE END 2 */
复制代码

- @1 v. G8 f3 R3 s8 z! B3 K5 ^% H【8】在main主函数循环体内实现1 N2 w6 P# u/ y4 U  g
  1.   /* USER CODE BEGIN WHILE */4 x& m5 i4 l" q# {  c& v) B
  2.   while (1)  ?& A; q8 I& w- G" j; Z
  3.   {
    1 h+ Z% c/ K) q. ]8 ~5 k) r
  4.           //USB模拟串口的查寻接收处理(其编程原理与USART1串口收发相同)
    0 a  k( i9 l4 d( [! f3 I: a$ S5 J+ o+ b
  5.           if(USB_RX_STA!=0)//判断是否有数据
    ' A7 w8 l+ _3 I3 L$ X
  6.           {6 L7 `5 q# p; k: E% g1 Y- Q  ]
  7.                   OLED_printf(10,42,"%.*s",USB_RX_STA, USB_RX_BUF);
    1 v1 `( H- _0 H
  8.                   Toggle_led1();
    ( k6 y# B7 C# Z: N% u3 }+ x4 h
  9.                   USB_RX_STA=0;//数据标志位清0
    - F- D3 D/ S6 H4 @
  10.                   memset(USB_RX_BUF,0,sizeof(USB_RX_BUF));//USB串口数据寄存器清0  g7 ?' m% R0 b; t# G1 ]4 U. [
  11.                   HAL_Delay(10);//等待/ a7 D; H# B5 e# `  j
  12.           }
    % O# v. Y* P5 u1 B* S
  13.     /* USER CODE END WHILE */
复制代码

; f& F0 g+ _8 \! @' r# b8 I四、编译及下载* l. W* j$ ]* X& [" k5 F% Y
        编译下载,该开发板原理框图,需要将跳线帽重新插拔设置,相当于原理的USB-ST-LINK连接到USB_MCU上,即原理lpusart通信及ST-LINK下载的USB接口改为连接到USB_MCU的DM/DP引脚上实现通信。
& C7 t( l7 ]; k  [  K5 g/ t. h- T4 X- h: E8 q6 F- w2 a
59a80db11f4f4dbb80fdd21b3a5c67e8.png ! D7 j3 Q* i  X2 u7 R6 ]2 p

4 _2 p! E6 h, J" `) ]/ z  a/ }4 p         更改调线帽后,打开串口工具,发送数据,顺利在点(10,42)位置绘制输入文字,LED1等会切换状态。+ i: p4 o6 A) {" K, I7 f  ]7 e
2 q! F/ B9 X3 I
97a35a8bc13b42b39461355775943d6e.png
3 J5 ^7 ]) b: L  O6 m
5 T8 {, M1 U; P6 w+ ]; v————————————————
, H- T6 m5 k, _' \& x版权声明:py_free-物联智能
/ _! W5 D5 V1 B' x3 k2 b! K4 V2 ~! e如有侵权请联系删除
. k/ o6 c  d$ L  O; L( d4 E! ^3 K; j+ f! }5 E

6 \6 @0 ^& ^: G; v7 d
收藏 评论0 发布时间:2023-4-6 17:47

举报

0个回答

所属标签

相似分享

官网相关资源

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