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

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

[复制链接]
STMCU小助手 发布时间:2021-11-8 15:27
1 实现原理$ H( m- g; v$ f; u
使用串口通信可实现数据帧的收发,完成机器人控制、采集数据的传输等任务。制定串口通信协议并定时发送一帧数据,常见的处理方法是利用空闲中断,但STM32 HAL库好像没有专用的空闲中断,自己实现起来比较麻烦,这里利用非空中断可以实现同样的功能,缺点是效率比较低,稳定性经过测试也还可以,如果有更好的思路欢迎提出。
7 Q# j6 T( |: p; M8 F% Y) A
% W- \8 l4 `1 h7 Z( l6 M3 ~/ B' j' d1.1 制定串口通信协议! o! f7 I9 x5 O* {* Y) U
制定串口通信协议时一帧数据的长度和内容可以自己定义,STM32的串口传输的数据类型为uint8_t,注意取值不要超过0-255,下面给出一个电机控制的串口通信协议表做示例
2 A) J. v9 a# D0 S/ w! |; X% q  u1 U$ e# F; b! Z) T
PGWOK2SYP{HGB`WU`E`(@$J.png

: E8 i4 A9 N* W  O$ {6 N& g2 q6 p& c" F7 Z9 I
1.2 分析中间变量2 [) Y3 m* ]6 F+ F( E. k# Z3 n
创建接收数组g_fUART1_Buf,其长度等于串口通信协议一帧的长度
* |) b  a8 Z  `0 g# b创建接收一个字节的暂存变量g_fUART1_Byte,配置一个字节中断一次
( ^! Q# z* D" W/ n# Y创建g_fNew_Pack用于主程序中判断是否接收到完整的一帧数据
" Y* H1 y5 ^, I) ]+ I3 `创建g_fNew_Pack_Cnt记录成功接收一帧数据的次数,方便调试
% m  @( \3 p1 W( s( x
  1. uint8_t g_fUART1_Byte = 0;
    , ^! }7 p- d; @' ~0 Z+ f
  2. uint8_t g_fUART1_Buf[10] = {0};
    6 u) L* H' D4 Z: O' e

  3. 7 b$ W  ], A! ^1 C7 y: q4 x7 }
  4. uint8_t g_fNew_Pack = 0;. X6 \6 d& p4 i6 T. L  F4 n
  5. uint8_t g_fNew_Pack_Cnt = 0;
复制代码

5 H9 T3 ^; d. e
. K9 ^3 @; T3 ?8 J2 g1.3 接收数据的处理4 [1 i; n! n* b/ t$ \
新接收到的字节g_fUART1_Byte写入接收数组g_fUART1_Buf的末尾,每接收一个字节,就将前一个字节左移,保证新接收到的字节一直往数组里填充,当接收的字节超过接收数组的长度后,继续左移,去掉旧数据,填入新数据
3 j+ L% D0 o- j: i! ?! g2 i& {0 ]  O' |; R5 N
20210317153926655.png
6 J6 r3 T- |; |; i; W: L( f
- W$ I+ b' {' H3 {
2 STM32CubeMX配置  g7 H" d6 _1 O" |( @  X
2.1 SWD调试接口配置
5 w/ R: G% k, @' [SWD接口用于程序的下载、在线调试1 E, Z: a& G# @2 q4 x2 U- w

2 |9 c. c" {& r! m# F0 \0 r5 n3 q4 B  x
20210317153912428.png
# I- ]0 x) }6 p7 t! r' ^9 |

" n  V4 w  N) \# k0 `" l2.2 时钟树配置* _2 p- M' x; P" ^' }* H
STM32G0系列内嵌高精度(±1%)RC振荡时钟,无需外部时钟,所以这里并未使能外部高速时钟,开发板上也没有焊接,直接在时钟树配置选项的HCLK输入64,配置最大的64MHz时钟频率即可。8 G7 c6 ~' Z! I, K" b& [4 ^. i0 H
4 A  _) O/ H6 j. v
20210317154247720.png

9 D% Z- n( r, s. i
; T1 q7 Z9 e1 x, J* y2.3 UART1串口配置0 x9 P& N/ W. V0 _  R
UART1用于接收上文制定的串口通信协议,并开启非空中断
, T1 U) M* }0 h' |" }+ q! \
6 i: w7 t8 J3 W; N4 z2 L2 W2 L' v* Q
20210317154445383.png
8 n( ~! h9 K* W$ O% t
- E  q2 J+ l8 q, y2 i
& t4 [0 j0 T: d9 e, y9 ~) t
20210317154445195.png

- u; o7 p5 j  a6 ~9 G; ^
: G8 I, R$ Z& V6 o1 V2 v+ F3 q) R6 I) j) g8 P- V7 n
2.4 生成工程
8 G+ `$ I2 V3 H! d4 x输入工程名,选择存放路径(不要有中文),选择IDE为MDK-ARM;只生成用到的文件(目的是提高工程编译速度,减少工程占用空间),并生成单独的.c/.h文件,点击生成代码
: \( l. h. P9 D/ F) A9 {. ]* E  I5 g* y1 n& O
20210317154512883.png

0 Z+ n" P6 f/ Q# H/ D  Y- \3 m9 ?8 u
20210317154512880.png
$ z7 m, F! S$ s- l5 D
+ Z! V, ?6 ~" Q0 c7 h5 Q
3 添加用户代码
9 F# E: r3 Z! B" f! m. o8 {5 G3.1 定义全局变量- D5 M8 z. f$ c) D3 f
  1. /* USER CODE BEGIN PV */
    / f3 n! u8 `& ~) }& ^
  2. uint8_t g_fUART1_Byte = 0;$ n# ]; T9 t6 L: `# n, l4 {
  3. uint8_t g_fUART1_Buf[10] = {0};
    : U% a% n; z8 V+ X. Z
  4. + [6 K) j  T' H, E5 S: ]6 q, m
  5. uint8_t g_fNew_Pack = 0;
    4 C9 L+ x/ N" J9 p6 y
  6. uint8_t g_fNew_Pack_Cnt = 0;2 Q8 U, M1 c( |7 O6 H/ v
  7. /* USER CODE END PV */
复制代码

; m# v( R. L' B* d6 ~  l6 j- }  F9 K! ?$ l
3.2 UART1中断初始化
9 D7 `: S  j1 s8 I& r. N5 f- Y
  1. /* USER CODE BEGIN 2 */2 X, r8 j# _% u9 p
  2.         HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1);  // 启动串口非空中断,第3个参数代表1一个字节中断一次
复制代码

8 N; A: \' m1 E8 f8 t* Q4 y$ G3.3 UART1的printf重定向  u+ \, E! N  G: w+ I
  1. /* USER CODE BEGIN 0 */5 e- I+ z$ g, O% U" X
  2. /* printf重定向 */+ X% t: j) T: I
  3. #include "stdio.h"
    + w# t  f% y3 h" C( |2 A
  4. int fputc(int ch, FILE *f)' u3 P' Y/ Z& i, Q! s' M: Q
  5. {
    ! L6 Q0 ~/ \8 a- ^9 j
  6.         uint8_t temp[1] = {ch};
    8 M! a* u% O( Y8 A) F! e
  7.         HAL_UART_Transmit(&huart1, temp, 1, 5); // huart1根据需要修改
    ) T$ O9 N. E# k/ A+ B, g' p
  8.         return ch;5 n& p" G0 G1 F0 P  g" h. T5 W& Q
  9. }
复制代码
6 J% I; n2 V$ S; A7 o. ^! G7 z
3.4 UART1回调函数
; g  ]5 x( a% h. S1 p5 {5 V; o
  1. /* USER CODE BEGIN 4 */; c% {- I7 R9 [! G& [# Q* P$ x
  2. // 串口接收数据回调函数0 t9 n% U: @. g
  3. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    5 t6 y; M8 X# U. ^1 p
  4. {: W! r4 }1 i5 y
  5.         if(huart -> Instance == huart1.Instance )
    . D" \% l' X) C, Z
  6.         {
    . c7 r- l" J5 V! O% t5 N5 m( F
  7.             // 往接收数组里填新数据,即实现原理的1.3
    3 A: {% g0 m8 W; J( ~2 Z. t
  8.                 for(int i=0;i<9;i++)
    2 W7 o# Q2 ]! r; B& d# B
  9.                 {
    * k2 c) G4 P$ B  d5 I
  10.                         g_fUART1_Buf<i> = g_fUART1_Buf[i+1];9 h$ W9 [! _+ F4 ]$ ^2 U4 A! B
  11.                 </i>}6 ^$ w. }) x: [3 a+ I6 E
  12.                 g_fUART1_Buf[9] = g_fUART1_Byte;& B, m8 T& [5 ]
  13.                 if(g_fUART1_Buf[0]==0x5a && g_fUART1_Buf[9]==0xa5) {
    " a1 }; I* J- U( l9 U0 {" S) s  ?* R4 A
  14.                         g_fNew_Pack = 1;// 成功接收一帧数据,刷新标志位! Q! b- }3 e: ]+ |* A# `: T
  15.                 }0 Q# W7 I3 H5 e5 @
  16.                 HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1);  
    , P- a( }/ d1 n. z/ z  o( O4 z
  17.         }
    ' `" v+ r7 c( o% P/ n  l% n; @
  18. }
