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

【经验分享】【STM32G0】借助串口非空中断实现空闲中断的功能

[复制链接]
STMCU小助手 发布时间:2021-11-8 15:27
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
PGWOK2SYP{HGB`WU`E`(@$J.png

" {" 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
  1. uint8_t g_fUART1_Byte = 0;
    : J; ]* [  ~6 g' a
  2. uint8_t g_fUART1_Buf[10] = {0};/ X2 \7 y, v( \: }3 d  j

  3. : Y& U  W! X- G8 X% \
  4. uint8_t g_fNew_Pack = 0;
    ! e+ m/ V( R( _# J6 f$ t8 X
  5. 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
20210317153926655.png

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/ ~& `
20210317153912428.png
% 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
20210317154247720.png
" 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+ [
20210317154445383.png
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/ Y
20210317154445195.png
4 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
20210317154512883.png

1 s( H) K. V. d1 J% S% C- L4 f! W4 e" R. O. F$ a" i) j/ N
20210317154512880.png

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
  1. /* USER CODE BEGIN PV */2 @/ }; p/ v  N7 K/ W- A+ q
  2. uint8_t g_fUART1_Byte = 0;, |2 A9 J1 ]  d. n! s# f
  3. uint8_t g_fUART1_Buf[10] = {0};" w8 X' I# K4 P/ c5 O4 j; e' W
  4. 3 y, _. x0 G6 ~% C' y
  5. uint8_t g_fNew_Pack = 0;
    1 U% ~+ J4 y# ~$ G
  6. uint8_t g_fNew_Pack_Cnt = 0;
      h# ]7 I5 L+ @& q: ~
  7. /* 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
  1. /* USER CODE BEGIN 2 */
    * [; o- k, S# }( S, j& S8 H
  2.         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, @
  1. /* USER CODE BEGIN 0 */
    2 ?* N3 z) s( H8 t* ]
  2. /* printf重定向 */
    ) b: r% T7 i3 A/ F
  3. #include "stdio.h"9 S$ h, p+ ~. d
  4. int fputc(int ch, FILE *f)
    & _; P$ P. Z2 U, Q4 `9 `
  5. {
    : Q4 p7 V* G' Z* a5 j
  6.         uint8_t temp[1] = {ch};; b# R8 M* M  `
  7.         HAL_UART_Transmit(&huart1, temp, 1, 5); // huart1根据需要修改
    0 i8 T. `& ~6 ^( l" ~
  8.         return ch;" e% `2 {2 i6 l7 X1 m' }2 I3 [5 n7 A
  9. }
复制代码
+ Q8 E( k* L; u( s
3.4 UART1回调函数
  i9 }" w' o# g, {
  1. /* USER CODE BEGIN 4 */
      w9 k1 d, s/ K3 l; z( y$ W* y; Z
  2. // 串口接收数据回调函数
    7 e* ?% }6 P& w
  3. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) / A! Z2 E9 f4 x; R
  4. {
    , q) e. q. U* x1 \
  5.         if(huart -> Instance == huart1.Instance )
    2 f2 ]) i. e4 H" T+ w
  6.         {% Q  K  y- _# p; I% B2 d7 \- }
  7.             // 往接收数组里填新数据,即实现原理的1.39 z3 s; P' k7 H4 r' f" K
  8.                 for(int i=0;i<9;i++)  y/ |4 @: M& ~6 p. y
  9.                 {8 Z& n5 }( m8 j/ o  f% o
  10.                         g_fUART1_Buf<i> = g_fUART1_Buf[i+1];
    , X' y3 M# T" \! J/ u
  11.                 </i>}
    $ v$ i* h$ O6 T& X
  12.                 g_fUART1_Buf[9] = g_fUART1_Byte;
    ( v; g5 Z. J# ]& E+ k$ s; q: _
  13.                 if(g_fUART1_Buf[0]==0x5a && g_fUART1_Buf[9]==0xa5) {& a# P# r( Y1 O* O
  14.                         g_fNew_Pack = 1;// 成功接收一帧数据,刷新标志位
    ) u) J1 B* v8 u; Q- x( B" x$ k
  15.                 }
    1 Q( @1 s  L8 n5 D5 |; b
  16.                 HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1);  1 I1 R4 ?7 s1 _- V% w% \! L
  17.         }  H. i- G. e, w
  18. }
复制代码
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
  1. /* USER CODE BEGIN WHILE */; t7 X& V# f* {( t6 ?
  2.   while (1)
    1 R  y# t+ z, R( W, Y
  3.   {! P# t& w4 j# D9 t2 t+ K/ W
  4.                 if(g_fNew_Pack) // 主程序中判断是否成功接收一帧数据" g3 M& L0 J- I! O% }& ]1 B6 D/ V
  5.          {
    9 R+ @* q2 \8 t6 x/ Z# B7 Z
  6.                         g_fNew_Pack = 0; // 清除标志位$ Q% y0 ?6 C+ ]/ }0 x/ ]
  7.                         g_fNew_Pack_Cnt++;
    5 P4 U0 b  S1 L7 d
  8.                         printf("g_fNew_Pack_Cnt=%d\r\n",g_fNew_Pack_Cnt);// 输出成功接收一帧数据的次数8 L; ?* [& T+ z- u6 p) b. t: W
  9.                         for(int i=0;i<10;i++){
    % ]# S. M5 J; ~/ L
  10. <span style="font-style: italic;"><span style="font-style: normal;">                                printf("%x ",g_fUART1_Buf);// 16进制输出接收到的一帧数据
    + D$ B5 m3 F" I" f/ l: x
  11.                         }
    ; l) Y1 P; @! ^2 X7 A
  12.                         printf("\r\n");* {$ `2 O' u+ U$ P" I) m$ n( s
  13.                         // uart1_handle(); // 串口1接收数据的处理函数,根据需求自己添加
    5 p, D- a. F: `+ A

  14. & h% d8 f! ^6 v0 q
  15.                 }% a+ ]! n& Z# v
  16. " _# H) a! H# _: m+ b
  17.     /* USER CODE END WHILE */
    3 o& e) ~% f4 r- S' h" e& y3 n  H

  18. 3 I( B  O5 r7 v
  19.     /* USER CODE BEGIN 3 */7 b, m9 M( P+ M+ l4 K  @" d) G- K
  20.   }</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 a
20210317155825530.jpg
8 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
20210317155810731.gif
收藏 评论0 发布时间:2021-11-8 15:27

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版