概述. h/ m0 ], H: K% A3 m
本文利用中断实现串口不定长接收(非DMA),使用HAL库,将接收的数据打印出去。
! M4 @/ Q1 B7 @$ I$ l* Z" m! @
1 c3 w3 j/ j0 t( d& [9 N M硬件准备, C9 ]' d/ n9 C* Z
首先需要准备一个开发板,这里我准备的是NUCLEO-F030R8的开发板:- Z1 [7 U5 g' o m$ W" ]: F' f
$ Y, A1 d% P/ p6 c; J/ o
8 L7 G: [- c2 R: D1 t( Q% z2 c
& H" Z P% C# k! }! t; _. K# c& p选择芯片型号
5 s+ j2 ?& P6 Q3 m使用STM32CUBEMX选择芯片stm32f030r8,如下所示:
# s% O( Y( z- _- F/ i3 W$ Y" C2 Z) n
' ~% o" j' j9 O' V7 ^' k
0 Z' p6 z" K+ h7 e0 }2 L# l配置时钟源/ U- `8 d$ e4 B# }7 h
HSE与LSE分别为外部高速时钟和低速时钟,在本文中使用内置的时钟源,故都选择Disable选项,如下所示:9 Z. {* Z/ _+ n
: L+ S/ l: C7 @' P, f
5 U$ i) P9 i9 V! u
2 ^ f2 m4 F# \5 S0 f% Q" s0 `6 b
配置时钟树
: Y/ L: ^2 [2 g6 k+ F' Y+ I+ a4 ISTM32F0的最高主频到48M,所以配置48即可:
H* Y c& q1 k$ A9 L0 z3 m, L& P1 f" ~
" [4 u! u. k! l
, P$ G; K6 N& Y X8 B& M1 }
串口配置
! f5 q- H* n9 P0 D$ j" B" C' c本次实验使用的串口1进行串口通信,波特率配置为115200。
M0 ~) k, w( W: J% F2 Q4 {" V: f# c6 y% `$ L R9 v. L
; Z& O# K" \5 }8 T
. d& X) T# m+ k( ]& K
中断
1 U2 Q2 ]$ O3 F# \( @
% l' v8 [; X3 x2 n# T* i* F8 }
* L4 l4 g7 ~$ W
& }+ E1 w, ~( P& z5 p
GPIO配置- w) q0 [! a4 V3 M4 R
板子上led为PA5端口,故设置PA5闪烁来验证是否正确。8 s3 a1 G$ X5 \' I
4 s4 v. g5 {! ~3 @1 F6 W
( x: d/ h7 b+ Y" V. w3 ` K4 {
3 l4 r. g+ X) \7 d$ Y- F8 _& S定时器配置
+ r8 V: L9 M; r( e1 {' k' H本次实验使用的是TIM3来进行计数。
2 e) |% H- m" @$ p. k. _+ n: p2 e$ x: }8 B+ E8 ?0 K7 a
& S6 i. }# Q6 M* f+ r8 \# @+ S9 t' B
PWM频率计算如下所示
9 W; [ {! O; L6 I( [" K: n
/ P) u* W( y) G D$ R
1 D7 w( y7 O7 g; F5 W3 K2 x/ H0 T; ?
& l: Y& l+ s# O' P在上面配置TIM3参数,预分频系数设置为480-1, 自动重载值设置为10000-1,那么PWM频率为48,000,000/((480-1+1)*(10000-1+1))=10Hz,即 100ms一个周期。% O7 d- g5 A$ m! R5 L) M
, u- ~# v: }; ?0 c
生成工程设置
: m- p8 ]$ ^2 B注意在生成工程设置中不能出现中文,不然会报错。; A& V* R) R- U) @
9 A3 B& q( @. H. w. x
' ~, v) t: u$ I) C) Z, f: D' { D$ T7 X+ u: F
代码生成设置6 V7 z# e0 s: v, q! e2 }
最后设置生成独立的初始化文件:+ P8 ^. \7 H* R4 T6 @6 |
) v* [2 v4 I8 H
8 ^5 J) I" |% M# R
6 R# w& ~) m+ K6 G# C1 x9 t6 J5 p. R生成代码
3 ^6 |8 j0 {4 }1 B* p' I
# q% Q( M. q. }6 L( H: [ t7 W4 z
; L! N* S5 M- J5 R, e' x$ P
F( l; t( M0 {4 x5 i% x
配置keil6 v: o0 Q# ?' ] ^
1 i$ C' X. m$ k3 w) X% v' s7 J( t1 f
3 ?6 T) _1 l* J+ K: J
2 H0 S+ p4 }9 \1 a$ v( n3 L3 B& r代码. l( _) F& @; `6 D* U+ E
在main.c中,先加入头文件。- /* USER CODE BEGIN Includes */5 Q# _. E# x0 v! s0 C0 Z
- #include "stdio.h"//printf头文件
5 Y: T3 f. ]* {4 ?+ }# G7 f* i' k; A - #include "string.h"//memset头文件/ E: c; K4 _1 {: V
- /* USER CODE END Includes */. j: U+ T! H" E" A# x8 z5 z
复制代码 4 X2 x* k3 ?! U% _7 g% e
定义变量存储。
/ W2 P% c5 ^4 X9 d/ o
# \! \: d* S4 o% H6 R5 D1 F- /* USER CODE BEGIN PV */
8 x+ X! [5 s0 ~7 o2 N! ^7 g - uint8_t RxBuff[1]; //进入中断接收数据的数组% A6 {: d& Y) O) ?) r8 {( Z/ _1 P8 p
- uint8_t DataBuff[5000]; //保存接收到的数据的数组, e! p0 x: Z8 v: P; Y
- int RxLine=0; //接收到的数据长度
0 W' Z* I" A: c8 k m6 s8 j - int Rx_flag=0; //接受到数据标志/ j- h6 t5 h! ]! Q" Y- _
- /* USER CODE END PV */
; a( l1 n% k: s% r8 D ~ G6 h( K
复制代码
) F9 \) M* c; N+ D$ o定义printf的重定向函数fputc。
! F. U9 E8 t$ n7 D! k5 t B/ x" g. U# r& F0 `4 t* y
- /* USER CODE BEGIN 0 */( K- h! w! F& H4 I6 N9 u. e
- void printf_usart(void);//输出内容
) N- M2 C0 S4 C - int fputc(int ch, FILE* file)//定义printf的重定向函数fputc,满足串口调试打印3 h4 M* L6 [6 \% ^% ]9 U
- {
3 o6 O' g; q6 P- K# W: q - return HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 100);# P d9 f7 ?* ?7 P( ` m
- }
& b' b% Q8 q$ { - /* USER CODE END 0 */
复制代码 6 ~" V3 O* Y- @' T
打开串口和定时器。
4 s- T/ w3 g: K. W
5 Z7 _5 I& F' v, _ H- /* USER CODE BEGIN 2 */
* p2 p d" T' k5 E4 D - HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuff, 1); //打开串口中断接收. L- u4 j( L0 { L1 z% z
- HAL_TIM_Base_Start_IT(&htim3);//开启定时器2 c; I' b% ^+ q$ R: q6 U4 k
- /* USER CODE END 2 */
复制代码 6 Q3 s! e- @' y& A$ ?2 K
串口接受代码,当接受到最后数据为FF时候,直接打印,否则等待100ms打印。
& ^0 {2 M+ t8 e6 W; ~
5 L5 ?- l9 ^- v# p8 N X) K- /* USER CODE BEGIN 4 */3 v5 B& X6 N4 a
- void printf_usart(void)
5 x, ^3 w+ O3 y - {* F) G3 F+ P0 A
- printf("数据长度=%d\r\n",RxLine);
7 p; C, k3 v/ Y8 C5 V - for(int i=0;i<RxLine;i++)7 X3 Q4 [8 B! Q. X, U: \% G9 r; V( e
- printf("数据:[%d] = 0x%x\r\n",i,DataBuff<i>);
+ F0 I6 }$ y6 F/ t - memset(DataBuff,0,sizeof(DataBuff)); //清空缓存数组
, q4 Y0 a+ e1 `1 J; l1 B0 e4 G - //memset()作用:可以方便的清空一个结构类型的变量或数组。; O0 N5 i: _+ [+ @
- //例句:memset(aTxbuffer,0,sizeof(aTxbuffer)) 用memset清空aTxbuffer。
- x5 g+ f/ [( z9 ~ - RxLine=0; //清空接收长度/ b/ ]1 l# H' e" f
- }" S4 H N$ Z/ f9 Y. [' {2 K
2 e) {3 ] p, i, r- 3 M3 A# x. q3 V$ @& N" i1 Q# Z, n
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
% ^& m4 c+ e m, R* k0 H - {' v d @* N# U5 m
- if (htim->Instance == htim3.Instance)' q2 N& U9 c7 m% l0 u" i) a- U
- {) y- e3 |; j4 g( s/ c, j& _# D8 x
- HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
) G1 x; H2 g& | - /* Toggle LED */
' L* Z) T6 T1 I$ e - if(Rx_flag==1)
( ^/ X9 Z6 k `' X* v n2 N6 ] - {7 ^, K0 G5 b: X+ b9 H1 N
- printf_usart();# Z& y U) @. s! H" y- c. }( }
- Rx_flag=0;
$ E) _ T& O0 r' l _( } - }* B2 j& U; s% a# J1 [
- }
7 o0 {9 R/ v! [. L9 O - }
, I2 d6 K* W/ d: V" k& j- _% O2 q6 X" [9 G - : p# \2 |, P4 \ j
- & O. @+ [4 Z+ u! j/ r0 q) z5 t
) h/ {, N/ C' \9 N% V. q- // 捕获中断回调函数,每次捕获到信号就会进入这个回调函数6 q$ F% W- r) X, L8 _$ n# Q$ W
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle)1 I L# z! l4 F3 z3 Z
- {
+ a f9 e6 D" s8 U( r4 Y4 s0 a. V - RxLine++; //每接收到一个数据,进入回调数据长度加1" H, O: M a) y, P
- DataBuff[RxLine-1]=RxBuff[0]; //把每次接收到的数据保存到缓存数组1 R( D% d c$ [1 [, ~
- Rx_flag=1;
. p4 ?( S0 L9 l - if(RxBuff[0]==0xff) //接收结束标志位,这个数据可以自定义,根据实际需求,这里只做示例使用,不一定是0xff
0 `+ w, Z. k8 D% S* Q4 J* k3 H9 w - {+ V6 \, c5 h3 c8 N, O1 @7 L z7 c$ E
- printf_usart();
3 `2 c6 Y7 c+ m - }
) K0 l8 F- c5 _! D - RxBuff[0]=0;
4 C$ }8 G0 m" v' P& F& ~% e - HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuff, 1); //每接收一个数据,就打开一次串口中断接收,否则只会接收一个数据就停止接收0 m( n0 ?' m4 G/ J: t: f& O
- __HAL_TIM_SET_COUNTER(&htim3, 1); // 计数清零,从头开始计
; V1 n8 b# x, ]+ n6 k* D - }
% G' \7 L2 g+ W; X% V, y7 ] - /* USER CODE END 4 */
& n; q, T' {$ H2 S/ o1 { - </i>
复制代码
8 x, q: d7 |9 p# D2 d6 E演示效果$ `2 T; J" s6 P) b' b
可以看到 发送11 12 13需要等待100ms左右才能发送,如果最后加上ff直接发送。
: H' z" d7 n7 r# S8 W/ `2 j$ d3 b8 ]2 [( T
|