1 实现原理$ v+ I; Z# n& n
使用串口通信可实现数据帧的收发,完成机器人控制、采集数据的传输等任务。制定串口通信协议并定时发送一帧数据,常见的处理方法是利用空闲中断,但STM32 HAL库好像没有专用的空闲中断,自己实现起来比较麻烦,这里利用非空中断可以实现同样的功能,缺点是效率比较低,稳定性经过测试也还可以,如果有更好的思路欢迎提出。/ m; |& l. }; x o
# t* [; m2 @; }2 B t
1.1 制定串口通信协议+ w! r3 d4 O' [8 E: U/ [; U$ U5 K
制定串口通信协议时一帧数据的长度和内容可以自己定义,STM32的串口传输的数据类型为uint8_t,注意取值不要超过0-255,下面给出一个电机控制的串口通信协议表做示例0 T8 a! J, y! f8 d
! Q) u3 T: r& p% {- N+ P
1 i2 w5 Y5 ]* i7 Z. i
1.2 分析中间变量
1 Z, C b! W/ m创建接收数组g_fUART1_Buf,其长度等于串口通信协议一帧的长度
8 A& n- E4 E( a- q) @+ Y) L创建接收一个字节的暂存变量g_fUART1_Byte,配置一个字节中断一次
, R3 Z; `; Q9 @3 r% [8 i1 _( e. F# P创建g_fNew_Pack用于主程序中判断是否接收到完整的一帧数据
& L, i6 z" |% y+ x7 Q3 Q* R创建g_fNew_Pack_Cnt记录成功接收一帧数据的次数,方便调试+ j8 N9 o- I6 o n( \) ~9 c
- uint8_t g_fUART1_Byte = 0;- w+ m/ ^5 S- N/ v$ v5 E
- uint8_t g_fUART1_Buf[10] = {0};
& X) C2 F3 o( ~' A. C- N+ j( w! {
) k8 ^& H: p% [7 ~+ a& F5 D- uint8_t g_fNew_Pack = 0;
; n( {, `" m4 G4 x9 J% A3 \ - uint8_t g_fNew_Pack_Cnt = 0;
复制代码
/ @ b" [) l# z, X4 l& I1.3 接收数据的处理 e# i3 P/ e% z. `" t
新接收到的字节g_fUART1_Byte写入接收数组g_fUART1_Buf的末尾,每接收一个字节,就将前一个字节左移,保证新接收到的字节一直往数组里填充,当接收的字节超过接收数组的长度后,继续左移,去掉旧数据,填入新数据6 A& P- E! G5 \: U7 I; V. X" Q
8 e R2 M7 S3 [9 S v
% i$ R: y$ V5 @4 C" y5 |- Y; ?- J5 Z
2 STM32CubeMX配置
9 {% Q% H) ]. \; c2.1 SWD调试接口配置
* Q( C6 P5 v: @% lSWD接口用于程序的下载、在线调试 z8 x% @4 m+ l, M6 |
/ u, L" a! p+ K1 z& n
" A [" P* ^: D
% F1 N$ W5 A5 G2.2 时钟树配置
, S. o" [8 ]" v# r5 |STM32G0系列内嵌高精度(±1%)RC振荡时钟,无需外部时钟,所以这里并未使能外部高速时钟,开发板上也没有焊接,直接在时钟树配置选项的HCLK输入64,配置最大的64MHz时钟频率即可。
% E* a! X4 w. O2 G
! I: L+ f# H7 r! Z# G
& E& ?1 L: k1 H, e
; Q8 F7 ]' R" z/ `- s3 ?0 @4 B1 \
2.3 UART1串口配置5 X( i/ d' U- N
UART1用于接收上文制定的串口通信协议,并开启非空中断& t% F5 E4 E- O7 u @
$ {2 X, r9 M# p9 L- f2 p
$ Z4 V/ _9 R0 v7 }
/ ~: \7 Z! G7 I6 a5 W1 T
; {; i: l# M( @9 |% w4 {
. r* G; m+ X) K4 X4 H" @$ w9 P. x3 w2.4 生成工程
8 ~3 k, v% @1 } T输入工程名,选择存放路径(不要有中文),选择IDE为MDK-ARM;只生成用到的文件(目的是提高工程编译速度,减少工程占用空间),并生成单独的.c/.h文件,点击生成代码
- K, Z: l: r+ u. R# z4 D K7 y: n
$ m' N, t' x4 X9 Q9 O4 p! v
% {4 f2 v0 S" q t7 m5 A* B
" E" C& d# n6 w; u. t- j1 }8 C, w" \6 J. ^
3 添加用户代码5 w2 Q7 ~& J( N9 u( J! l* k
3.1 定义全局变量
; r8 R' m' S! f5 D7 C( P/ y# w2 W3 d- /* USER CODE BEGIN PV */
% ]2 V% a q9 G - uint8_t g_fUART1_Byte = 0;/ }$ W* T6 [& F1 s& p# ? \; J
- uint8_t g_fUART1_Buf[10] = {0};
8 w' t* R# E7 \) G5 U
+ {$ c0 E& k1 X* o* V, j, v- uint8_t g_fNew_Pack = 0;
; l' j- |6 y( {. m0 t - uint8_t g_fNew_Pack_Cnt = 0;& Z, B4 I# X' G6 p" _' _; N
- /* USER CODE END PV */
复制代码
7 s& R9 z3 n8 H f9 u0 K3.2 UART1中断初始化$ n4 C& z2 J# ]6 p
- /* USER CODE BEGIN 2 */
& `5 n- q u1 J- u; b6 V - HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1); // 启动串口非空中断,第3个参数代表1一个字节中断一次
复制代码 6 y/ p7 J' b. T4 {/ z( l
3.3 UART1的printf重定向, a3 A5 A6 Q! M5 ]0 b7 Q# ]
- /* USER CODE BEGIN 0 */
( j# i8 d4 l# ^% j; M - /* printf重定向 */
7 t# m% Q% F2 {( o - #include "stdio.h"
2 d K6 F, L( N" D/ F) P( k% M - int fputc(int ch, FILE *f)' z6 ]- o- l3 S$ \" r
- {) }) b8 \7 N- E. |
- uint8_t temp[1] = {ch};
* c3 g2 m: j( o6 b - HAL_UART_Transmit(&huart1, temp, 1, 5); // huart1根据需要修改" Y, E+ V* J" X$ Q: L; k
- return ch;" b% p) s& V- A
- }
复制代码 0 R2 P1 i6 j6 ~2 M
3.4 UART1回调函数
2 W _1 F' g' [: A2 O- /* USER CODE BEGIN 4 */* Y Q! v# W0 Q# _4 y
- // 串口接收数据回调函数; L* z% X1 a& P4 H
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 0 g' g) v. g3 @
- {
! t% R. V: B. }$ f$ v# T - if(huart -> Instance == huart1.Instance )
3 @3 V, x7 j4 |2 R3 [- Q - {( d- V: h5 Y" U6 q
- // 往接收数组里填新数据,即实现原理的1.3
) c& ?1 ^" I$ @( T - for(int i=0;i<9;i++)
( ~9 t% Q0 _! D, N X - {
8 X% s8 B6 _% Q r9 R* r - g_fUART1_Buf<i> = g_fUART1_Buf[i+1];# f% _& d9 N4 }' K6 A
- }
3 D0 n; {) T+ n8 \* b- K0 o- ?* W- X- T - g_fUART1_Buf[9] = g_fUART1_Byte;
+ o+ f% n: U* \8 Y# q, p - if(g_fUART1_Buf[0]==0x5a && g_fUART1_Buf[9]==0xa5) {
/ G; W: Q" J3 v% v, U' q - g_fNew_Pack = 1;// 成功接收一帧数据,刷新标志位
4 f P0 f* X" a9 k - }
7 w/ X) s: i4 Y8 v - HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1); ' N6 n8 |( X3 n+ P: h% k9 W9 n
- }5 a2 n: S) a3 t/ d
- }% [* ` u ^* o8 t3 X& T
- </i>
复制代码
; M8 K7 O" `6 c! W) r( ^( b; A3.5 接收数据的处理5 r* K* i9 u: j3 w' p7 p0 T! ~) q
- /* USER CODE BEGIN WHILE */
" w; g" f& V2 {$ |5 G* z - while (1)" c; o4 {7 h- t2 g0 G2 w( X
- {
# Q9 d* U; e! J- @, D - if(g_fNew_Pack) // 主程序中判断是否成功接收一帧数据, }& a7 l/ l a7 u! j% Y1 S. P8 {
- {: d5 c5 B! a' V- a
- g_fNew_Pack = 0; // 清除标志位
$ C- g; B7 @ U ^0 S9 U - g_fNew_Pack_Cnt++;8 W/ _/ C$ y7 w- [; Q' W6 R
- printf("g_fNew_Pack_Cnt=%d\r\n",g_fNew_Pack_Cnt);// 输出成功接收一帧数据的次数
9 t6 z9 o2 n$ N2 N; | - for(int i=0;i<10;i++){
, r; p) T4 E5 L* H - printf("%x ",g_fUART1_Buf);// 16进制输出接收到的一帧数据
! Y8 u: b% n1 J) T6 t! U- S - }
* r8 E# M/ ~# v& S, h5 D* V3 [ - printf("\r\n");
c' { \( x3 E/ F: M! I - // uart1_handle(); // 串口1接收数据的处理函数,根据需求自己添加
/ ^4 j( v- E; E. d# p S* d7 O
8 ?& j# y9 ^7 C% s: d3 D: R- }
6 i5 W4 h2 u! @$ q; N
) R6 P5 C5 f9 ?) x* H, Y/ R2 I- /* USER CODE END WHILE */
3 z+ `- S! G4 Y$ r - 1 w p- d0 _' x. |
- /* USER CODE BEGIN 3 */2 _6 S4 D" ?" x* ?; k! v
- }
# v$ `/ Y7 r3 Q7 r
复制代码
% ?/ |8 L# n, R! a4 效果演示' {" {" X3 h0 q) ~+ k/ @
gif中为方便演示,除了帧头帧尾,没有使用上文串口协议定义的内容,用11 22等数据(16进制)代替
+ J) L1 k& d+ J
7 y7 C- z# f6 O% \' Q% `
7 k% j& j; a3 I {5 |
" L% P9 B4 Q$ s& d+ [; o
3 G' J- Q& v2 w
& E- c4 `5 P, U" E, ^1 T5 `————————————————: I( i8 H( h- r$ k
版权声明:周云川, b9 w( r S6 P$ d/ M$ d
: R: y5 [5 X1 X/ e9 o0 Q' U8 K& m/ o7 O$ f, b0 V
" d2 l0 m( ~5 E/ b; z y, F |