1 实现原理
6 z4 Y/ j! x3 r' t$ H2 Z2 {0 h7 M使用串口通信可实现数据帧的收发,完成机器人控制、采集数据的传输等任务。制定串口通信协议并定时发送一帧数据,常见的处理方法是利用空闲中断,但STM32 HAL库好像没有专用的空闲中断,自己实现起来比较麻烦,这里利用非空中断可以实现同样的功能,缺点是效率比较低,稳定性经过测试也还可以,如果有更好的思路欢迎提出。: R0 K# J9 W! L2 i2 `) J. s; \
7 p3 e1 h* y g) M; |$ ]% _3 K1.1 制定串口通信协议0 H V, f2 ], Q1 _( _% _
制定串口通信协议时一帧数据的长度和内容可以自己定义,STM32的串口传输的数据类型为uint8_t,注意取值不要超过0-255,下面给出一个电机控制的串口通信协议表做示例7 z8 T) ~) u# }9 |8 o# Y
% I+ [% e8 D# E0 N
" {" E2 R7 V2 s$ r
1 f. x8 W+ E' G1.2 分析中间变量9 ^: ^* A4 I4 j$ { |
创建接收数组g_fUART1_Buf,其长度等于串口通信协议一帧的长度
4 P- I/ G% F" f( j" r' J/ S创建接收一个字节的暂存变量g_fUART1_Byte,配置一个字节中断一次9 Z' }) J1 t) t
创建g_fNew_Pack用于主程序中判断是否接收到完整的一帧数据5 [3 M2 @& K( f8 U+ {2 G% l
创建g_fNew_Pack_Cnt记录成功接收一帧数据的次数,方便调试
4 K! h* K( r0 B0 T- uint8_t g_fUART1_Byte = 0;
: J; ]* [ ~6 g' a - uint8_t g_fUART1_Buf[10] = {0};/ X2 \7 y, v( \: }3 d j
: Y& U W! X- G8 X% \- uint8_t g_fNew_Pack = 0;
! e+ m/ V( R( _# J6 f$ t8 X - uint8_t g_fNew_Pack_Cnt = 0;
复制代码 1 c5 Y& t: Y0 ]$ D, Q( L/ a7 P
2 z! o9 J8 R+ y% x1.3 接收数据的处理7 J, P. p% i I* _3 O) \* ?
新接收到的字节g_fUART1_Byte写入接收数组g_fUART1_Buf的末尾,每接收一个字节,就将前一个字节左移,保证新接收到的字节一直往数组里填充,当接收的字节超过接收数组的长度后,继续左移,去掉旧数据,填入新数据
+ j, p2 @7 s6 u1 H9 Z5 d
2 w0 f) r/ R; A" ^2 B4 B' a! |; X7 p2 z
9 q; ?/ @8 y. w' H4 c+ ~( w! i" f& A7 \. ?. i
2 STM32CubeMX配置# j( U% X! y1 ~0 I0 X7 ?! l
2.1 SWD调试接口配置' ?: H( ]2 |- G
SWD接口用于程序的下载、在线调试
1 h# R, i1 [1 u2 d4 B
; w+ o$ ^6 H6 k+ {+ B/ ~& `% Z0 T7 j* G+ Z6 J
& y6 u- y4 L, o3 A! l8 U2.2 时钟树配置
. r+ q! M }* [# r2 i. _# ~STM32G0系列内嵌高精度(±1%)RC振荡时钟,无需外部时钟,所以这里并未使能外部高速时钟,开发板上也没有焊接,直接在时钟树配置选项的HCLK输入64,配置最大的64MHz时钟频率即可。/ X* ^ q2 X! m! {8 k- K; r
; t/ L6 O6 I& D" r$ w/ {4 }1 C; l9 l6 I5 M
/ ^3 b* R1 _: x! L0 w
2.3 UART1串口配置
% r4 u+ p/ h5 \UART1用于接收上文制定的串口通信协议,并开启非空中断
" X6 S1 O* a2 \2 A
! P+ ^9 z8 p! e+ [
1 T8 D/ D! G5 H9 G+ C7 q) e6 P9 A
3 P t" A2 s; n H
0 a8 `5 P% p- W/ Y4 K/ o, b0 ^3 i
0 `) j, F# `6 {+ a* @: B( m0 d- F y1 ?; N; L5 Z+ k9 q
2.4 生成工程
+ y' _& }! q( i0 L4 O0 k) j- k输入工程名,选择存放路径(不要有中文),选择IDE为MDK-ARM;只生成用到的文件(目的是提高工程编译速度,减少工程占用空间),并生成单独的.c/.h文件,点击生成代码2 P* h5 I) z$ J7 s( Q0 d- Y
# f- q; @ C, c8 w8 S
1 s( H) K. V. d1 J% S% C- L4 f! W4 e" R. O. F$ a" i) j/ N
6 e8 s$ T8 K. R/ |7 C, W4 z9 h9 v* F6 V1 j! A
3 添加用户代码
$ p# }$ o- Z2 l: y% q1 _7 g3.1 定义全局变量
2 S+ ^/ n9 p' o \9 K- /* USER CODE BEGIN PV */2 @/ }; p/ v N7 K/ W- A+ q
- uint8_t g_fUART1_Byte = 0;, |2 A9 J1 ] d. n! s# f
- uint8_t g_fUART1_Buf[10] = {0};" w8 X' I# K4 P/ c5 O4 j; e' W
- 3 y, _. x0 G6 ~% C' y
- uint8_t g_fNew_Pack = 0;
1 U% ~+ J4 y# ~$ G - uint8_t g_fNew_Pack_Cnt = 0;
h# ]7 I5 L+ @& q: ~ - /* USER CODE END PV */
复制代码
1 l! @3 t$ e7 r- ]' ~" u* z" o; ]' ?# X, j, w
3.2 UART1中断初始化3 ^$ m- Q( x3 a. l0 H9 V# Z: U
- /* USER CODE BEGIN 2 */
* [; o- k, S# }( S, j& S8 H - HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1); // 启动串口非空中断,第3个参数代表1一个字节中断一次
复制代码
7 Q; o o* h: g1 G- i$ v3.3 UART1的printf重定向. K, A" \" h; M* x, A# u, @
- /* USER CODE BEGIN 0 */
2 ?* N3 z) s( H8 t* ] - /* printf重定向 */
) b: r% T7 i3 A/ F - #include "stdio.h"9 S$ h, p+ ~. d
- int fputc(int ch, FILE *f)
& _; P$ P. Z2 U, Q4 `9 ` - {
: Q4 p7 V* G' Z* a5 j - uint8_t temp[1] = {ch};; b# R8 M* M `
- HAL_UART_Transmit(&huart1, temp, 1, 5); // huart1根据需要修改
0 i8 T. `& ~6 ^( l" ~ - return ch;" e% `2 {2 i6 l7 X1 m' }2 I3 [5 n7 A
- }
复制代码 + Q8 E( k* L; u( s
3.4 UART1回调函数
i9 }" w' o# g, {- /* USER CODE BEGIN 4 */
w9 k1 d, s/ K3 l; z( y$ W* y; Z - // 串口接收数据回调函数
7 e* ?% }6 P& w - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) / A! Z2 E9 f4 x; R
- {
, q) e. q. U* x1 \ - if(huart -> Instance == huart1.Instance )
2 f2 ]) i. e4 H" T+ w - {% Q K y- _# p; I% B2 d7 \- }
- // 往接收数组里填新数据,即实现原理的1.39 z3 s; P' k7 H4 r' f" K
- for(int i=0;i<9;i++) y/ |4 @: M& ~6 p. y
- {8 Z& n5 }( m8 j/ o f% o
- g_fUART1_Buf<i> = g_fUART1_Buf[i+1];
, X' y3 M# T" \! J/ u - </i>}
$ v$ i* h$ O6 T& X - g_fUART1_Buf[9] = g_fUART1_Byte;
( v; g5 Z. J# ]& E+ k$ s; q: _ - if(g_fUART1_Buf[0]==0x5a && g_fUART1_Buf[9]==0xa5) {& a# P# r( Y1 O* O
- g_fNew_Pack = 1;// 成功接收一帧数据,刷新标志位
) u) J1 B* v8 u; Q- x( B" x$ k - }
1 Q( @1 s L8 n5 D5 |; b - HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1); 1 I1 R4 ?7 s1 _- V% w% \! L
- } H. i- G. e, w
- }
复制代码 1 {# I$ v# G! ^* A% r+ A
& H3 N- `+ I1 F( v0 O! K
3.5 接收数据的处理6 E9 y* a [2 b: S+ @ P" Z
- /* USER CODE BEGIN WHILE */; t7 X& V# f* {( t6 ?
- while (1)
1 R y# t+ z, R( W, Y - {! P# t& w4 j# D9 t2 t+ K/ W
- if(g_fNew_Pack) // 主程序中判断是否成功接收一帧数据" g3 M& L0 J- I! O% }& ]1 B6 D/ V
- {
9 R+ @* q2 \8 t6 x/ Z# B7 Z - g_fNew_Pack = 0; // 清除标志位$ Q% y0 ?6 C+ ]/ }0 x/ ]
- g_fNew_Pack_Cnt++;
5 P4 U0 b S1 L7 d - printf("g_fNew_Pack_Cnt=%d\r\n",g_fNew_Pack_Cnt);// 输出成功接收一帧数据的次数8 L; ?* [& T+ z- u6 p) b. t: W
- for(int i=0;i<10;i++){
% ]# S. M5 J; ~/ L - <span style="font-style: italic;"><span style="font-style: normal;"> printf("%x ",g_fUART1_Buf);// 16进制输出接收到的一帧数据
+ D$ B5 m3 F" I" f/ l: x - }
; l) Y1 P; @! ^2 X7 A - printf("\r\n");* {$ `2 O' u+ U$ P" I) m$ n( s
- // uart1_handle(); // 串口1接收数据的处理函数,根据需求自己添加
5 p, D- a. F: `+ A
& h% d8 f! ^6 v0 q- }% a+ ]! n& Z# v
- " _# H) a! H# _: m+ b
- /* USER CODE END WHILE */
3 o& e) ~% f4 r- S' h" e& y3 n H
3 I( B O5 r7 v- /* USER CODE BEGIN 3 */7 b, m9 M( P+ M+ l4 K @" d) G- K
- }</span></span>
复制代码 , t, p$ X. i. z% g* x& s. S4 {
4 效果演示$ [) G/ B) I. g3 R6 h
gif中为方便演示,除了帧头帧尾,没有使用上文串口协议定义的内容,用11 22等数据(16进制)代替
0 v5 S# D6 [4 \% v3 p0 V8 R
: o7 H% |& F0 ~9 e3 {# P( x5 a8 K" G0 h& _7 i' T6 t0 I
2 y* X4 `4 D# H: \" C
) z2 O X0 E7 Q! s( A* {, v, m+ l3 N8 J
|