1、配置时钟
6 _5 X4 _4 T& Y$ Y ~, C/ a E& q' ?+ T T$ ~& k+ q3 W
, b: v' Q, b% Z: {: A9 o
& R: _( d2 p( t( u
2、开启USB: I: |" u8 I4 g, E1 L
+ b& \: y/ t7 ], z& y- c, A6 I) M
) N( S* o; ?6 Y+ F; y* U1 P: Q, }1 G
3、开启USB设备:虚拟串口
$ A, d. ~$ P" E% h4 l1 n
6 f% u0 Y; x$ o" M
1 a3 e- U! {& f; }' p8 u# c
( g* f) c6 A3 { {4、生成工程8 q6 I9 m! N* R, V f, G
1 c& L8 m+ l% E; T6 `4 g' j
5 s0 k; s1 w+ C) `3 }$ G9 A
- U( C# z3 P) A6 ]; P
% M% {) e+ b8 P& @$ o6 Z
1 C) @% s" a- T( `7 X5、修改代码实现回环收发数据测试. g7 R+ I x' q. ]: G
在usbd_cdc_if.c文件中新定义一个结构体:1 j; g; x6 X2 ?% z
$ ~, \1 _6 B1 V3 [! f
- USBD_CDC_LineCodingTypeDef USBD_CDC_LineCoding =: F. a4 M; R& N8 w
- {
: W& b0 V& f% `* \1 I/ f O- W - 115200, // 默认波特率: L1 w) H4 W7 n" H# V
- 0X00, // 1位停止位
' w& C+ U: d- z8 s$ e1 ^1 O - 0X00, // 无奇偶校" r/ @% C9 R1 e
- 0X08, // 无流控,8bit数据位# r1 I% `7 Q3 B5 N7 W g1 z
- };
复制代码 2 `* r+ I4 R) C$ Y
找到CDC_Control_FS函数,找到CDC_SET_LINE_CODING和CDC_GET_LINE_CODING分支,添加以下代码:
! W2 f$ F1 g2 [0 ]0 @6 z$ T1 v1 k(CDC_SET_LINE_CODING:你在用串口助手选择波特率时候,STM32就会调用这个分支进行修改USB波特率)
, r1 C$ [6 d1 L) ^. |(CDC_GET_LINE_CODING:获取STM32的USB波特率)! M) O8 S+ Y8 k. O
+ s; o% T5 x' ^3 ]1 ^5 [2 \
- case CDC_SET_LINE_CODING:( H, _( n0 a. C H+ k( [: d
- USBD_CDC_LineCoding.bitrate = (pbuf[3] << 24) | (pbuf[2] << 16) | (pbuf[1] << 8) | pbuf[0];
- }- X( m, e/ R0 O - USBD_CDC_LineCoding.format = pbuf[4];
6 W, I0 U. r# N, E% V - USBD_CDC_LineCoding.paritytype = pbuf[5];0 l8 v/ _- m' Q- e, k f# Y
- USBD_CDC_LineCoding.datatype = pbuf[6]; Y9 T# r/ o& d% k. _
- break;5 }5 Z5 F: j2 A3 }: e9 F, D
- 7 h% F4 }! N1 Z% e0 Q2 q5 F) W6 H
- case CDC_GET_LINE_CODING:
~, [6 ]+ j7 C+ \ - pbuf[0] = (uint8_t)(USBD_CDC_LineCoding.bitrate);! N6 q' [4 |" `1 @
- pbuf[1] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 8);% h+ a! w# f7 N7 {( \# u! E4 K8 l' f. {
- pbuf[2] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 16);6 h; I: e9 \) ?$ z$ `
- pbuf[3] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 24);! F( a+ `$ d8 A# H8 |4 T
- pbuf[4] = USBD_CDC_LineCoding.format;
9 }5 C. l# a) I) }* ~! \2 c1 J4 ] - pbuf[5] = USBD_CDC_LineCoding.paritytype;$ Y! `( t' L! d+ g" M' U
- pbuf[6] = USBD_CDC_LineCoding.datatype;
8 p" {1 u" W R" m8 {' E, N - break;
复制代码 : i8 z" c+ {6 E9 k$ ?5 h
8 E+ \$ M9 G, a, F
如下图所示:. J2 D0 ^$ l' W8 R3 V0 b
! P# v+ B& {7 ^2 O- n
& x3 o3 v; n9 @' S. B& v2 b% w& T0 y- w& ~( f8 Z7 T9 U& u6 C
找到CDC_Receive_FS函数,这个函数如果USB虚拟串口数据收到就会被调用,我们在这个函数中将收到的数据在发回去,只需要添加CDC_Transmit_FS(Buf, *Len);这一句即可,如下图:# s7 U/ j4 Z" @' j; x5 o5 @$ B
/ n0 T" a9 Q# t: P6 J
! k; S; Z( r3 e0 |/ b& t
' {. i/ m1 x% L( c% U) y8 T2 D然后编译工程并下载,接上USB之后,设备管理器COM出现一个新的端口:5 O3 ]2 F6 _ M( r; H4 P% g2 @
* _' F! ^7 Y6 p5 _5 C! }: C
5 B, @2 I% b/ i+ |+ C3 s% y) _+ j
/ p( _3 ^& u+ [4 s, U+ R我们使用串口调试助手给它发数据:
+ B; j, Y0 g7 p. m$ I5 u& U# M% n: {0 D' r* f
/ `7 D, B! H4 p& D
9 l2 F; T! s4 }# z" q
6、实现USB转串口功能% N/ I8 ?& g/ e: X; }0 \
发数据流程:串口调试助手发送数据->STM32的USB数据接收->STM32转发到串口3
3 k. t4 n) w+ y收数据流程:STM32的串口3收到数据->转发到USB->STM32的USB发送到串口调试助手/ z: H* s* q# d* W! B6 t
, s4 n2 f9 I/ b6 e4 x: V8 x8 x第一步先在这里加入串口3的初始化操作:! |: Q- Y: i$ x. `- a
$ b# d" k$ D8 X* C( g+ e+ Y; S
- G/ j+ M# o( V! Q+ g* ]
8 @/ P+ }) Y& z贴一下串口3的初始化代码,这里我用到了队列,因为实际测试发现串口3接收数据量比较大的话,那么转发到USB虚拟串口的时候会丢数据,所以这里采用了缓存队列,当串口接收到的数据到达一定数据量之后才做一次转发到USB的操作,并且开启了空闲中断,作用是转发最后一包数据:: a# L% {5 d" b4 g8 Q
* q9 ~* U" H% b2 ]& @) r, G& A
关于队列的使用可以查看我的另一篇博客:C/C++语言实现的一个缓存队列
' v5 V$ {% ?* |1 R& L4 W0 h) |3 e' |: j" }& P
- /*
: g8 U- \. Z! t# D4 q* V - * myusart.c
2 O* K: m0 [9 \) ^- I) h - * b5 I. ~+ m/ Z _# a
- * Created on: Mar 22, 2021
3 K- ?4 Q' B! @ - * Author: hello
9 B' {" }' l* o" {% i4 h- Q9 s% } - */0 t" S7 `" q4 o1 b
- #include "myusart.h"
1 }# R. I8 U) C1 u7 h - #include "myqueue.h"; @5 v7 Q8 M1 O E: H) S' ?
- 8 c) W5 F: A. u$ x8 m8 [4 j9 s
- // 队列大小,定义为USB_CDC的发送包大小
! x( m/ _ K: [ - #define QUEUE_SIZE APP_TX_DATA_SIZE% M5 U2 d! j4 N
- 0 g" _: {. m! @. `' B, T/ @
- // 队列数据空间。请使用宏QALIGN4,目的是为了根据队列大小计算实际需要的队列存储空间大小并对齐4字节; E$ e2 M0 I% [' Q( W( f
- uint8_t QueueBuffer[QALIGN4(QUEUE_SIZE)];
; j0 _4 o2 V0 [# g
9 @; l( n6 v# R+ v* ~( w& p- // 队列句柄
; b# h+ X8 Q$ o6 U- p - Queue Uart3QueueHandle = {0};
$ p6 S$ }2 b: B G9 ]' q/ g - #define UART3_QUEUE_Handle (&Uart3QueueHandle)
0 X( L" i4 t: v1 ~+ ]: g
8 }$ A% C0 R& C. [- // 串口转发到USB的缓冲,定义为USB包的一半大小
+ c4 z; g/ K% j$ D) T% H - static uint8_t buffer[APP_RX_DATA_SIZE >> 1];0 P; p6 E" A8 u/ w) @2 z: u3 `
- - R9 c; Z/ y0 d% g
- void UART3_Init(const USBD_CDC_LineCodingTypeDef *USBD_CDC_LineCoding)
A: c' L2 t, u( Y( p: _ - { f C2 F7 F; w0 h
- __HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE);
: k! c0 }: s' B, p - . O+ s2 E" f9 G
- __HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
0 E& V% x, D1 B
! m: f5 D! o' e7 A1 X+ C- HAL_UART_DeInit(&huart3);
; {3 [" r' q, {1 y5 B, G
& x9 |7 m9 p9 p: h( U# n* p- huart3.Instance = USART3;9 y: ^$ G# ]7 I9 l! \, ~3 [3 |
- huart3.Init.BaudRate = USBD_CDC_LineCoding->bitrate;
1 z7 Y* M$ p% S/ ]. f" e* f! R - huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
, z. q5 q' ^! a/ \ - huart3.Init.Mode = UART_MODE_TX_RX;! x/ f) h7 _1 K& J+ ?
- huart3.Init.OverSampling = UART_OVERSAMPLING_16;
# z4 \- }! S1 j/ {3 Y6 O
! n/ o9 V i# m/ _) h D1 \9 [' }; P- switch (USBD_CDC_LineCoding->paritytype)
) ]% B; J! {* _, V - {! {6 |3 l( A4 k! Y2 n
- case 0:
0 r- z' B( Z- J* z' q8 r! L - huart3.Init.Parity = UART_PARITY_NONE;
+ \5 T6 \. u* Y7 n3 R - break;
9 a! O0 G2 j7 k, q+ q - case 1:
# G7 X5 ^1 s( v* B) _3 z - huart3.Init.Parity = UART_PARITY_ODD;8 {: ^, z1 R T/ ~
- break;; S* F" b/ h9 x( q: w
- case 2:( g8 U: M+ ~& h: R& @; h9 [8 a. E
- huart3.Init.Parity = UART_PARITY_EVEN;( \" g1 v% Y: i* S4 }3 u' a
- break;
* h" _2 w2 g6 H7 I2 z) V: `5 V - default:+ G7 Q( f/ H+ }( h
- huart3.Init.Parity = UART_PARITY_NONE;
* M2 C2 k: j. p2 M( I, b. e - break;
: r$ {$ j( D0 s. G5 A, j5 K# H - }8 Y6 }* ^6 Y; D' O7 Z
! A- q( R9 q* I3 b6 k- switch (USBD_CDC_LineCoding->datatype)( f9 A. D* l" l% M- q
- {
, ?1 y* m2 S/ B9 `8 V2 a9 j4 _ - case 0x07:
) a9 \/ \9 x' [- n2 U( C' \' `2 Y. M - huart3.Init.WordLength = UART_WORDLENGTH_8B;/ j& G/ \0 d9 F( e
- break;
7 ]! L" j* z- O7 z* ^ - case 0x08:# g0 V0 X& V7 S6 c) h
- if (huart3.Init.Parity == UART_PARITY_NONE)) c0 L, r& U) \# t
- {! E3 {2 m& B% `0 [& d
- huart3.Init.WordLength = UART_WORDLENGTH_8B;% {, Y/ U5 n! }
- }
2 s0 @- v0 x) E7 `+ ~; ] - else: B0 O; l' Y: J9 W
- {
! G; E. ?/ M* ? - huart3.Init.WordLength = UART_WORDLENGTH_9B;, a) j9 Y7 E7 h' x) ]
- }
4 ~" \8 u; }& F$ J2 V0 t - break;
! [9 E+ r! |& F5 P - default:# ?- _# }: @ E) i z0 i7 ?
- huart3.Init.WordLength = UART_WORDLENGTH_8B;
2 K! {6 Y6 ^% |) Z* t - break;% a+ x; ?) {/ L: n
- }
6 I. D" H! ?3 t0 Y+ }, h- @
( F% D: m5 B D- switch (USBD_CDC_LineCoding->format)2 k9 e2 W" \4 f- g9 @; j
- {
8 q. w2 q3 [7 G - case 0:
# r) |* J( `1 ?9 j - huart3.Init.StopBits = UART_STOPBITS_1;
7 b, E* y9 g8 [0 \; y - break;
8 y4 n% l" h& m D - case 2:
7 e! [ U! _' F3 m$ V( @ - huart3.Init.StopBits = UART_STOPBITS_2;& E& c6 R: }$ O* L2 o% p2 a* R! w. k
- break;
6 J$ @- g& d" g - default:% r7 `: `7 Y: ?7 U k
- huart3.Init.StopBits = UART_STOPBITS_1;
# R6 a: B7 O. j - break;
$ j* O$ I/ f! g4 n/ t - }
( O* e5 N" ~$ n- d, c4 K - C# l& c/ D" S! S6 l- p$ k
- HAL_UART_Init(&huart3);
" \# d# A& s" ~6 V - 7 A' q3 t; _; f4 s& K( Z0 y! z
- Queue_Init(UART3_QUEUE_Handle, QUEUE_SIZE, QueueBuffer);
3 J9 L! F! L" P9 E4 s - & a" ^0 Y# \6 N6 l; |0 F# @
- __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);6 O e7 y3 K4 ^! U
' Z) l+ O& }! ]" ^9 e0 `1 ^- __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
- d/ D( y# s) H" x - }
% W/ Y! l1 t) E5 `7 ?
1 `3 M2 J; ^: s' R3 t- void USART3_IRQHandler(void)
. D5 f7 {, ~3 F7 `: @( q2 g5 \ - {
) U- @2 v. E; j - static uint32_t nsent = 0;3 T# l+ j% `. A7 g/ z
- static uint8_t dat = 0;) K1 Q6 Z( S7 }0 [. W0 R* T
- ' t$ U3 W' E5 s$ N3 v& @
- if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)
/ d! F+ E4 {: z6 R8 p- G* y - {7 R4 _" f4 N2 j3 s
- __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_RXNE);
/ B. }, P8 h; H5 g - dat = huart3.Instance->DR & 0XFF;
; A9 R% { g3 b - Queue_PutByte(UART3_QUEUE_Handle, dat); // 入队一个字节的数据
- C" ^# y. ?3 _ - if (Queue_GetUsed(UART3_QUEUE_Handle) >= sizeof(buffer))
3 _3 Y' d; L9 D: [% D; a1 M - {0 ?7 b0 Q- ]3 z/ t( W6 k
- Queue_Read(UART3_QUEUE_Handle, buffer, sizeof(buffer));
" D: [/ T. t% e: } - CDC_Transmit_FS(buffer, sizeof(buffer)); // 转发到USB
1 M% v# h( E" i - }
5 L* g8 E2 e) X( L6 i! R5 t* z$ | - }
b4 H7 l* I1 e. v' T+ Q1 E - + N2 U! ~) c2 E3 W
- if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)
9 Y! Y% K, b( C6 P% \5 D {: ~ - {
- O1 `0 h" T& v$ |4 b/ i5 R - nsent = Queue_GetUsed(UART3_QUEUE_Handle);
. }4 r4 J v) e8 ] i - if (nsent != 0)' @- o- [6 `0 b* y
- {
, o9 \8 R# {& e - Queue_Read(UART3_QUEUE_Handle, buffer, nsent);
4 U( M+ n$ O3 W3 V/ k - CDC_Transmit_FS(buffer, nsent); // 转发到USB
3 [" L4 b7 J) B t1 I. } - }+ o! F7 v) H2 H, X+ F" b( q
- __HAL_UART_CLEAR_IDLEFLAG(&huart3);
* J) Z- a `+ b/ T3 p" c. N+ j - }' @9 G/ B& T3 h5 [! }" z
- }
+ s% Y. E, g7 z. m - - d2 X+ u& s: W7 a& p# G- n
- void UART3_SendData(const void *buf, uint32_t len)0 p4 K& A5 z A/ n9 B) {
- {- E5 e6 j0 X; v% \% ]4 V( N3 _
- const uint8_t *p = (const uint8_t*) buf;. j! s) q4 m0 p: A# [
- while (len--)
3 m9 p3 `9 H/ P5 M - {6 e9 a2 Z, b P3 f* x
- while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);
* Z2 b' {( X1 g3 v+ k9 G0 E: U) N - huart3.Instance->DR = (uint8_t) (*p++ & 0XFF);
* W6 `2 G! z. I4 D7 D - while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);5 I! J- G, b6 i) r1 s( l7 d
- }
6 r$ m/ f3 R! h4 { - }- L3 z* ^6 {7 f1 J7 @
- 0 x# @* r, p7 _, ]- E5 m2 A
- //#ifdef __GNUC__
& J# o9 u7 w9 P, Z - //#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
( `; w# q' w# C - //#else8 e: {; P' l1 q# k8 D! f+ r( y
- //#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
7 j1 G& k# X, n5 O u+ x1 n( @ - //#endif
9 I- f5 v& A& ~ - //PUTCHAR_PROTOTYPE
2 G/ b* W9 b; \, e! B$ _) A4 [ - //{
# t* f) x6 J: c4 b" ` - // while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);
8 r9 f: e" p& {0 O9 V - // huart3.Instance->DR = (uint8_t) (ch & 0XFF);, E' \ X) M; S& o y" ~9 _
- // while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);+ j; ?0 r0 j: w1 q7 m$ N& j
- // return ch;
_8 P5 T8 U- X( H8 z - //}
/ B( f% @, b' p- u5 `5 I8 n
复制代码 " S8 g# n: m0 A& n3 ], W
然后在添加USB转发到串口的操作:
- p/ m {% H8 a- q' T7 E
7 j1 o4 Y9 \! y7 n" n7 u3 G% E8 O6 _9 a0 J9 z& u3 m/ Z
/ R `9 x, n4 T1 }) l这样就实现了一个类似于USB转TTL模块的功能!
9 ]# s1 Z7 ^; z
0 U7 e7 q9 D6 o( y/ |. h& p' n
& h, I1 D% i7 M) R
# j( ?" t7 { l D+ D
|