复制代码
. g0 F" [+ g; G6 Z

  w; u- s5 f, R3.5 接收数据的处理
6 Y& ]! Q  q9 {' t- A$ m
  1. /* USER CODE BEGIN WHILE */
    $ y' ]+ m2 n: M; f8 W- M
  2.   while (1)+ U1 j9 }5 s! i
  3.   {( q3 Q& _, ?2 M. t" j2 K
  4.                 if(g_fNew_Pack) // 主程序中判断是否成功接收一帧数据
    : r9 ?) h0 t; e# j1 J- p
  5.          {
    8 f: A. T9 D- x( z
  6.                         g_fNew_Pack = 0; // 清除标志位
    " Y1 G$ r! b, h2 c3 ?! P
  7.                         g_fNew_Pack_Cnt++;
      U7 @" ~( Z- @! E% P# Z
  8.                         printf("g_fNew_Pack_Cnt=%d\r\n",g_fNew_Pack_Cnt);// 输出成功接收一帧数据的次数
    8 D8 e, h! w8 D
  9.                         for(int i=0;i<10;i++){
    ! [* o) @- f4 x5 w4 C
  10. <span style="font-style: italic;"><span style="font-style: normal;">                                printf("%x ",g_fUART1_Buf);// 16进制输出接收到的一帧数据. e% c( j* a5 L0 x6 y4 Q
  11.                         }
    7 S; w2 Z# K5 m) t6 _5 W
  12.                         printf("\r\n");" h7 }. B0 ~3 Q: u# B6 ]
  13.                         // uart1_handle(); // 串口1接收数据的处理函数,根据需求自己添加( ~+ E3 A( x! |' E3 W$ d

  14. $ d3 ~( K' f4 C, ]% o# ^
  15.                 }5 D, o. q' d/ O4 w* c
  16. * @9 B+ n# T: t- ^: ~
  17.     /* USER CODE END WHILE */
    % ]8 H3 U3 [9 p* m. d  T+ P

  18. ) s$ ^9 V% S! B, ?, y4 Y+ t
  19.     /* USER CODE BEGIN 3 */( x6 i" c, F# ~) k2 e# O$ k4 `8 P
  20.   }</span></span>
复制代码
$ [- R" r- h, o& x# i) F
4 效果演示
* ?+ M5 R, }9 n) D3 K6 ^3 V/ Dgif中为方便演示,除了帧头帧尾,没有使用上文串口协议定义的内容,用11 22等数据(16进制)代替
, R% `. s$ q7 }6 h# z6 p( p
1 E9 S4 n, w( |2 v6 x
20210317155825530.jpg
9 X4 Q2 Z8 K0 e: t! Y1 l

- Q3 v. W7 j# d0 @* X" z
0 Q1 F' ^  D3 c9 |) C) U5 i0 d! U; R/ S3 ^0 m' q1 T
20210317155810731.gif
赞 收藏 评论0 发布时间:2021-11-8 15:27

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版