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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-6 17:47
一、USB_OTG简介
: A8 r/ y- }' k7 F- ^6 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 的底层驱动。' X" p' K% o" a

" O: `+ x) g8 m! V6 }4 D        stm32芯片的通常USB物理接口管理两个引脚:
1 m; X- T+ I9 R3 r

( Q" V6 N5 c2 @% |            DP/DM:内置上下拉,由控制器来设置不同类型的需求+ I! ^0 P1 w# A4 C
( x2 t8 J. p) L6 H2 S2 G* V
        OTG实现时,额外配置ID引脚:
" u. Z# S7 d. S6 F5 W. r            ID:检测插入的线是B端还是A端,用于区分A类和B类设备,
. Y. w' w$ q% f! L- y7 c
$ h8 y# m7 m- @) w        USB Host 模式下,需加配置Vbus引脚:! @* _8 V9 Y: N9 V0 f6 x8 w$ |
            Vbus:内置检测器,用于测试UBUS的有效性4 _+ e; u9 I3 c4 E1 r

& Y2 v8 t% p5 E" L2 x) }& T        额外地需要支持SOF和NOF时,需要加配置SOF和NOF引脚。
3 L9 F/ S! A: ^) p* t
9 n! c. ^0 l- u& E4 d( y        例如本文采用的STM32L496VGT3芯片,其配置支持到的引脚如下:
2 M+ v$ |  ~  @8 g, s
. e7 |2 S7 u; ]( o
946bf813d73844edb8f5d5b509d33692.png
, E$ z; n! n1 V! e3 y, c/ |
& y& a" Q  n- a$ U
USB协议栈的层次划分
% c( g* g; o: z" u8 I+ y    一个Host可能有一个或者多个Device  Q/ @, X# P" h. \7 {3 D
    一个Device可能有一个或者多个Interface' U& E# p& y/ W+ F! u
    一个Interface可能有一个或者多个Endpoint# p. q( J: c! x/ X# {* T
        Host(主机)连的是Device(设备),这一层是走物理连接的,也就是信号线实际连接两台设备。
' A) Z; z- h9 i5 A0 J) ?+ X' C: w6 `        Device(设备)下可能有多个Interfece(接口),从这开始是逻辑概念,一个Interface,就是一个独立的功能接口,每个Interface模拟一个设备功能,比如集成了键盘和鼠标的USB设备,里面就是两个interface,一个是键盘,另一个是鼠标。Interface之间通常是隔离的,互相不干扰。5 v( R9 t: a2 E. \
        每个Interface(接口)下面有一个或者多个Endpoint(端点),这也是逻辑概念,例如控制信号端口、数据信号端口等。端点是USB设备通信的基本单位,所有通信几乎都是从端点发起的。, V9 ?2 G) h: a9 }3 A
) A7 s6 n! o% f/ e7 J" J2 z1 C

2 ^$ t# v' L- B2 I二、创建工程及USB_Device配置
9 i0 u$ T/ S2 K/ s        本博文基于STM32L496VGTX3芯片实现
2 M2 T: z) ?6 l3 {
( Z+ \% n5 A8 y$ ], _) Y        基于该工程的(.ioc)文件创建了新工程stm32L496VGTx_USB,并移植了相关源码,实现了lpusart通信以及lcd显示字体。3 R! z. A3 h$ {( E
/ h& J  z/ t" J) h, ^) K
        现打开工程的(.ioc)配置文件,进入cubeMX配置界面,开启USB_OTG_FS的USB Device功能,参数保持默认配置。
  O9 D2 T6 q  p; a; F4 I' }" O% G4 G
0e2ab1db00174f8ca243e394bec74d24.png
) b- v6 x2 N1 e; h, k- X. o, V+ X
4 Z1 ^& s4 T' ~% _        确保USB_OTG_FS的中断功能已经开启。
) Q4 u% P; G% U1 Z- Z  r
" q) |/ e% r% ^; E( U
cbcb43dc403e4cc781ae49e349c631db.png 8 \, z3 j3 l, i; l. T
* q) r- T5 x! e
         开启USB_OTG_FS的USB Device选项后,Middleware栏目可以去配置USB_Device信息,本文MCU作为USB_Device与笔记本电脑USB_HOST相连接,实现串口通信收发数据,因此选择通信类型虚拟串口功能,如下图所示,参数保持默认。
7 M; b% p+ D, P9 T! a8 p0 F% {. f! K- p7 C* Z/ I
0ea91a79221747838f2cc568f27da59a.png
5 Q) \  n4 f) ]* i. Z: I* \
. z4 a4 b, ?: Y' \# A+ e+ P         设备描述符按默认设置:: o" c/ [! w% U1 x% X1 p! I
3 w* D0 g$ e! k5 h1 l' w( ^" ?* a
7c5c236592f0408092b2ca0601aeea69.png
0 X, _& _/ ?$ D; S! n* T- A- t/ M) I- k0 W- {
         由于USB驱动引入了中间件代码,并比较复杂,需要更多缓存支持,现进入工程配置页面,调整min heap size和min stack size为合适的值,防止因数值较少而出现无法编译、编译异常、运行错误等情况出现。
7 d: ^4 m- _" d8 \, G/ z. r3 `; |# ~6 m/ F
75294d6ff0ab476182e634d01bde6f2f.png 5 d0 i: }8 c5 Q, h/ ?
! o( h( X0 ~: B# R& O; L: D/ h9 _
         点击保存输出生成代码,关于USB_Device驱动相关代码如下,其中usb_cdc_if.h/c源码是用户可修改源文件:
5 V! [1 o, l/ g  m4 B3 p
" ~2 S) V  {- G) b* O# G. L; b
38ca521a5d8e4882838bd57d4050b72f.png
, y2 Y. V; a% Z3 `8 D# a  I, f6 A
" {( I' u  r- U 三、USB_Device驱动实现设计/ ~0 v+ |1 c: P. u. s* e
        【1】在usb_cdc_if.h中,添加USB相关全局变量(接收缓存数组、最大长度、接收标记及长度); K: s& y7 ^+ `8 |% u
  1. /* USER CODE BEGIN INCLUDE */
    , L, t5 o& k- |, _. g
  2. #define USB_REC_LEN   256//定义USB串口最大接收字节数
    - p$ j' ~# k( ]. r/ B* t
  3. extern uint8_t USB_RX_BUF[USB_REC_LEN];//接收缓冲,最大USB_REC_LEN个字节.末字节为换行符
    " {5 e* ?: L& g% V0 C/ x
  4. extern uint16_t USB_RX_STA;//接收状态标记(接收到的有效字节数量)
    : Y+ I$ {, Y- v
  5. /* USER CODE END INCLUDE */
复制代码

$ L+ c- d  r! E8 g        【2】在usb_cdc_if.h中,添加USB打印输出函数USB_printf声明  x5 T1 Y8 r1 F  ~0 c3 l. z
  1. /* USER CODE BEGIN EXPORTED_VARIABLES */
    % H6 K/ s! L- K! \2 v
  2. void USB_printf(const char *format,  ...);//USB模拟串口的打印函数
    . A* P4 g' L6 K. f4 Q6 S' g* g0 X
  3. /* USER CODE END EXPORTED_VARIABLES */
复制代码
  U' D8 N0 F+ c  \  Y. X
0 V1 N1 ]/ c! L" Z2 Y& l. a
        【3】在usb_cdc_if.c中,调整USB接收函数
& L1 t0 ^% T- ], o- i
  1. static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
    + g9 ^0 K* C# g; X
  2. {4 R3 D% Z; @. M/ ~
  3.   /* USER CODE BEGIN 6 */
    * @3 s0 ]% _- ~5 t5 D' E5 k
  4.   //新增代码开始处
    4 c0 c! ~: G: d
  5.         if(*Len<USB_REC_LEN)//判断收到数据量是否小于寄存器上限  V* g! L5 d/ F. h( ^
  6.     {
    5 [; Z1 L4 e6 c6 b' |
  7.        uint16_t i;/ v7 ], ^. @1 o
  8.        USB_RX_STA = *Len;//将数据量值放入标志位! |: s* E6 i8 `) I* ?3 X! m
  9.        for(i=0;i<*Len;i++)//循环(循环次数=数据数量)4 m( w' v1 c& F" A% B
  10.            USB_RX_BUF[i] = Buf[i];//将数据内容放入数据寄存器9 G# L. a4 `8 f4 W) `
  11.     }% g# S3 Z/ D  R  c1 z/ t- {1 o! ~
  12.   //新增代码结束处
    7 R- {# |1 V, E- a" P) }; P, T
  13.   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
    # L+ j; G' b0 ]. |
  14.   USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    ) B* p$ [; G. K9 f& `
  15.   return (USBD_OK);3 }7 k( G1 a' h* N
  16.   /* USER CODE END 6 */" m- Y& L/ d  ?; e. A
  17. }
复制代码

) G( ]+ E0 b# g* M; W: ?& }        【4】在usb_cdc_if.c中,调整USB发送函数
4 R- K5 @4 q; M) ?
  1. uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)- a2 R5 I7 S. G8 U: G  U3 s
  2. {- i# S/ r; v! ^- P6 _
  3.   uint8_t result = USBD_OK;
    - L) v3 q5 P5 \2 j+ {
  4.   /* USER CODE BEGIN 7 */
    * _) ^9 d2 f! [0 @
  5.   //注释旧代码
      w6 L0 t8 f; M# X1 V3 Y$ M/ |
  6. //  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;7 q5 |& b- h' |- P
  7. //  if (hcdc->TxState != 0){/ W! [' c( y4 J" R$ e7 p
  8. //    return USBD_BUSY;
    $ P  G4 K" g2 D
  9. //  }8 y) A7 m: F& }/ q- J  t$ ^$ V
  10. //  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);2 ~4 ~1 V9 q0 X( q* y: A9 s5 N3 F: B
  11. //  result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);' u0 o# M1 _* {- k8 Z
  12.   //新增新代码. q( M/ |6 c! j- e' T
  13.   uint32_t TimeStart = HAL_GetTick();
    ! n) m) Q) h. ~) y3 }; J" |: \8 O9 \
  14.   USBD_CDC_HandleTypeDef *hcdc =  (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;2 I" t  u1 \& _' n7 T: r! n" _
  15.   while(hcdc->TxState)- b, F" G/ j  b. t& o
  16.   {
    1 X2 |1 N: X# s' k% x" O1 H0 v6 Z
  17.      if(HAL_GetTick()-TimeStart > 10)
    9 F3 T' Q1 l9 f, J
  18.              return USBD_BUSY;
    4 j! z3 p6 A7 o
  19.      else
    1 S, _; i- k1 k" [
  20.              break;
    ) b( Y6 z5 w' i5 d" _" R- l
  21.   }/ x; Y, k. i: Y9 [9 Z: g+ ~( V5 F9 K1 v
  22.   USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf,  Len);
    7 ?2 z. C& \7 [
  23.   result =  USBD_CDC_TransmitPacket(&hUsbDeviceFS);! `2 P9 P  }: S1 |+ c# a
  24.   TimeStart = HAL_GetTick();
    0 T$ Z8 q0 l' @7 z* u6 _+ D
  25.   while(hcdc->TxState)9 L2 c* u7 j( T% o- F
  26.   {4 c5 W3 K" R: g% r" n; ]
  27.      if(HAL_GetTick()-TimeStart > 10)1 A0 `6 H* s* I% T0 a
  28.        return USBD_BUSY;- a! ^  o0 Q8 j9 i2 o* S9 S
  29.   }
    : `5 w! i3 v) p3 i( e" P
  30.   /* USER CODE END 7 */$ L6 H2 Z4 ?1 T+ {
  31.   return result;
    8 p9 w8 w& F- [6 e# C" o4 D# g
  32. }
复制代码
: P$ S" A- W- t6 M: t3 H% \
    【5】在usb_cdc_if.c中,实现USB打印输出函数USB_printf定义$ o* L: O3 |' X, O2 s' L
  1. /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
    $ I" d- m8 S) M1 Q3 v
  2. #include <stdarg.h>1 |0 ~( u# c/ t9 _! V
  3. void USB_printf(const char *format, ...)//USB模拟串口的打印函数
    ; D9 Q! ~! ]' e: d' O5 H  X
  4. {
    , b# `1 s* m8 ?
  5.     va_list args;. }7 x& Y* S+ J- d8 N0 Q
  6.     uint32_t length;
    # A0 G, P6 [' [& D1 K7 ]) a- w
  7.     va_start(args, format);
    + T- c1 b4 y2 P: V  r7 I
  8.     length = vsnprintf((char  *)UserTxBufferFS, APP_TX_DATA_SIZE, (char  *)format, args);
    % q' q. I" T! W! h
  9.     va_end(args);
      `: ?% p! q7 v* Z# @) v
  10.     CDC_Transmit_FS(UserTxBufferFS, length);2 W  _% s: P1 n' Y
  11. }0 C2 Z; h* Y7 k
  12. /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
复制代码

, N1 E3 T& L+ A; t        【6】在main.c文件中,添加USB驱动头文件支持
" \" J. C) S4 a3 u0 K$ g* J
  1. /* USER CODE BEGIN Includes */5 @  U. }/ d" l0 F
  2. #include "../../ICore/led/led.h"
    9 A7 g+ O. e8 s. n) M$ f& Z' i
  3. #include "../../ICore/oled/oled.h"3 M6 [+ v9 J# `5 J8 p5 g/ `( r! x
  4. #include "../../USB_DEVICE/App/usbd_cdc_if.h"
    ( C' R& l% M4 [+ D# N
  5. /* USER CODE END Includes */
复制代码

2 n8 h" F3 m) ^$ P/ K  u, F        【7】在main主函数初始化处
# O! [' K$ Y/ A! K) @9 Q$ K
  1.   /* USER CODE BEGIN 2 */
    5 F" S  W$ J+ p4 B! w/ |
  2.   OLED_init();
    + K/ q% N6 [6 p$ z8 j: |! x! l0 O! a- r
  3.   //设置OLED蓝色背景显示
    5 i, T+ L5 W1 L" b
  4.   BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    . t# u  z8 x8 ?8 h" v' s
  5.   printf("OLED_Clear_DMA\r\n");
    $ f, [" d0 F; b5 }' z
  6.   /* USER CODE END 2 */
复制代码
3 U) `, f$ \% z' K
【8】在main主函数循环体内实现
) G4 \$ `7 c, H) x& o0 z$ O
  1.   /* USER CODE BEGIN WHILE */
    0 z: O" h' i( O" |
  2.   while (1)9 x8 _' M1 {7 ^; f1 M* t# D
  3.   {+ O6 N3 O0 j) `. Q1 |
  4.           //USB模拟串口的查寻接收处理(其编程原理与USART1串口收发相同)  T/ f2 {( i4 ]: l; M9 k
  5.           if(USB_RX_STA!=0)//判断是否有数据
    9 s& J; W! t# `# _) Y
  6.           {4 S$ H. t& b: [' r$ S$ H; P0 c
  7.                   OLED_printf(10,42,"%.*s",USB_RX_STA, USB_RX_BUF);3 p7 Y  H1 N, D7 c# C
  8.                   Toggle_led1();
    ) P& e( r) x* R* e4 d
  9.                   USB_RX_STA=0;//数据标志位清05 O/ M' w* e. ]; x
  10.                   memset(USB_RX_BUF,0,sizeof(USB_RX_BUF));//USB串口数据寄存器清0
    + H7 V! n0 [( f# y2 h( @
  11.                   HAL_Delay(10);//等待: a/ v. j' g: D# G7 W1 r: t7 z
  12.           }
    , ?' s) A  q* a1 k( V3 S( ]
  13.     /* USER CODE END WHILE */
复制代码
' c; x1 Z8 T+ p* K9 ?
四、编译及下载
/ I. S1 T/ b2 ]1 b( H        编译下载,该开发板原理框图,需要将跳线帽重新插拔设置,相当于原理的USB-ST-LINK连接到USB_MCU上,即原理lpusart通信及ST-LINK下载的USB接口改为连接到USB_MCU的DM/DP引脚上实现通信。
  h- Y1 K5 A4 i( z' @
: V4 x6 E+ E9 E- k  n8 ]6 Z8 F
59a80db11f4f4dbb80fdd21b3a5c67e8.png
' A' O/ Y( [( Q) m8 e8 N# a2 c2 [  [7 p' y6 Q$ [8 Z6 R3 @
         更改调线帽后,打开串口工具,发送数据,顺利在点(10,42)位置绘制输入文字,LED1等会切换状态。& A, W* O3 J. l/ t
% G& s, }9 j9 }1 d
97a35a8bc13b42b39461355775943d6e.png
2 k9 U6 @* Q* y: a1 G: Z
3 @2 g. P# t, R' G1 x3 L5 b————————————————7 W& _8 e$ k0 N+ Q4 k6 p; }- e8 v
版权声明:py_free-物联智能
8 z& l- p- t6 r8 h0 ?如有侵权请联系删除
. h/ u7 M6 J2 n$ ^& Q; Z
9 W# w7 u; T4 p7 {
; m0 C1 w9 U7 r8 F) J
收藏 评论0 发布时间:2023-4-6 17:47

举报

0个回答

所属标签

相似分享

官网相关资源

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