1、配置时钟
! l, E# d5 m% O J$ Z+ f0 Y p( b" d; }
, O5 z8 E6 h0 Z( M
$ B1 j- v R: V1 q8 s( c* @6 m( R
2、开启USB
6 k' d8 x3 Q8 e/ ^6 k m& N$ F! G! l; P/ L9 t, }) Q$ b
0 c% t7 M1 V1 e' T( q. `9 r' R% s, ~* t! j8 r4 V
3、开启USB设备:虚拟串口
" u/ k' t, b# g6 l7 b9 K5 |% W, ^1 V H: [7 x$ E3 m
$ ]& ~. V, E$ h2 G+ c, H% R! P
# Q3 G. K4 \/ Y) i" \ Q' ]4、生成工程4 O8 [; L- U N7 H* W
7 f. z* X) N% O
3 l) k/ X0 @0 Q0 q5 N
# }; f) D9 Z% Y& B5 B& O; q
2 y. J7 `2 H. F0 M8 S5 ]- x
) h& `+ q4 i+ x5、修改代码实现回环收发数据测试
. Z/ x* \' L5 i2 J: R0 G, c; y# w2 n0 b在usbd_cdc_if.c文件中新定义一个结构体:0 K/ a) r6 {; _9 w5 H5 n
6 ?/ \8 V% e' r3 ^2 ]( u- USBD_CDC_LineCodingTypeDef USBD_CDC_LineCoding =+ u. _( S! P7 u) |
- {6 G/ v3 {- N# H; I U, D
- 115200, // 默认波特率
1 ~. ~" a+ }$ L - 0X00, // 1位停止位. D/ r1 X" i- }) \# L0 G
- 0X00, // 无奇偶校
( J/ j$ |$ Q# Z% a9 F# y& l* Y4 a - 0X08, // 无流控,8bit数据位
- s! J0 L1 V1 }7 @0 W& M$ R% q - };
复制代码
( G C0 R! l/ m* l, v" m* Y9 i找到CDC_Control_FS函数,找到CDC_SET_LINE_CODING和CDC_GET_LINE_CODING分支,添加以下代码:
) y, E6 y8 y0 J' H0 r(CDC_SET_LINE_CODING:你在用串口助手选择波特率时候,STM32就会调用这个分支进行修改USB波特率)
$ |. X0 \7 m, k, t/ Z& c(CDC_GET_LINE_CODING:获取STM32的USB波特率)
4 q$ ?: t8 `& W% J5 i2 _5 Y
: ?" \% S" g. v5 E9 z# u5 ^" m, Y- case CDC_SET_LINE_CODING:/ @. c6 u5 S6 x1 O1 f J7 b1 N: O
- USBD_CDC_LineCoding.bitrate = (pbuf[3] << 24) | (pbuf[2] << 16) | (pbuf[1] << 8) | pbuf[0];" R) o% n4 H' |8 K7 w- C6 h
- USBD_CDC_LineCoding.format = pbuf[4];
" ]- @( `' g2 ]) C' P. @- O( O4 p - USBD_CDC_LineCoding.paritytype = pbuf[5];
* }# t* `. J+ L2 V, j7 {' w - USBD_CDC_LineCoding.datatype = pbuf[6];
* j |! Y7 v0 o( ~8 \1 U - break;
; e6 S+ d* t) Q' M. J& Z A - O' D8 [( S7 {- n. w; v
- case CDC_GET_LINE_CODING:, |3 b" F. x0 I
- pbuf[0] = (uint8_t)(USBD_CDC_LineCoding.bitrate);/ L0 b. e7 V" O& l L% |
- pbuf[1] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 8);& m/ U# O" ^) X; S4 J
- pbuf[2] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 16);& z7 b7 p1 @ O V% R6 F
- pbuf[3] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 24);% ]6 ?3 a$ E7 Z G- G
- pbuf[4] = USBD_CDC_LineCoding.format;
3 K# ]/ N3 n8 l( p |; ? - pbuf[5] = USBD_CDC_LineCoding.paritytype;
8 I0 r0 J% J! p: z - pbuf[6] = USBD_CDC_LineCoding.datatype;
( P; Q( A; x1 p& } - break;
复制代码
9 F; ?9 E. |+ p* j6 Z) C; v2 R9 l, H
: O, f t& t0 M. J8 z如下图所示:
5 E5 A9 Y4 t6 ~& r( j" j9 g/ e9 s) |! ?) f: f
4 \ F, U! U- {$ ?7 s- @
& l( @, S. a2 `. q6 j+ S' V; ~2 F找到CDC_Receive_FS函数,这个函数如果USB虚拟串口数据收到就会被调用,我们在这个函数中将收到的数据在发回去,只需要添加CDC_Transmit_FS(Buf, *Len);这一句即可,如下图:' |: i* @/ n6 {) U# I4 R$ \* R2 |# t
2 u+ g. I* x$ o- S* R
3 Z$ ^% l# Y0 Y2 P1 s ?
4 U! v. S& k; U3 ~: r: P& A然后编译工程并下载,接上USB之后,设备管理器COM出现一个新的端口:
1 E7 @5 u6 x$ h& ^! ^' s6 }" ]* r W5 c1 S: M3 r) r
! y7 T; H# |' y8 G Z% a" t7 c6 B( ]0 n$ d% z$ R. n' b* B! i5 T
我们使用串口调试助手给它发数据:; D2 D" t8 V; g
: O. H, f- Z0 z% h; e/ M f1 ?
8 D$ u9 `6 ~; G' ?& @- K( S8 t$ W4 J. X/ L9 `
6、实现USB转串口功能
: \ A! t5 B3 {% ^# H( d: I发数据流程:串口调试助手发送数据->STM32的USB数据接收->STM32转发到串口3
8 V2 Y! Q+ w0 p# @收数据流程:STM32的串口3收到数据->转发到USB->STM32的USB发送到串口调试助手
; o9 o$ A% m% m5 B- m/ _1 I- F w I9 N
第一步先在这里加入串口3的初始化操作:
+ w/ E. D+ b a2 {: T, x
: N' A/ C" s4 a- j3 h8 g
5 |* y" e- u* R: h1 F" R% L8 S
$ R* r8 D- ^! e贴一下串口3的初始化代码,这里我用到了队列,因为实际测试发现串口3接收数据量比较大的话,那么转发到USB虚拟串口的时候会丢数据,所以这里采用了缓存队列,当串口接收到的数据到达一定数据量之后才做一次转发到USB的操作,并且开启了空闲中断,作用是转发最后一包数据:" N7 L: g) p+ J3 _' d' G
' o3 W9 N ?2 ?; d" S$ \
关于队列的使用可以查看我的另一篇博客:C/C++语言实现的一个缓存队列
3 a! T. ] w$ Q$ e: R) l, ~4 o% z" ~. g% O# k
- /*" d2 n3 h8 c+ Y
- * myusart.c
" \) B+ R" X6 h3 p - *' i4 U& F% l* |: K
- * Created on: Mar 22, 2021
' ]8 Y0 Z, n% q: h' X! l) W) J - * Author: hello; m. f G# Y$ K+ y
- */8 P0 T' x* k9 Q
- #include "myusart.h"! G1 @- B* l8 @/ N1 G2 a3 n- K3 b) s
- #include "myqueue.h"* {; {# Y, P* i9 }3 M! Z% P% ^
- . W* N( C5 i- V8 B9 \7 r6 R' P
- // 队列大小,定义为USB_CDC的发送包大小# D6 Z# g: L8 D( m
- #define QUEUE_SIZE APP_TX_DATA_SIZE
8 v' A2 Q) L% f2 }6 S" ^+ l, o - * A5 y% i( t6 }1 J9 T
- // 队列数据空间。请使用宏QALIGN4,目的是为了根据队列大小计算实际需要的队列存储空间大小并对齐4字节) b3 j9 B) ]8 s1 I: K" f
- uint8_t QueueBuffer[QALIGN4(QUEUE_SIZE)];' n7 l3 b2 z2 l0 }/ m0 k
- 9 e ?1 R1 W7 P% ]
- // 队列句柄
) u2 }2 ^' D* b: `# a, ` - Queue Uart3QueueHandle = {0};( ~1 d* s* } b' E# J
- #define UART3_QUEUE_Handle (&Uart3QueueHandle)
. d" Y% f* B/ ^" Q: S
( D* C' j0 {. W* H8 ]2 n+ S+ K- // 串口转发到USB的缓冲,定义为USB包的一半大小" [% i% l; j2 q/ p( Q
- static uint8_t buffer[APP_RX_DATA_SIZE >> 1];
+ c4 S0 z; Q; M5 E \' x% g - ! C3 l6 ?3 O* n% u
- void UART3_Init(const USBD_CDC_LineCodingTypeDef *USBD_CDC_LineCoding)4 F7 A, ]) o4 O7 U' z7 u
- {0 u9 H) P5 v |! S% L% s& w
- __HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE);
$ L8 l7 r) z% J# D
. Z, F6 m. W! v* B; ^- __HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
. s0 B8 u3 K |/ B. m* `( j - ) X& V7 }, U! g8 \& n2 m
- HAL_UART_DeInit(&huart3);+ @; H7 N" s2 }+ }
- " `: Q) Y% W8 [" c y( k' n
- huart3.Instance = USART3; n' k& \3 G: M$ {
- huart3.Init.BaudRate = USBD_CDC_LineCoding->bitrate;
5 G$ I; ^, v/ n: J; Z/ K1 g - huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
% ~( H, }. I7 F/ e8 m% r - huart3.Init.Mode = UART_MODE_TX_RX;# F3 j8 w+ S- q0 _- @. _0 j/ k `
- huart3.Init.OverSampling = UART_OVERSAMPLING_16;
* n9 P1 {; G- T+ g( z - - R6 ]3 y( Z# z' H: H& {
- switch (USBD_CDC_LineCoding->paritytype)
( I6 I1 L0 [: W, _ - {
+ s4 N8 [9 n3 T$ _+ p0 s: o - case 0:
; I2 G' g% {/ I$ ` - huart3.Init.Parity = UART_PARITY_NONE;% x' M& D9 ~6 u( C
- break;6 M3 I- D. { K) \" @
- case 1:
% E4 ]" S% K: j0 r3 V - huart3.Init.Parity = UART_PARITY_ODD;. x1 `8 f; B5 }
- break;7 a% G/ g! d T* y! _
- case 2:
( X6 N; T U% M- G' j0 y - huart3.Init.Parity = UART_PARITY_EVEN;
9 m4 @( p9 Q6 |9 r - break;
1 e; y+ W5 h: y: y2 T7 t4 l - default:( m- W8 w" U, }% }3 G! ^8 g* r+ h
- huart3.Init.Parity = UART_PARITY_NONE;
% M. P( |% y3 b# c - break;/ A7 t# ^/ N) L$ N" i M, a4 l' O
- }. }6 |2 K% ]+ z7 M" v( }2 R
+ L9 z+ G5 [: G0 |- switch (USBD_CDC_LineCoding->datatype)% J7 a A, \! J" N( F
- {
. m \) j- n: T/ S7 L - case 0x07:0 s1 H$ y; i9 R0 J- Z6 ~
- huart3.Init.WordLength = UART_WORDLENGTH_8B;: Y' r/ n$ p2 X: d8 e
- break;$ ?2 w v1 y. {; _+ y/ f. g# J
- case 0x08:$ m1 @; O* y/ f0 e$ H( |+ l
- if (huart3.Init.Parity == UART_PARITY_NONE)2 W* u0 c2 E8 D. _" S$ H
- {
4 H' y! w9 x' ^ - huart3.Init.WordLength = UART_WORDLENGTH_8B;$ {. E( p7 g: M9 X; i6 K% w
- }
8 A" K6 Q, F& [5 p - else
% {4 n* b2 D" v$ [/ ^4 } - {, v) |6 B1 G4 i5 U0 G' w7 `6 @7 L
- huart3.Init.WordLength = UART_WORDLENGTH_9B;! Q- [+ W& N1 y5 K! y$ O
- }1 T; ]7 }$ s, X, p! Y6 [0 l
- break;6 @/ {& [$ R1 j9 N% e' S/ t
- default:
( T- M2 m( _' h/ x - huart3.Init.WordLength = UART_WORDLENGTH_8B;
8 M. @( @4 d0 Q! x- H; l - break;
3 h1 X6 \6 t7 R0 Q3 a( h0 p% a - }! C! M. }6 M O, N* h9 U
# ^( `$ \3 T# \- ?) m0 S" x# \- switch (USBD_CDC_LineCoding->format)6 _% x" D4 m: U" V9 E
- {
- S& b8 d/ [7 r# D/ d/ b6 k; O - case 0:
: O0 e0 f' I& ?. l. w+ w - huart3.Init.StopBits = UART_STOPBITS_1;0 `& Q1 e) ]- V# c) n" X7 W) S
- break;
' F* s: }% A: s1 R5 [# Y - case 2:
/ o: g4 J5 O4 m& D! p2 a' F( | - huart3.Init.StopBits = UART_STOPBITS_2;
' _1 I8 |+ T+ x0 s' s - break;
1 w) K% [: \8 ?, N/ p9 |4 k7 c - default:
( Y# V, k$ v: X4 s1 v8 Y - huart3.Init.StopBits = UART_STOPBITS_1;
/ \- v' {& D2 E - break;
( d' J( r! [$ H! }# {* G3 i, m) L - }$ j3 E% X. ~: A; e }
- " n) P' M: }( u [: i. {
- HAL_UART_Init(&huart3);
8 r( D9 ?. R" @ D2 n/ @) ]
; ]4 m# Z2 Q G- Queue_Init(UART3_QUEUE_Handle, QUEUE_SIZE, QueueBuffer);
" [; @: Z8 L$ x4 t m
0 `% v8 r# h& N+ S2 u& H5 r& h- __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);* W7 M: T4 c, U9 S0 B: b
- % e2 V+ _( Y7 n, ?6 Y: n% z
- __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); e7 h' N$ g7 M+ _2 z8 ~2 T
- }
6 z* n+ l+ L/ b6 |( H0 o9 U
* r/ ^. J/ n8 Z) |* h' e' B) Z- void USART3_IRQHandler(void)4 C' D3 h, Q% R- |
- {
7 S1 }7 J, K) z; { - static uint32_t nsent = 0;
" ?0 @2 K V0 n% E - static uint8_t dat = 0;4 t- H( B3 I/ h
/ z0 L! B/ W v8 N- if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)
$ @9 R4 S2 J' w* g+ t$ [# I - {
% V: s0 f& B& k3 A7 \) |3 v - __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_RXNE);
3 ^. Z7 U" Y7 d9 R0 `/ |6 p, ~5 X - dat = huart3.Instance->DR & 0XFF;' ~/ Y2 I* g* e1 q
- Queue_PutByte(UART3_QUEUE_Handle, dat); // 入队一个字节的数据
& Z. y; }+ ]3 r. J- s" M3 ?/ h& l" l# h - if (Queue_GetUsed(UART3_QUEUE_Handle) >= sizeof(buffer))
- Z" y% [- L+ N0 p3 J' O/ ]# _7 ^ - {
7 y1 e' ]4 t3 E - Queue_Read(UART3_QUEUE_Handle, buffer, sizeof(buffer));
1 i, a f- b6 a2 J" v V2 A - CDC_Transmit_FS(buffer, sizeof(buffer)); // 转发到USB# s5 H) c) K' @! I& a0 D
- }& U- T% n6 t* N" y) A: S
- }
' Y8 D) V) V. J2 @: K& D8 `4 ] - 0 r) `9 R" u0 D, k* \
- if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)
+ B! c9 a4 r6 M9 |! s! G - {1 d( @8 C7 Z, U- F
- nsent = Queue_GetUsed(UART3_QUEUE_Handle);& V5 P8 e- H8 o: j+ }% i+ c, P
- if (nsent != 0)3 \" m/ u& {: o- U2 A8 K+ D& z
- {# d! j% y% y! b% _7 M" |; Y! L
- Queue_Read(UART3_QUEUE_Handle, buffer, nsent);
% {2 e( @5 F" m W- g6 Q - CDC_Transmit_FS(buffer, nsent); // 转发到USB
' ~6 P( {5 G' |1 x+ G - }
8 Z7 Q1 _# u8 T$ [% M - __HAL_UART_CLEAR_IDLEFLAG(&huart3);$ Z* s0 ` Y o. a3 ~
- }. U# r4 I( l4 @0 [
- }
/ I; @7 d4 n7 m - 3 N6 v( o ~ A
- void UART3_SendData(const void *buf, uint32_t len)
/ T* \! c3 ?! t - {
% Z4 b9 A/ v W. t) W - const uint8_t *p = (const uint8_t*) buf;- D* _$ r3 [- T2 s$ D
- while (len--)$ b) p$ W+ G" q
- {
/ P% V ?- X8 X& q3 x& D& g0 O - while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);+ ]8 K9 P6 F- i, z7 K0 a+ D
- huart3.Instance->DR = (uint8_t) (*p++ & 0XFF);
! o2 u# g' q/ e: \" T- V+ A - while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);$ g- B& Y o/ ^" P/ p
- }
6 Z, t% S; R1 n q- d! ]: D - }: p8 x6 I( g3 K0 R! n0 g8 F h
- - o# }- y. @1 ?3 H; o
- //#ifdef __GNUC__* w; @! F+ H( v# J+ p- e- n
- //#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
- N# x4 f& a! \1 h$ U: `: H0 W - //#else
9 i. s# c+ i8 | - //#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)5 X7 A1 c8 M: J+ `
- //#endif2 z* j; f6 e5 w
- //PUTCHAR_PROTOTYPE
; P, h, F+ P/ ~1 t! h- R) E4 \) | - //{
) [4 _4 ^" E1 f4 f& s - // while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);2 X( N9 w% i# h+ M9 r: B# T) a; ?
- // huart3.Instance->DR = (uint8_t) (ch & 0XFF);
/ e+ F% d( n' J b4 D - // while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);5 S7 E: b/ d G
- // return ch;1 r+ b" O2 x/ a- c6 x v
- //}; M S, h" |+ }9 x Z
复制代码
7 \/ V) K1 e, g然后在添加USB转发到串口的操作:. k6 c1 k: W8 r3 I
3 S: a- S: E( _& y( Z0 R
" l- o1 d$ U/ \5 e% @/ v- n! r+ ^
0 T: [; z0 Y: I; u/ w& Q, F这样就实现了一个类似于USB转TTL模块的功能!
/ a# l1 }8 s2 b# E4 {
9 O; a$ A' r0 K: x. a' o: C* }5 b2 |4 J' u- f" Y F
8 e+ v0 I7 g8 Q. |6 G% O
# ^" k( B9 A( q. _& B! j
|