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

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

[复制链接]
STMCU小助手 发布时间:2021-11-8 15:27
1 实现原理8 a% u& [( i% [8 T0 b
使用串口通信可实现数据帧的收发,完成机器人控制、采集数据的传输等任务。制定串口通信协议并定时发送一帧数据,常见的处理方法是利用空闲中断,但STM32 HAL库好像没有专用的空闲中断,自己实现起来比较麻烦,这里利用非空中断可以实现同样的功能,缺点是效率比较低,稳定性经过测试也还可以,如果有更好的思路欢迎提出。
4 L; S# q( d8 B) B6 {8 s& ^% T
% t" P, _8 _  f/ W4 v) S) r1.1 制定串口通信协议
5 P: ?' S/ U4 A) ?  D! l8 \制定串口通信协议时一帧数据的长度和内容可以自己定义,STM32的串口传输的数据类型为uint8_t,注意取值不要超过0-255,下面给出一个电机控制的串口通信协议表做示例
0 |+ e5 N9 u- u* D: Y  T$ J$ f2 ]. G4 u9 r2 H3 c. [- U+ D5 K9 ]' w
PGWOK2SYP{HGB`WU`E`(@$J.png
) C4 j: E! x8 W8 M
! E6 W) B' F) S6 x$ D- X6 b
1.2 分析中间变量" h$ D5 b: E4 t" e% v
创建接收数组g_fUART1_Buf,其长度等于串口通信协议一帧的长度# A2 {8 L. j0 v; M% t; X
创建接收一个字节的暂存变量g_fUART1_Byte,配置一个字节中断一次- W0 w% v2 c  b+ b! o% l
创建g_fNew_Pack用于主程序中判断是否接收到完整的一帧数据
- F) S7 j6 H- b8 ~( U创建g_fNew_Pack_Cnt记录成功接收一帧数据的次数,方便调试6 K" K) Z5 @& c8 a9 R
  1. uint8_t g_fUART1_Byte = 0;
    0 r( T' J; u, B+ g! S# B( d
  2. uint8_t g_fUART1_Buf[10] = {0};
    4 Q: g# j0 Q0 Y# l3 R3 y

  3. 2 ^, V- G+ i4 j7 ]
  4. uint8_t g_fNew_Pack = 0;* O* o: ~- b! g% r  J9 J
  5. uint8_t g_fNew_Pack_Cnt = 0;
复制代码

- Q" W" h: q6 Y1 X1 G
+ N2 B. l4 a0 S2 _) ]1.3 接收数据的处理
4 \6 l* r9 P& k新接收到的字节g_fUART1_Byte写入接收数组g_fUART1_Buf的末尾,每接收一个字节,就将前一个字节左移,保证新接收到的字节一直往数组里填充,当接收的字节超过接收数组的长度后,继续左移,去掉旧数据,填入新数据
- R# a. z: |( q: y' H* K8 o# J5 j8 w0 g$ p0 @' X* u
20210317153926655.png

  \9 p3 V3 J2 l" O# o: x' e* z4 |7 a" Z4 ~
2 STM32CubeMX配置
8 M) ?* h9 G* e" G" W! t2.1 SWD调试接口配置
; o. d3 M0 P, B  h2 m) ySWD接口用于程序的下载、在线调试
" Z. X/ y( R$ J  P# ?# b- ?" C5 _$ W: O4 @+ \9 t- _
20210317153912428.png

+ o8 `0 Y" I2 S9 N
& N2 Y* Z& ^0 D: y2.2 时钟树配置
# R. a( k; `7 R6 Y, q7 v  LSTM32G0系列内嵌高精度(±1%)RC振荡时钟,无需外部时钟,所以这里并未使能外部高速时钟,开发板上也没有焊接,直接在时钟树配置选项的HCLK输入64,配置最大的64MHz时钟频率即可。
1 D! e' X9 U  S, c2 m' {1 x0 P4 Z
20210317154247720.png

! x" s6 Z5 K+ `$ N' g% e
" |: Q8 J/ R$ B# [* K3 F2.3 UART1串口配置
% z5 a9 r& V: }& L2 b; xUART1用于接收上文制定的串口通信协议,并开启非空中断
; A5 s/ {' q' v2 a0 v- ^. a
/ u' O( s* H% C/ X, I! M+ D/ f0 i
20210317154445383.png 6 K' v$ \  H# u* r, }% a
4 F! x1 F1 s# e" P1 Y

) ]# K& r4 @3 n% L
20210317154445195.png

$ A3 b9 H: e4 j5 O  S
# z% i: U; H4 \: J) g5 D
$ j+ m5 |# z. d2.4 生成工程! f& J: d9 W! c6 Z  l, y
输入工程名,选择存放路径(不要有中文),选择IDE为MDK-ARM;只生成用到的文件(目的是提高工程编译速度,减少工程占用空间),并生成单独的.c/.h文件,点击生成代码8 |4 V* F  h( Q9 S, C3 h! t2 u0 z

3 Z1 @% p5 ]/ V1 s' r0 a- Z
20210317154512883.png
8 r. M# u& ]# |- f. F# s8 }

3 c& \; n8 z5 a
20210317154512880.png
8 [( j2 f) u. W" o. M3 n( V& m
& {! P# Z8 K. `. c3 e8 x
3 添加用户代码
! s  I- O3 R; f8 ?3.1 定义全局变量0 K/ x9 l9 g3 _6 u+ A
  1. /* USER CODE BEGIN PV */
    . g( t! _- X* d9 |4 U3 Q5 t1 ~. r
  2. uint8_t g_fUART1_Byte = 0;
    2 V; a" R& L9 l* h, Y& q# q- J
  3. uint8_t g_fUART1_Buf[10] = {0};
    * q! S; l! A6 O3 N
  4. , o+ x& `4 @% S, W# F9 `
  5. uint8_t g_fNew_Pack = 0;) P7 @4 {  i) }
  6. uint8_t g_fNew_Pack_Cnt = 0;8 Z& l( H* t! G; `+ v5 ?2 @
  7. /* USER CODE END PV */
复制代码
" @. {9 s  I$ \' Z: t; _+ F5 Q$ b
: b; Z2 u3 y8 f4 F/ A( j
3.2 UART1中断初始化- I, R  v$ k1 b( f- b
  1. /* USER CODE BEGIN 2 */
    0 B, z6 B' |+ p! v
  2.         HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1);  // 启动串口非空中断,第3个参数代表1一个字节中断一次
复制代码

, X8 z2 o! X* F/ e3.3 UART1的printf重定向  V8 ~( O. X1 l" r% G% q: v9 l
  1. /* USER CODE BEGIN 0 */
    + e% _* x5 x8 {- e$ @
  2. /* printf重定向 */4 t0 @6 y& ^, Z9 o  y
  3. #include "stdio.h"5 j' L$ ~/ ]* {* j1 u0 T1 S- C1 o
  4. int fputc(int ch, FILE *f)$ ]5 T: u& n9 l# h5 _
  5. {1 N" W. @- ?3 [" d/ w) x, f
  6.         uint8_t temp[1] = {ch};
    / H! P: `" l1 K' H7 p8 O
  7.         HAL_UART_Transmit(&huart1, temp, 1, 5); // huart1根据需要修改8 T4 [: o; M5 Y4 D
  8.         return ch;5 J" V7 t4 |7 ^) j+ n+ E
  9. }
复制代码
3 z6 h: y9 I! }# U7 S! s
3.4 UART1回调函数2 W% y. f' {8 h( U
  1. /* USER CODE BEGIN 4 */
    : P1 G$ E; W* I5 }! o" a
  2. // 串口接收数据回调函数
    $ L, O% G/ Y7 e5 c
  3. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) . H9 H# v$ D# [' @0 K# e
  4. {
    5 I1 d( J. N9 J: F6 _& B
  5.         if(huart -> Instance == huart1.Instance ) - `! v, k) x7 k0 A8 v, o) w
  6.         {
    ( c9 b  p: D% i; m* N
  7.             // 往接收数组里填新数据,即实现原理的1.3
    ( v- A% p; {5 t5 s
  8.                 for(int i=0;i<9;i++)) J: _. ]5 u8 z2 q2 v  _( s
  9.                 {
    ! @$ g3 m5 S/ @' T) C/ v7 s: L, W  v" A
  10.                         g_fUART1_Buf<i> = g_fUART1_Buf[i+1];
    0 B, P7 P9 c6 O4 h
  11.                 </i>}
    . m) L" P& Y6 B5 _6 w/ ~
  12.                 g_fUART1_Buf[9] = g_fUART1_Byte;: e, W2 r4 X7 J* k
  13.                 if(g_fUART1_Buf[0]==0x5a && g_fUART1_Buf[9]==0xa5) {1 ~' g, K/ o4 k$ ?0 C6 Y
  14.                         g_fNew_Pack = 1;// 成功接收一帧数据,刷新标志位
    ) Y1 I  p$ _% s9 \: p( j
  15.                 }9 S/ C% i( c  Z$ p1 K$ d  v. x4 f0 R
  16.                 HAL_UART_Receive_IT(&huart1, (uint8_t*)&g_fUART1_Byte, 1);  
    - s; P( Y% z: D# T  f
  17.         }
    " c& U5 n( Z. {2 D' ~0 ?# ]" y
  18. }
复制代码
! p. r2 d4 d; k; P

% e: o9 z; k: _2 T8 r3.5 接收数据的处理# G5 z+ m5 q- g% I4 k' ~
  1. /* USER CODE BEGIN WHILE */6 h# m; X5 K1 ]% d0 m
  2.   while (1)- n* c# ?- N' i# F5 F
  3.   {( O4 |; {, C3 N- t2 a3 O
  4.                 if(g_fNew_Pack) // 主程序中判断是否成功接收一帧数据9 s5 A% k+ w1 `- j' d
  5.          {5 H) ^; B& C% O' G1 v$ h0 ~
  6.                         g_fNew_Pack = 0; // 清除标志位
    9 p+ H  p% E  ^$ |' U
  7.                         g_fNew_Pack_Cnt++;
    , W$ ^, W" m- Q9 n+ ]2 V
  8.                         printf("g_fNew_Pack_Cnt=%d\r\n",g_fNew_Pack_Cnt);// 输出成功接收一帧数据的次数# m$ K! u& u- A5 b0 H$ h
  9.                         for(int i=0;i<10;i++){
    5 k7 F2 s; g9 u$ ~
  10. <span style="font-style: italic;"><span style="font-style: normal;">                                printf("%x ",g_fUART1_Buf);// 16进制输出接收到的一帧数据: ]+ q2 T  n' g
  11.                         }
    & B5 R. C$ L# e$ E7 D$ @0 t& l
  12.                         printf("\r\n");: _0 s5 _+ L! C2 P
  13.                         // uart1_handle(); // 串口1接收数据的处理函数,根据需求自己添加( R/ ?8 A5 k' K* f- C

  14. : _- f: N8 M; Z3 [0 b  \
  15.                 }! j2 _4 E( V. P+ E
  16. $ m/ @, i6 v7 g+ D$ N$ p
  17.     /* USER CODE END WHILE */4 L1 a. l6 k+ i3 I2 D8 x& v
  18. * k( T6 T5 s' D# t& Q
  19.     /* USER CODE BEGIN 3 */9 _4 y( R3 C8 l+ D: w( C& ^
  20.   }</span></span>
复制代码
: S1 @2 X7 X) D: j8 K5 w+ Q0 J
4 效果演示
" k# I! N$ g6 Y5 ~* g1 @gif中为方便演示,除了帧头帧尾,没有使用上文串口协议定义的内容,用11 22等数据(16进制)代替
1 _6 A& S2 c. I  ?
$ J  s5 J( q& W2 U1 Z
20210317155825530.jpg
8 u3 u5 B- U! c5 q: }7 P8 \1 ]

5 A1 f0 f$ \6 [6 m3 i; A* U* h% b% r  u

3 X; {- J8 g" V7 o. w' q' @& ]
20210317155810731.gif
收藏 评论0 发布时间:2021-11-8 15:27

举报

0个回答

所属标签

相似分享

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