|
前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考!
, `1 c+ N r `; F0 r- H! g7 D: m一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序
# P6 [) m7 U, p2 C" A& K6 \; y. ` - {* B) d* x& M, H, U- z6 b
- u8 clear=clear; //消除编译器没有用到的提醒3 v! F3 e+ O; N8 R; l1 [
- 1 S( U6 m. X' n8 N& g- Z
- //USART_ClearFlag(USART2,USART_FLAG_TC);
/ Z3 F0 v& P' }8 e0 q, R - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) 2 i0 g% T: T, H8 N- v' }
- {
3 B5 z" ~$ h# h D6 m- E& S; X - USART_RX_BUF[RX_Data++] = USART2->DR;
% E; Z% t" Z, x# I - }
D! R% I% ^) u; g% T - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) $ p2 y# e2 U1 i; N5 h
- {
4 ^3 j, k. d" |' S/ B4 _/ ? - clear=USART2->SR; //读SR寄存器 可以清空寄存器
' u$ y' k5 ?' q% _2 U; \ - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断) / m4 X/ K/ J g- J7 h, f
- ReceiveState=1; //标记接收到了一帧数据
/ X8 ^% R1 }6 u$ o! F - }/ I8 e- Y* V+ o2 B
- }
复制代码 + Y+ X+ H5 @( P8 r/ a
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
) _- D- u% W. H+ F* U; s5 R - {
0 [& ^! v5 }7 p/ J L1 D - // uint8 i=0;
3 d" g$ b, s) ~; n. m - uint8 u8CRC = 0;
+ F* c6 s1 w' f4 t - uint8 u8Count = 0;
; H' m; c" u' p; F+ J5 A - uint8 TmpVal = 0;
) |: ~! s8 v/ R/ R; S( [! @ - uint8 DATA_LEN = 0;
0 T) y9 r) H* k( h - uint8 n = 0;
3 _4 y( f9 d# Y, D% s - uint8 HEADER_BYTES[4];4 f4 b$ r/ H" f
- uint8 DatBuf[30] = {0};
+ ~ m, L. y- B. A - u8state = GET_SYNC_STATE;
7 O1 v" S* g! U) V' z - if (Read_pt != Enocean_Data){" R% @8 Y7 B2 j0 O, a9 ~4 i
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等
& X6 V6 `: m3 ~2 T/ `( y - while (Read_pt != Enocean_Data)
8 r# x3 x* B7 _3 R8 F1 d' C - {
1 g; Y O+ B8 k5 q - TmpVal = USART_Enocean_BUF[Read_pt++];8 f% Z4 S$ J8 w, o5 n6 |' Z
- if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代+ X$ |% a2 _& v. z
- Read_pt = 0;
3 f/ b5 J0 L) i; Q1 m l - switch(u8state)6 X7 \" K. \4 [, p( [
- ...
复制代码
$ {) _; x- Z! |+ g# n3 g% f自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){, Y4 t3 j$ t4 q) T3 n M- O
- HAL_Delay(7); \2 b5 D' C8 a7 q) \
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
' `5 R8 d2 r- ?! M9 k- G2 M4 h7 g - if(Enocean_Data > 10){
$ r" L$ m g7 s - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
& \. T- k: f' ^! q) b/ p% Z+ c - }
' W# r# l+ L' ` - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
6 D D% ^, i/ c - Enocean_Data=0;
! m& @+ Q* h$ Y- v - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用7 x( I0 I* f1 e7 l
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
/ m& A6 k( n$ F1 u - }
2 s- N( {$ V! p2 s. P% s. b - }
复制代码
$ @0 y. |* {+ h0 `1 T在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 " c1 q2 I4 Y2 b6 o$ _/ v: _
现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);0 k0 J) d- V% o
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 2 M& J# ?# q, f% Z! H( q/ Z* d% \
在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
3 i o& d& m' P. P. \/ w - {; j( m7 a5 n& d* Y! P' {
- /* USER CODE BEGIN LPUART1_IRQn 0 */6 l) _; x/ ]8 C% Q
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
6 ?. P+ _2 ~. H: Z. p; J" T+ m - {
& P k C$ T6 l& U _2 \ h' R - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
% W8 J/ e9 N5 C" B! O9 c - ReceiveState = 1;
( X( Z7 @' P2 t' K1 v: v1 F8 k* r1 H4 x2 i - }
0 v3 J6 J$ o2 G6 a% Y1 e- y - /* USER CODE END LPUART1_IRQn 0 */
' n# f0 m5 {- z - HAL_UART_IRQHandler(&hlpuart1);8 `) H) R1 a+ v" R' O
- /* USER CODE BEGIN LPUART1_IRQn 1 */2 C H; J6 A8 i
" n* O9 O* R! Y4 |9 ^' y0 t- /* USER CODE END LPUART1_IRQn 1 */
% S! d7 g/ C! t: a - }
复制代码
3 g W% D* n' C. A2 ^$ t; }用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
) c8 e0 ?2 T4 d) o - ReceiveState = 0; ! W& @* C/ @3 c& W$ o+ O+ q. R* A+ n
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 4 U7 x; G5 z& A
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 O, k0 T2 f8 i, ?+ k) n- e: [
- Enocean_Data=0;) L5 B+ A2 o8 [ o" F. I F
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
1 m! p- n& ^' H7 a - }
复制代码 5 V; f* |9 d4 W
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。
0 a4 Y% n+ f9 G
/ J) T: C" i2 a4 D+ R: k6 O0 M
0 V, g% e% Z) T
实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){" d- T2 l0 X8 M; G2 F) e1 c3 c
- HAL_Delay(7); //测试,重要- Q: |0 R- b! b
- ReceiveState = 0;
2 T: U" G, }9 ^3 c0 T% V - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 & A' Z$ b/ m( K! ?+ r, s
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
! S% ]. U6 `3 t# T3 P+ Z+ b* W" W! j+ @ - Enocean_Data=0;* ]. z ]* w/ O, ]4 [3 Q. c
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错 e( C. B7 h+ U% V
- }
复制代码
* b! I; {6 B4 j) N发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: 7 y/ E5 W [7 s. ^5 k
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
% B. D, L/ Y% K这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; ; Z* R6 d3 B7 o8 d2 H1 c
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void) L, v; r) N: X
- {) a4 w4 R: l, g& v. T9 M' ?( |
- /* USER CODE BEGIN LPUART1_IRQn 0 */ x* p8 j( K; _. J: K/ ~# x. M4 X
- " i5 j$ T+ l+ }0 {: \1 t
- /* USER CODE END LPUART1_IRQn 0 */" d2 g8 Y- @% h( ~4 H. L
- HAL_UART_IRQHandler(&hlpuart1);) ~$ k: I% n2 i5 ]- q8 Y
- /* USER CODE BEGIN LPUART1_IRQn 1 */8 }3 p# X8 u2 L
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))# X) s: @4 O# H
- {
* h K8 n1 |# w$ p) ~* v. [ { - usartreceive_IDLE(&hlpuart1);. D7 t1 j% l. y) Q# h' s
- }" k& b) ?5 T: A! |
- /* USER CODE END LPUART1_IRQn 1 */" z h- n9 y" ]$ ?6 H- z6 C7 D
- }
! A6 G/ _7 }; B- I! v3 O - * ]2 U5 p, t: N7 U( o
- /* USER CODE BEGIN 1 */8 p' v) L8 }! a% A$ m8 T# J1 T' ^
- void usartreceive_IDLE(UART_HandleTypeDef *huart)' ?$ h2 ]5 q& @5 m: v4 L5 S' t
- {4 R9 u" M& w: B/ Y4 j) [
- __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断
' d$ K2 ?) g# ?# r) e6 \ - // HAL_UART_AbortReceive_IT(huart); //终止接收3 c' x0 I0 @$ I& G1 g# }
- ReceiveState = 1;
5 g) Z% M# x) D: k% l+ q - }
复制代码 ; t7 H, e* }' U, ~7 p3 q% X. h
用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);5 y! g; k8 L' H0 L
- printf("send command !\r\n");- `# j9 X6 ]5 b" t9 K8 F% b2 w
- COMMAND_GetmoduleID();. W9 J. Q3 V! r2 }3 ~1 {' N
- // HAL_Delay(10); //7ms不够
) F# K( D I4 B - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文( o: e7 B X; [' n2 F" k
- // while ((Enocean_Data - Read_pt) < ID_blueNum){1 _6 }! X- C- p) ]
- // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);( p4 [5 {) d3 X: p D" J5 ?
- // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西% @; X% l7 Q! r
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 3 ^ Y# P: l6 R, g
0 ^- d6 Y) W2 r- @, N% O
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
4 A" c/ \! _! Q8 M - printf("send command !\r\n");* x$ Z+ D$ s: R/ T- L4 k+ f5 {
- COMMAND_GetmoduleID();4 \3 b% y% A: V; s
- HAL_Delay(10);//7ms不够
# S Q+ z4 s6 {2 N - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
* Z. g5 A/ y( Y! H3 U- y) g% Y0 v- O - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
# n& k9 T+ J7 K3 T; H7 f* H% p
) ~" i0 S+ d# a3 o# P9 B1 a
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);; R+ i( E4 q0 j" e: E2 O( l; Y
- printf("send command !\r\n");
$ r% V4 f) E# G( J- S9 v& P' r- t - COMMAND_GetmoduleID(); M& f% O( x: ?$ h) v1 c8 I; \
- while ((Enocean_Data - Read_pt) < ID_blueNum){
e+ l$ D3 e5 c5 l - if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
/ K# {( _9 b3 S9 Y) i9 G - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
8 J" S- e2 [/ ^% E# {' H2 m - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
. ]2 a" F9 h. R q6 U
! }$ n9 D' ?9 n8 s0 D9 Q
测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) ! N5 \7 U1 G/ c _7 p5 A
4 j2 r) x* [0 p5 ^) F
$ G4 s& N. r. }: t( C' ~
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
6 h+ M1 I q- j- S' f. v; N+ ]' M' j - printf("send command !\r\n");5 r; D, g k2 u" ?# }/ b
- COMMAND_GetmoduleID();
d% Q) v% o5 A# L# z - while ((Enocean_Data - Read_pt) < ID_blueNum){
/ z, ]% o$ H# ]( f - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");# Z, Y' q% I7 z; Q0 i
- //HAL_Delay(1);//延时可以,1ms就可以
$ Q0 K% t ~3 X( s7 r - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西2 ?" j: o% l. X" A
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
; I' Q0 `( ~8 D* R# n6 e* D
$ L( P# O4 k' Q' B0 t$ r- Q0 o+ v* v0 y" }
# S* [* @1 k+ B$ x7 o' @9 e5 l2 [
' H$ p. s' u" I: p3 E1 \
/ c/ a5 O/ J1 x- ~- J经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
) @' k- N9 Q! ~ U6 o1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小/ U4 ~* l- E: ^9 x# ]* w
- #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
3 T+ ?# s2 O' a4 m! e - typedef struct9 N l8 {/ f' e2 K# [
- { / Q. Q, k- ?4 K+ P
- uint8_t UsartRecFlag; //接收数据Flag
$ b5 [% ^ x% q" v' d( Y; F/ d - uint16_t UsartDMARecLen; //DMA接收到数据的长度
" E" {# Z2 ]4 c - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
& q: B. O& @1 y; |* m6 C - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
! S% B: q3 | C2 d9 e1 l6 r7 F - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer6 h U. ^& h% m+ d) ^2 Z) ?3 I
- } Usart3DMAbufstr;
8 K( }' ?7 C' R. k6 { - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)$ y" R' o" r7 }( L' G' O% y( ^& y; F
- {
5 O9 b; d R% s' n z - /* USER CODE BEGIN USART3_IRQn 0 */. u5 V& g6 M/ w
# f. W6 Q" v4 i, L }- /* USER CODE END USART3_IRQn 0 */
% x2 `7 C3 _8 I - HAL_UART_IRQHandler(&huart3);
4 X4 B. V1 G( s6 } - /* USER CODE BEGIN USART3_IRQn 1 */
! d% b9 x& ?, P9 {+ ~ - if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断
& }2 @. V0 c" w$ `3 G+ ? - { / R( }$ c) K9 O; h1 f7 l7 s
- u16 res = 0; * K; a- A+ Z6 V; ]7 S; f
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
! M: u$ Y. d9 L' k+ I; R3 ] - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断
- l- M8 ]# j: q4 o9 ~; e* E I - res = huart3.Instance->ISR;
6 M7 s5 h: c) u - res = huart3.Instance->RDR; ( R: k, ~3 _( |7 Y5 g( d
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间
) C3 d$ D. h9 d* n& `/ D( x% x6 `% F - Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 3 j2 H; Y/ C6 k
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用5 T3 N$ H6 n2 ]8 W7 P5 v, Y# Z
- }
6 S r& T: d7 @1 m* r" X. e; P* F, ^ - /* USER CODE END USART3_IRQn 1 */
6 J6 @6 K1 O- X6 q4 p3 q, P+ j - }
复制代码 3 P" g( |5 K! I4 M
数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
# v7 J/ u6 c3 K F - { 0 r, v9 R- `- ^; t3 h
- //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线
9 k6 j2 l0 N" N/ `% ] - if(huart->Instance == USART3)
0 K* w2 _) V% ~ - {' h Q1 z" t4 G
- // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取
+ C W* Q9 ~% B, ?" ~3 ^4 d - // {
' \& Z) X5 {+ t( l! _ - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延+ @/ |' |$ s0 d3 l) l
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数
8 O h, e; ?- x: g5 L* y2 F8 J3 g - // }
. `3 g, o/ s, P6 V- E - // else1 A" f8 e+ k6 `. w- X3 c) T9 K
- // {0 Q) t- Y x' k4 @
- memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头; J( M& K! T8 r z/ H, C
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度 . J0 `: {8 z( i$ X: [% O# l
- // }
8 I, Z5 R" }3 l - 5 f, U7 X8 c7 b( i2 g7 y, s
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收- s, A4 _4 U% D
- Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
1 n2 P& Z0 v8 ^5 `- U4 ~$ i2 i& v - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收# O% A) G5 t8 Y. s6 `% v
- }
8 t9 a0 b; E' n" T- G - }
复制代码 4 q0 Q+ _( \6 |8 U5 U' y
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
- ]( z' }" L+ U, w& g - {' ?/ Z5 T6 M5 Q7 R3 s; { O
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
q. b, z# x# Z$ ?* k8 }: Y0 H - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除8 [- u9 v9 e. b
- Usart3type.UsartRecFlag = 0;//标志位清09 j, k8 J3 J9 [' K* h
- Usart3type.UsartRecLen = 0;//已有数据长度清0: x, D* t! j; `. G1 J
- }
复制代码
" [7 E7 u$ i# G- |1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
4 y4 a# o: D& U4 T3 Z二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图:
" _9 }& {- \$ p$ l1 u+ J
" i1 s( U: `2 T I& [: Z
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
: e/ `8 O- u' ?" t) ?) Q- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志
4 f5 B7 T4 Z, h) F' B3 C - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);
7 q( q2 ^9 U( J* q* R - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);( @+ J( }! h* c* x# M
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码
G2 w" u5 ~" Y2 ]比如,在我清除串口接收缓存的函数中,我加上了这几句代码: " m" x: A4 _* H# ]
% M$ W% O* J+ j O! A v
: m7 l9 j P# c
8 I. p$ v& p3 a% B6 I- i这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。
& n& a: t9 f/ n m: q2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断:
0 e7 m! O% y$ E
2 C/ V, q6 B" n8 X
" g+ Y& [ P, Z O2 N
在这个函数中有这么一段:
5 n! n$ c" Q0 h3 {. [8 \6 b/ t. q9 g
7 a' _; E- M+ ?" F2 q: B# h# a
& u' B9 _7 Q0 ]里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: : |- s; }' f4 J" `+ X
?1 B1 c- e- a( |3 n& @" A- @
判断的语句如下: - if(huart->Instance == LPUART1){' R* a# W7 A4 m: w
- Enocean_Data++;
( f4 F; p" p& s3 V- U - if(Enocean_Data > 98)Enocean_Data = 0;# `# r) E8 S* X% F* s
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){! c: C" W1 F* P+ K$ a' `1 H
- hlpuart1.RxState = HAL_UART_STATE_READY;* W ^3 R6 a( [0 D
- __HAL_UNLOCK(&hlpuart1);
* ~2 u9 p5 K* E' w- R0 S$ k - }
; ]# n1 E* N% N3 I! p- b$ V8 ~ - }
复制代码
" `2 Z4 b6 i9 y$ {4 K/ j, Z如有侵权请联系删除& a6 P; ~7 h# G2 }4 a
转载自: 矜辰所致 % c# N& {4 \3 A1 A0 ?1 Z: O* c# m R
|