我这里使用的芯片是 F1 系列的,主要是利用 DMA 数据传输方式实现的,在配置工程的时候要注意配置好 DMA,并开启中断。 5 g5 u* ~. J6 V% Y
如果出现数据长度对,可是数据接收不完整,把Memory勾选即可: 4 c" c5 m! r: ^/ V* ^1 ]* _5 ^
1、利用STM32 cubemx 建立一个工程 2、利用STM32 cubemx 生成代码后,我们先定义一些变量来使用 - /* 自己添加代码部分 */
7 W( A9 N$ q0 K. H7 R6 R - volatile uint8_t rx_len=0; //接收数据长度
* N0 g7 Y1 v8 l1 M/ c3 F# \ - volatile uint8_t recv_end_flag=0; //接收完成标记位
; u" f9 Y0 }8 b+ Y$ Q - uint8_t rx_buffer[100]; //接收缓存
7 \/ `1 A" \3 O) f - char BUFFER_SIZE=100; //不定长数据的最大长度,设置为100则最大长度为100
复制代码 7 N, A/ h4 T" u2 ]7 a$ p
这里为什么要定义volatile 关键字呢? 主要是因为volatile 关键字提醒编译器定义的变量是易变的,编译后的程序每次需要存储或读取该变量时,会直接从变量地址读取数据。在中断或多线程中使用volatile关键字可以避免不同优化等级时程序出错,提高程序的鲁棒性。 接着对串口初始化添加一些代码,程序如下: - /* USART2 init function */
: p, A/ e' @6 g& i7 ~ - static void MX_USART2_UART_Init(void)! \9 A5 d' |' [' s% \7 y& u& u& ^
- {
& g- b) |$ W* n9 {& {/ k - ( L: s3 Q6 y" m) f. |9 V1 [6 B
- huart2.Instance = USART2;/ I1 q; w& o9 |
- huart2.Init.BaudRate = 115200;2 B3 s* m5 E! z: k
- huart2.Init.WordLength = UART_WORDLENGTH_8B;8 A4 O! S8 B/ E% g; d' A- W- c
- huart2.Init.StopBits = UART_STOPBITS_1;) W! ^( t8 M) B: B$ b; |( L
- huart2.Init.Parity = UART_PARITY_NONE;1 ^# \6 S( n) k9 Y
- huart2.Init.Mode = UART_MODE_TX_RX;
: B5 z+ N( z( O& o( | - huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;. s3 [" V# R9 T! R s# V* c; s/ [
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
/ q$ n, g9 B R- L: x - if (HAL_UART_Init(&huart2) != HAL_OK)
' v. Y- Q$ P5 N6 S - {
6 V5 R5 \( t3 k7 g+ b7 W - _Error_Handler(__FILE__, __LINE__);
) e/ T d1 j$ |, X& S: l' t - }7 [+ Y* g" m# s! S& W0 H& q
-
; ?# G2 D* {$ c- w; J7 I! J - /* 自己添加代码部分 */
6 w) e7 @# |6 E3 y+ _% c - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能idle中断
0 x. G) _! [2 I1 [) Q/ E% e! C& e" z - HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //打开DMA接收,数据存入rx_buffer数组中。; R4 ~* J* D% y7 `& J- r: r5 c& m
- - [ J. e* l$ c1 |6 l7 H) C- C
- }
复制代码3、接收函数我写在了另一个文件上,其他文件要用到上面 main文件里面定义的变量就要声明一个外部变量 - extern volatile uint8_t rx_len;( ~% K; x4 ]' D* f7 B+ H
- extern volatile uint8_t recv_end_flag;! d3 Y% J- r% p5 d7 J
- extern uint8_t rx_buffer[100];& e0 [, ~6 b% u% ]# S
- extern char BUFFER_SIZE;
复制代码
! T2 q; l$ y1 O) s4、接着修改串口中断服务函数,在串口中断服务函数里添加接收代码,代码如下: - void USART2_IRQHandler(void)
8 `/ r, [# N9 X) ]# d, i( x4 i. d I - {( n; i2 D6 Q! E) h0 a- `
- /* USER CODE BEGIN USART2_IRQn 0 */
% D" B' C! p% m5 Z4 D0 H -
3 l1 |$ h/ p3 W0 _ g - /* 自己添加代码部分 */
1 V7 K9 I7 z/ P - uint32_t tmp_flag = 0;9 }/ ? E" w1 ^. L
- uint32_t temp;
8 ^. ^% S" R+ n1 h - tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); //获取IDLE标志位: X7 Q, I; k% y( s
- if((tmp_flag != RESET)) //idle标志被置位
7 I' c% g9 D6 x: C! Y6 z - { 2 F7 R+ X# s; N& x" f
- __HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除标志位
, u# g8 _1 v5 F, I" ^ - temp = huart2.Instance->SR; //清除状态寄存器SR(F0的HAL库USART_TypeDef结构体中名字为ISR:USART Interrupt and status register),读取SR可以清楚该寄存器* y: d' ^2 W, _8 Y% V3 u
- temp = huart2.Instance->DR; //读取数据寄存器中的数据,读取DR(F0中为RDR:USART Receive Data register)/ n0 k/ e9 A0 v; {& d
- HAL_UART_DMAStop(&huart2); temp = hdma_usart2_rx.Instance->CNDTR; //获取DMA中未传输的数据个数,NDTR寄存器分析见下面
7 r' H% O3 ]6 d' M) V* o - rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数3 s7 x6 V! b8 a! o0 F! s
- recv_end_flag = 1; //接受完成标志位置1 % H$ V0 R3 s) k& u I3 Q$ _ a" }
- }* z- S* a$ v2 \, S5 s1 {
- / U6 A+ u5 e7 U$ i4 G5 Z- ~2 K
- /* USER CODE END USART2_IRQn 0 */ ! a; J# h9 ^5 Y
- HAL_UART_IRQHandler(&huart2);1 q2 d' V3 N8 X0 I$ I9 r1 p
- /* USER CODE BEGIN USART2_IRQn 1 */
6 }8 u. j$ g2 Q9 C; {/ q
1 ]8 u6 h0 f) T" [- /* USER CODE END USART2_IRQn 1 */* X. ^ h$ H. B2 a7 g" C
- }
复制代码
" i# S8 Q( U; c c# | 上面的 CNDTR 寄存器在 DMA 通道结构体中定义了 CNDTR 寄存器,这个不同的芯片HAL库里面定义的命名有点不同,有兴趣的可以自己去查看一下,那为什么是未传输的数据数呢,STM32的中文手册给出了该寄存器的具体说明。(注意:建立工程的时候要添加串口TX RX 的DMA通道,以及打开DMA中断) - /**
4 ^$ ~6 ? }8 s) A - * @brief DMA Controller$ h9 V5 `! g s: K: {; X" V
- */) T8 o9 c% G) n4 [
- typedef struct8 {: Q3 g! \: ]# u
- {. X- U* Y \% a, Q
- __IO uint32_t CCR;
9 |( S# I; T- b+ u& F% h8 F - __IO uint32_t CNDTR;2 O; }9 t! z/ _7 A3 _$ G% x9 s( x
- __IO uint32_t CPAR;0 p3 N* h5 s; I( q5 E# M* I
- __IO uint32_t CMAR;
( g; G5 |3 L4 H# D: U - } DMA_Channel_TypeDef;
复制代码 ]& c& C4 r1 l& q
寄存器说明如下: 6 F i( v% u( w5 o" M- a
: O- O# h' T9 A( U5、接着编写接收处理函数,代码如下: - /***************************************************************& D' b2 Z* q0 C6 {1 O
- *函数名:Data_Turn
4 |/ b3 k/ L6 `* b! d - *输 入:无
- S/ M4 F6 l |3 R- I - *说 明:串口接收完成,返回串口查看接收情况1 }+ [5 Y7 ]6 y
- *返回值:无
$ O8 _) O/ j# i0 B$ I - **/
/ u3 s4 e O5 t7 l2 M- ]% G - void Data_Turn(void)
3 X$ K( C) J. Q" e - {/ D5 |% D! j; m. r/ b
- & j; s2 Z$ L+ H. q+ e
- if(recv_end_flag ==1) " w7 M+ `+ P8 G- C% G' U) t
- {9 U1 S: |6 O9 Q" z. L$ m- p1 S
- printf("rx_len=%d\r\n",rx_len); //打印接收长度 s1 L, z/ Z9 N r8 }' O7 W2 R
- HAL_UART_Transmit(&huart2,rx_buffer, rx_len,200); //接收数据打印出来
7 ~. R% {/ N0 s2 ], C- \' Y1 ? - for(uint8_t i=0;i<rx_len;i++)
: y8 M+ _. Y% f" W - {
. m5 ], A$ o& u, I* f0 L9 ] - rx_buffer[i]=0; //清接收缓存
( U0 H. w Q9 d- n- t - }" M( [) r, t/ X6 l- W
- rx_len=0; //清除计数
4 N* |7 I: n' a2 S/ w! P9 \1 n - recv_end_flag=0; //清除接收结束标志位& Q/ h R l$ ~) J0 s- O
- }' A8 X( I7 E. b- t8 G" o
- }
复制代码
/ w( x* x( P4 u 6.再主函数里的的while循环里再次打开DMA中断接收 - HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //重新打开DMA接收
复制代码
, c* G, y& u* n运行效果如下图,我的代码是接收到Do-0:1字符串,判断字符串,返回我需要的字符串,效果正确。
" m$ M1 r; G" S3 T3 P补充一点最近新发现的关于串口中断接收的问题: 串口中断接收如果使用HAL库的中断接收函数,接收到的数据量远小于设定要接收的数据量,串口一直处于Busy状态,会出现接收死循环的情况,接收的数据跟设定不符会出错 注意: 测试过程中发现,用接收串口助手的所有数据都没问题,不过接收模块的不定长数据时,如果这个数据之间包含回车换行符会接收不全,例如:AT\r\nOK\r\n,这个就只能接收到AT\r,具体什么原因造成的还没找出原因,有知道的可以告诉我一下。 : M! o6 [8 o8 s0 A/ r( ?' n' i! O
|