前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考!
g1 L; x) Z+ @" B3 ]' a, ^一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序
+ \0 G1 k! K. |8 L - {
+ [6 D4 u- }( c" f& g( E4 n - u8 clear=clear; //消除编译器没有用到的提醒" ~: c6 _3 \1 u; o% |
9 d6 c* `; q7 _4 S7 n2 ?' w0 c- //USART_ClearFlag(USART2,USART_FLAG_TC);; C: r R5 Q s' W
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) : J" R: C. ]' u9 O
- {
9 Y3 u; t; ` D/ i: R - USART_RX_BUF[RX_Data++] = USART2->DR;
9 p1 u8 d; ~( b1 R- t3 x; ~ - }4 N3 q0 b" p% P7 c
- else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
% t; U5 d0 N; K0 ~0 ]5 W/ T3 u# C - {, Z$ g1 B8 M6 `5 ^0 Q4 z
- clear=USART2->SR; //读SR寄存器 可以清空寄存器
& g( u, u& g: o2 I( ` - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
9 F0 x1 [+ d) {. [# g% C - ReceiveState=1; //标记接收到了一帧数据
0 k, |! o! t- }1 s. `4 k1 P5 V - }
6 |: b. A: y# K - }
复制代码
$ g) C/ c9 j+ C4 N) \不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
, r% d3 U3 ]1 v: q: L2 \ - {5 h7 s7 u1 I3 k6 u8 G4 s
- // uint8 i=0;4 U" D% x$ {- O& E; ~
- uint8 u8CRC = 0;# E% z' ]9 A0 [
- uint8 u8Count = 0; : A# E! ?4 X. M' q
- uint8 TmpVal = 0;
$ R6 C, g* m# {: j - uint8 DATA_LEN = 0;- Z& M' }4 v" @; ?
- uint8 n = 0;. _ v& W% v, p9 c
- uint8 HEADER_BYTES[4];
0 y# h/ X# A7 e) ]3 O% D% B: q - uint8 DatBuf[30] = {0};4 g( U0 y7 j6 \, @, x
- u8state = GET_SYNC_STATE;; M2 A/ Z) k) \2 w3 F5 N% t
- if (Read_pt != Enocean_Data){5 Z) x% j* J! v& T( d, [
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等
9 _, H2 P0 x1 s9 Y- n - while (Read_pt != Enocean_Data)2 |. r- x. b" ?) p+ H1 h& y
- {1 J' ^+ {. i% u1 [0 a5 F; J
- TmpVal = USART_Enocean_BUF[Read_pt++];! u9 p& J# l0 o" l! C' s- K
- if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
. g6 d. b7 Q0 u0 b7 X - Read_pt = 0;1 A* _6 y P* Y; W- o4 A
- switch(u8state)# m& t$ {5 L1 v1 t
- ...
复制代码
2 w: p4 w, h# T1 s" D自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){' X1 h# t2 X' X- h4 f
- HAL_Delay(7);% o+ i4 T% O' a( g3 T, \, \
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
% G: U b# K6 ?0 ~ - if(Enocean_Data > 10){
; e3 a V, S7 {1 t* w; o - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
8 p W$ G% W. ^ - } 6 g, ?& r! u U/ v
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
V1 a2 z0 m- k- E; P4 I G - Enocean_Data=0;* L% `( Z7 N! Y5 R b3 ^/ |, f0 ]6 o
- // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用
# |, i. P# c) w, W: V1 [ - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
( F7 w& I! Q: N8 D - }" E( b: x) h/ [8 r6 a) b- o! M
- }
复制代码
! |$ C+ e! d1 E; N8 C4 [( l& L在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
/ o' |& _5 U* h- [现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);2 _3 Y9 A5 R$ p
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
, ` K' `' \% s在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)! V. [$ Q( m6 N; ]- _# T4 f
- {
6 V3 Q4 [! B9 \ - /* USER CODE BEGIN LPUART1_IRQn 0 */
9 A# P! B. G* X) m# S$ y - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
D% K2 y" [$ y" c/ e - {* ?- q A6 z6 _0 D0 ~( \6 c4 u
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
& E" r8 v: `1 p4 H - ReceiveState = 1;
: v9 B+ g- V% \6 ^. h- G - }- [$ M0 T5 i. q
- /* USER CODE END LPUART1_IRQn 0 */ s; G1 h* e9 v. H) {! C& h B
- HAL_UART_IRQHandler(&hlpuart1);
7 `) q" l$ Y) J2 L5 B( U - /* USER CODE BEGIN LPUART1_IRQn 1 */5 T9 y, c/ w6 K, W& v" r2 c& I: x. T
- ' c$ j5 x; _/ b$ h
- /* USER CODE END LPUART1_IRQn 1 */
8 ]' L; O7 b7 w w - }
复制代码
4 p+ e h( U: K9 N: W% @. x; Z" L用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
% C( D& e( X8 J - ReceiveState = 0; 7 i5 x/ y4 r8 R0 L6 G
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 8 Z* d1 ?' n7 _6 y( t
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 8 j! U0 K" d' Z' K* O' Z; H
- Enocean_Data=0;
! Z( \! A: E0 h& ]% ]3 g! M - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
1 V: T; b$ b) B( Q, p; m, w - }
复制代码 7 A" a) i5 p4 M) X. T2 G
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 x. E0 s% T r2 u2 ^6 \6 A& w
W9 K1 ?% h1 j" |7 c) t7 R; a! Z
* Z0 {% e9 D: V0 x
实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){2 F F. I; a5 e2 W
- HAL_Delay(7); //测试,重要- U; d6 O: w& h; x4 i
- ReceiveState = 0;
7 \2 W5 a& T: H- m6 }( u7 S - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 4 F! J! t& Y- Y6 ]" j
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
" C x1 d! A+ c' Y0 c& ?( t - Enocean_Data=0;
- C# _7 y: x, |4 O& ?" \ - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错& C5 r* p2 B/ N# B* a
- }
复制代码
5 X; ^8 E5 F% }2 U- f% `发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: ) @& O+ z9 u9 a L) v
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码 2 O1 x7 l5 U3 O- R; L5 w" G0 E
这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯;
' W; r1 n( V Z4 ?5 d2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)1 o/ i4 N, b6 \& L
- {+ }5 J1 e& k$ Y9 m2 }% Q C
- /* USER CODE BEGIN LPUART1_IRQn 0 */; f G( W, @7 [9 t; a
-
8 X- h ~& u& w; \+ C4 G - /* USER CODE END LPUART1_IRQn 0 */2 R0 M2 Y6 u1 U' j0 d; s$ n
- HAL_UART_IRQHandler(&hlpuart1);7 g) I$ |, e# k' I# o' L
- /* USER CODE BEGIN LPUART1_IRQn 1 */
* d0 ]% m) K0 a# J5 j - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
0 v. N# Q( i" D: t0 _ - {
- W& v# \8 Y2 U6 h$ U7 [ - usartreceive_IDLE(&hlpuart1);
/ ]% F0 _6 {) u% x4 N - }; A9 d0 O' l* i S, U
- /* USER CODE END LPUART1_IRQn 1 */& u" |: G. e3 Z4 l# ^, S/ n
- }4 R0 ], d9 U& p: X9 V! E
- # t* `& v% S7 U! B4 _3 G
- /* USER CODE BEGIN 1 */ y7 Y0 \% a: P
- void usartreceive_IDLE(UART_HandleTypeDef *huart)3 {3 j5 {6 Y* _* F, h# @
- {
# t [5 \/ D$ n7 X/ {+ w; i y7 x) | - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断# [$ {3 \7 T( b E: {
- // HAL_UART_AbortReceive_IT(huart); //终止接收6 o3 ` U, o' H, m
- ReceiveState = 1; H; M# g1 {- j* w- s
- }
复制代码
_0 A/ V) M2 x$ G- u4 t# w" U用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 HAL_UART_AbortReceive_IT(huart); //终止接收 ,因为IT中断可以继续接收,如果终止了,数据处理不够及时,下一帧数据就收不到了。 3、最后通过判断标志位 ReceiveState 来处理数据,一般情况下直接处理,特除情况,在标志位至1 后,刻意的等待几个毫秒,等待数据接收完全再处理。 !!!最后移植又发现一个问题代码中 #define ID_blueNum 44 - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);& |* V/ b; d; v" O' q7 w* b
- printf("send command !\r\n");
9 Z8 y8 ?6 ?) ]. h0 z. t3 P8 q* m - COMMAND_GetmoduleID();& |+ d" v- u' }( Y+ X
- // HAL_Delay(10); //7ms不够
0 b, a' e! T+ w! ~1 d- E; q% o - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文3 u' p6 t+ T* J4 _* ^; K% J/ r9 _1 T
- // while ((Enocean_Data - Read_pt) < ID_blueNum){9 L0 N% g b- G
- // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);* r. a1 _7 u4 N, f$ N
- // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西% { A0 [2 e1 ?7 q( t5 i
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
9 r" S8 p- U+ Z6 [
1 a8 g# {6 Z5 q( v0 H如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);% Z% x3 U, t3 _" W5 a
- printf("send command !\r\n");
1 \+ g8 D0 b7 _; Z' k! Z* t2 P - COMMAND_GetmoduleID();
; K7 g- G0 s* h( N3 e - HAL_Delay(10);//7ms不够
7 L9 d9 J2 x+ L2 Z; n$ B6 g - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文5 c6 O: K- I: ~; m' m6 g" s, [9 _
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
- G0 ^) V; w" `' h1 F
& }3 \: H( p' |6 x
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
8 f4 R) B) K5 B5 R2 z/ e* f - printf("send command !\r\n");5 B( g3 [. @; P3 R
- COMMAND_GetmoduleID();) l' V' w9 {# `: g. c
- while ((Enocean_Data - Read_pt) < ID_blueNum){! u5 q4 V7 |. M3 \. ?$ i
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");) i% ?; ]. N1 b. s- O. N% ^2 T$ M
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西8 g% {7 B' t7 e3 m3 r* W8 }
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
! Z! L1 [, c0 l; \/ G
l8 Y' Q& L% c# ~
测试代码3 , 4(不加条件随便在等待中打印 , 延时 )
- z W4 M: r8 T- O# J7 G. D, a/ V0 ^4 H% r' E. j# H
8 O/ Q; X; J) p; Z f! S4 k& L- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);$ K7 {9 d3 Q9 C
- printf("send command !\r\n");" w- \9 t; K/ P! h0 K
- COMMAND_GetmoduleID();1 T& ?+ e/ y) S' j7 k0 g9 ~
- while ((Enocean_Data - Read_pt) < ID_blueNum){
/ u/ `1 q9 a7 P/ M4 k) n" ~ - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");, A/ q# G% c, ~5 e, e$ `
- //HAL_Delay(1);//延时可以,1ms就可以
6 v2 G+ A3 E) p) ~# c# R$ X - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
# [1 q$ J' m' } - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
4 _2 e3 ^" i0 K. x0 g; X- W$ m3 m6 u3 N: h+ @
% f) A+ d/ y2 }4 `1 s
# o: I, |0 C: h& x
, f( S+ x3 \0 O; j }/ v
* m5 i# @8 `) \6 v4 ^3 s/ L经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
c! u5 I {' Q0 {9 L& M1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
- c+ ?8 y9 S- u& O9 }8 R) I$ q - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
: Y# b1 X/ i C1 M3 I2 E - typedef struct( S$ I7 @) S3 M* ^, m9 g7 {
- {
. E W% s- k& p8 ^4 C* B+ E - uint8_t UsartRecFlag; //接收数据Flag; n3 D, L& f8 ^# W
- uint16_t UsartDMARecLen; //DMA接收到数据的长度
$ \' `/ i1 B- K. w! ~' ~) p( H; U4 T - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
+ \- D) P" D+ r6 e) w - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma+ H3 c3 o1 c1 r3 m0 R+ d
- uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer+ l, U7 l2 Q9 ?# V6 N; R+ Q2 D
- } Usart3DMAbufstr;; d) P/ L" s y( Q
- extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void), w1 w" h- f! m/ q8 ]6 r" h( x
- {
6 P8 J9 Y* q) a { - /* USER CODE BEGIN USART3_IRQn 0 */- B; M7 P# _$ s4 x
- 1 a" \: A0 r7 e; J; P" ^6 P Y+ g
- /* USER CODE END USART3_IRQn 0 */
2 `4 {, R9 M2 _; R6 p1 P - HAL_UART_IRQHandler(&huart3);
6 F6 m- i, W3 l x6 O' _, \& ]$ b Z - /* USER CODE BEGIN USART3_IRQn 1 */8 m) N5 w# J# {5 I- H
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断1 ]. i9 i5 x+ w( o' T9 [0 v
- {
, t$ k' X9 Q& i3 Z$ B5 [( M* d - u16 res = 0; , f1 O1 ~0 s: f( O
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
# j2 C! d$ T6 @& y - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断8 _6 c8 j2 Y! X! o" L* P
- res = huart3.Instance->ISR; & T" O0 V6 q( r- H( p3 ?( E
- res = huart3.Instance->RDR; 3 t5 K* z# s3 Z4 j+ R. g
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间
, I: c/ z; A1 n - Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 / ?' \1 h4 {. @: b1 O8 o
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
6 X" B" s( D e, J8 z - }$ \! h! J& H) q+ f8 m" ~
- /* USER CODE END USART3_IRQn 1 */1 u5 \8 }: ]. p, U x
- }
复制代码 7 k0 u2 O- E! z% Y( [2 c, \0 Y
数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
9 K8 {$ i5 l: l* J! U - { / l( X. J2 H# a+ _
- //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线
( m* o1 k* m9 r# r! I* n: ?( B1 q" s - if(huart->Instance == USART3)
: U6 v% h8 F J6 G$ G9 X- B( j5 N - {$ E5 q, t9 V3 F, J& Q5 g2 w; n6 u
- // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取
. @- s. k6 I N+ V+ p' X - // {2 W# C' I$ G8 D& R* q
- // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延% w, N9 K' B2 k
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数
5 P) r- k1 u3 k0 `7 ?' \% G, E7 w) A - // }
! Y( W+ q4 \0 `! X) v8 m6 V B0 e" E - // else' [; A# v/ ~& }3 P- V9 m- Q
- // {; O) S5 F! D) ?" K
- memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头& K' o" g: M/ G8 w3 L! r
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
2 [2 Y' G! [0 S7 ~: A8 V - // }* T t% u/ |* r( | }
- . h! X1 J& B l. ]- X( k# o
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收3 K5 r% k% L! b1 u
- Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据6 R: Z: ]% J& b( L
- HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收
" ~) t2 ] M2 E/ ~ - } X& J- [/ o' I: k# Z3 c) J! w
- }
复制代码
, o' F% L' t: t$ z1 p, u在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据! l3 f5 e7 a, U- T. |6 [2 Q
- {5 @) X3 a d7 Z W. j# c
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
; a% r1 I( g' V6 q1 H - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
1 [3 Q1 g% k! g/ a1 v - Usart3type.UsartRecFlag = 0;//标志位清0
9 J* g9 w/ m3 V- u$ f7 Q; Q! d - Usart3type.UsartRecLen = 0;//已有数据长度清01 q) h5 i! k" e- l/ Z1 L
- }
复制代码
0 e; ^8 T# _( z/ Y1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
E! ^3 ~& }4 N二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: - ?$ [3 Z# U4 q# _2 h
9 P0 K0 D/ U+ g/ t2 a7 r. v" x出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志: ( D a1 f* u, W1 R3 d3 Z# j5 _
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志
: w2 T z- ~& T3 Q5 r6 G& M6 A( Q - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);7 P* I- t& _- ^% n
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);# @; G" k' z. F7 @
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 5 z" Y: {: e# L+ L/ K- g* t
比如,在我清除串口接收缓存的函数中,我加上了这几句代码: : ^$ K/ S% u7 |5 e, W; }% u
" L3 \! n: K" l! o0 I h
3 a8 G; L% k- X2 ^
. @& v4 X8 t s$ f4 J这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 # t+ A, s1 e1 \" h9 o
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: 4 y) _6 t; g+ T: d5 _* ^0 J. V
: x$ |2 [6 k/ M9 R& W9 H- D* t* a9 C
在这个函数中有这么一段: % Y; m7 F$ ^4 i4 O& v8 M8 B
! D7 F* l$ F9 Q+ G# S# U. o- D' Q6 k
/ q3 d: h" {% e
- J6 i) p5 f" c8 S- \( Y里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: 9 u) C3 `$ f9 B+ }3 r+ d7 s
! w% d; J5 s; F" @/ F& a判断的语句如下: - if(huart->Instance == LPUART1){) h' r' n# b/ g
- Enocean_Data++;
- q0 R4 @) X' w) g$ l B - if(Enocean_Data > 98)Enocean_Data = 0;+ O4 d& f2 D# f- N
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){* k2 Y. P6 X* [ M
- hlpuart1.RxState = HAL_UART_STATE_READY;
5 h$ e, x$ L, M2 _, V' { - __HAL_UNLOCK(&hlpuart1);
" }, a# g5 Y! E - }
# R+ Z2 [/ X2 D$ }6 z - }
复制代码 9 B) e' x' Y* ]2 A
如有侵权请联系删除& n3 L# S* [; V' \, D: C
转载自: 矜辰所致
1 A" g; Y* w1 g) K0 f) {5 D |