前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! I5 J, b9 T' M9 t5 ?' Q( g8 I" U
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序
4 e8 \' L) }$ u' j! _% x6 ^" v* G - {% u" R: `4 J0 C$ B& t' v; `+ n( l- W
- u8 clear=clear; //消除编译器没有用到的提醒* F2 s1 y$ O' I' Y, A
- # F1 u2 `/ Q5 [) ?+ K0 u
- //USART_ClearFlag(USART2,USART_FLAG_TC);
' B" Z1 V" A6 u# D5 R! f4 I - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
7 L) g* J4 C; `0 G, g4 s6 B5 n - {
2 t% t6 y7 n3 f7 s4 a - USART_RX_BUF[RX_Data++] = USART2->DR;5 k8 Z1 u% V5 ?/ A1 w0 L
- }1 R7 R) S: g9 c3 i* ^5 f
- else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) " W4 B8 X" [) Q7 @7 f& V, x
- {) |: k* l6 s6 X$ l; [' e
- clear=USART2->SR; //读SR寄存器 可以清空寄存器$ e1 \1 X, l3 N8 t! L& V( S$ e
- clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断) 3 O, Z$ q% v% x8 i d0 u5 A
- ReceiveState=1; //标记接收到了一帧数据
$ V+ W, M6 i% `. L6 ?$ J( O$ M - }# ]" J0 _1 _( J$ t& }4 y
- }
复制代码
& p3 q( M: ~8 F, ^" [ o" H5 o# n不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)" Y- Y2 j- E9 i0 ]4 J
- {# C. O, |1 [# x+ h6 z+ H) F
- // uint8 i=0;2 a* P/ l2 i; R! v2 r# H' T/ c" A9 o
- uint8 u8CRC = 0;7 {4 h9 D, Q Z
- uint8 u8Count = 0;
0 S7 C5 `( K3 E - uint8 TmpVal = 0;
- K7 b; t, u) |6 k& {9 F0 J( U! Q - uint8 DATA_LEN = 0;
5 c4 `8 ]8 f( K- c7 o0 D7 ?. O; i& K% X - uint8 n = 0;
$ P5 y3 P+ [- q* ~ - uint8 HEADER_BYTES[4];
# T- b& k6 {: ~0 X0 f" S2 O - uint8 DatBuf[30] = {0};
. l" a! y, Y# Y - u8state = GET_SYNC_STATE;) R2 z. ^0 V" S
- if (Read_pt != Enocean_Data){
# R. r2 R3 |, y( K1 }' e) ] - delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等! T9 x" D! K$ T& r6 o
- while (Read_pt != Enocean_Data)
% ^8 o2 g: l- q' u - {9 r8 g& m( p. f$ v x* E; R: u8 W, t& a5 Z
- TmpVal = USART_Enocean_BUF[Read_pt++];
) Q5 C) Q6 j2 u! Z - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
8 ~! }; U6 Q4 e' V9 z; N2 N - Read_pt = 0;; K9 E5 k, [% k+ \; X/ a
- switch(u8state)" \# c* \3 Q1 w) `% U
- ...
复制代码 " N! G8 ~/ I/ n* n
自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){7 L# e5 u2 \) j4 ^ O
- HAL_Delay(7);2 C9 S! A( E+ G5 c
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);; N4 I" s1 V/ F; X) b
- if(Enocean_Data > 10){. W1 [$ W! _& v
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
% O N: E8 V. B. E- A$ L6 O - }
* M# L' T' P* H! _2 }0 q( R - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 & O; F! c4 K; A0 }5 w
- Enocean_Data=0;
4 B6 C* F- r& @3 E5 U - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用5 Q! ]9 ^4 c5 i+ S/ n
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错( B/ P, l& G) W2 C' L8 G
- }4 L! t4 [. F- e) s
- }
复制代码
& u2 p1 N* v: C |0 w2 @8 ^$ b; \在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 5 K4 f& X' ~) D- f# h2 b4 P- P2 @
现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
- h3 z3 J* X( T0 C# ]5 c. S2 Q% j - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
' ~5 k$ k: \. |4 c, l8 `* z在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
: f9 K5 T! v0 M( p - {
$ a+ K, {8 B& p' K" d3 h7 N; m5 X - /* USER CODE BEGIN LPUART1_IRQn 0 */
/ b* [7 u/ ^2 r+ \% N - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))# p8 {0 P8 D+ ~- j) w( s h
- {. u. e5 |. p# C
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
3 X+ V; c( ^0 R" J - ReceiveState = 1; " H7 Z# i4 q/ \" ], W
- }
; H+ A$ s% W' J7 o/ E( t% i: u- w - /* USER CODE END LPUART1_IRQn 0 */
, V$ p6 N7 {6 w% d% } - HAL_UART_IRQHandler(&hlpuart1);" r' s" k- |/ X3 o# B
- /* USER CODE BEGIN LPUART1_IRQn 1 */, a' `7 D6 l0 k+ w5 h
- " L: C Z; m* s6 ^8 A, \+ W
- /* USER CODE END LPUART1_IRQn 1 */8 @: N6 M+ R& k" A$ B
- }
复制代码
: K2 [+ s. k* _3 \( q4 F用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){! K2 K$ a+ C& {! L) [0 K; M' s! r% v
- ReceiveState = 0;
9 P! v/ I1 d1 a1 ^5 J( c9 S# C - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 B% p* h# O Y4 M
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
& r, T/ O- N8 E4 v( b/ N s( B - Enocean_Data=0;
X! q1 h' e: y$ q* N' G0 [ - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错6 A' g" P" [& v- W; X
- }
复制代码
* Q, P$ t. F* w! y; m5 H0 u3 B结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。
' L5 u% D2 n6 ~# s+ _9 n
K9 y4 ?3 B4 W5 ~
: }% L, m$ H, W3 \5 M1 e3 I( W实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
5 d M& r3 {7 G$ ]/ T - HAL_Delay(7); //测试,重要* k6 c! r1 ^/ I3 |8 Q4 e! }+ N$ W: o( a
- ReceiveState = 0; - C1 y- i J. a4 e; u
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 7 U" I/ X6 k" O
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 6 G' v6 O$ L) s! F! B: h' o
- Enocean_Data=0;
/ Z5 v% R8 V8 y& B1 n" o/ \4 S - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
) d, x$ ^: t0 k9 {$ F - }
复制代码
6 w" K9 ?# V1 j, a发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: 5 m2 i+ w, v$ m. b& ` o
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
4 {$ s" r* o5 r! H1 K( z这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; 8 u8 U: l" @9 e8 \
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)* X1 \" r% ~$ d: @3 Q( S
- {" z& r. v$ Y O- R' { Z, z
- /* USER CODE BEGIN LPUART1_IRQn 0 */
9 E4 o4 i) q' b0 S& }/ _2 S -
9 p9 |" d" B: v& r - /* USER CODE END LPUART1_IRQn 0 */
+ S- Q! d; e+ z" u: r" O - HAL_UART_IRQHandler(&hlpuart1);
! l6 `/ R; r1 R8 v2 H+ l& m - /* USER CODE BEGIN LPUART1_IRQn 1 */
3 e+ {. o. I1 U A; B' \ - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
% d0 j& [ {8 E - {+ J5 t2 f0 L) {- L
- usartreceive_IDLE(&hlpuart1);3 N, p' j+ L5 e4 L4 U6 }7 { x
- }
: ]& o$ K" ?" f5 p4 r& u9 t: J# ` - /* USER CODE END LPUART1_IRQn 1 */1 n( m" z! W2 v! K
- }, z0 E8 N3 _( [- l
- 4 F1 {7 i& G7 C0 P% h
- /* USER CODE BEGIN 1 */
+ Y& k8 M- k' M$ s% d0 u2 A0 S2 U - void usartreceive_IDLE(UART_HandleTypeDef *huart)
2 ~0 |) d D- I( n/ F6 V - {
8 [6 z, S. f q* y2 O, [ - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断; C- C' K* m [0 l
- // HAL_UART_AbortReceive_IT(huart); //终止接收
J6 d6 X/ g+ l8 s5 z: J - ReceiveState = 1;
4 s$ L' [0 L) c - }
复制代码
# g0 p! w* C* |* l8 D1 m用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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 ^4 i0 F2 a' y' e5 F6 i4 W
- printf("send command !\r\n");1 c5 ^- D; u6 k& h4 i0 ]
- COMMAND_GetmoduleID();
+ ?" K% T( \, r+ L - // HAL_Delay(10); //7ms不够
* V+ T# J: D9 M3 ^3 w; W - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文 B; v% I5 U6 h: a3 ?7 v
- // while ((Enocean_Data - Read_pt) < ID_blueNum){
j2 u# d8 |7 r& n U" r" M) o - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
- `* z& _7 D' ?6 ?: w* e" } - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
- v6 O0 W4 P. @' ?/ Y/ M6 ] - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ) V; ~$ W; a# y* m
8 ?% Q# M& ]4 f2 z' j, {
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
) E4 S% z+ s5 [4 A7 P( K: y) d - printf("send command !\r\n");) \! l8 E! L$ |; c* X
- COMMAND_GetmoduleID(); h4 { j9 C7 c3 v
- HAL_Delay(10);//7ms不够 ; g6 Z4 j: u# R; S) O
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
, V }$ }7 q2 X: V" y2 ]5 \; @ - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ( s, R# {8 I% q v: q! h& c
$ n: K' }7 e1 _' ] 测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
* T2 m5 L: w& [. r8 E - printf("send command !\r\n");
d4 e; j- G2 z5 q; x2 p9 |0 I6 @5 d/ T x - COMMAND_GetmoduleID();
# l) h* V' G7 P% {- ?8 w2 r/ Y - while ((Enocean_Data - Read_pt) < ID_blueNum){. x8 z Y a4 V6 Y7 t
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
+ f' ?/ ~, v5 Z) T, j& A: o, c - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
' z# K; o' E0 f - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
% O9 e* e# `" z
8 l5 l+ l% `1 I
测试代码3 , 4(不加条件随便在等待中打印 , 延时 )
* C/ e) q( g3 E, U
% S8 j& ]7 {& ]$ Z o/ S. @6 ~2 t
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
1 D0 F$ s! h+ C3 {5 @) j( o - printf("send command !\r\n");! \: Y9 T# D! [( g; x$ t1 ~
- COMMAND_GetmoduleID();
' t6 n0 v# o0 B+ Y - while ((Enocean_Data - Read_pt) < ID_blueNum){
W: t+ a3 M& G - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");
$ |( H: ?% D9 e5 {; _, g7 c0 i - //HAL_Delay(1);//延时可以,1ms就可以
7 Q' p0 i- y* n1 F* l2 R" | - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
; y# O }7 [4 r$ f6 A - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 $ a, @/ b1 D% u9 }, m
9 _ |) C) p; S, n- {) K# m @1 Q8 X( T# ], U6 v
' p/ S$ g h% z" L' E, w
. B4 U5 M0 U9 b. Y
; l7 a9 O: M' Y
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
7 b1 b8 b+ _) }. H. Y1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小# h; L- d6 U& f
- #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
; O y: k# Q9 }" z r# o- T! K - typedef struct
1 V' v6 w1 Q* T; y7 A1 p" S6 I - { _( l" f f) b; z7 H9 q8 V5 q/ {
- uint8_t UsartRecFlag; //接收数据Flag
' ^& Q; }% l& g0 T9 d% [8 Z; Z - uint16_t UsartDMARecLen; //DMA接收到数据的长度
+ t) V" M* D* Y/ K. N - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
, M* Q8 _! M% a8 [% n - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma, Z9 V$ A' f" F2 |
- uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
0 E) G$ U0 T* b - } Usart3DMAbufstr;
" ~4 D' {2 W% [. |, X! F - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)2 f# i9 O7 a* s
- {; h {( o( a$ v0 ^$ X# x6 F
- /* USER CODE BEGIN USART3_IRQn 0 */0 J0 ?4 O" R. ?3 v& }, U
- 8 q6 @" C$ w8 M) [
- /* USER CODE END USART3_IRQn 0 *// l+ C8 |& X: G" ?$ i
- HAL_UART_IRQHandler(&huart3);$ z! H& M% F8 h' w7 z: u
- /* USER CODE BEGIN USART3_IRQn 1 */, f" Z# f+ |1 m& {( e& C( e
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断4 ?9 R; z. A) _: Y3 V( @
- {
/ T" A" Q( f2 A' v9 e - u16 res = 0; ) p6 Z, C! E* A" ^
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
4 z! M: }6 Z+ F) B9 p# J - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断+ y u, p2 E7 o( n
- res = huart3.Instance->ISR; 7 c. w d- X' l
- res = huart3.Instance->RDR; / q/ u" W' L& K, H) v& d
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间! A/ j8 I7 ~: |# R s$ [/ i
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 : }( x" b) U/ N' ?4 J
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
) L0 R4 B( M0 n4 V# o - }, J# \2 r/ i8 \% g6 E4 g9 V/ m
- /* USER CODE END USART3_IRQn 1 */, g; C1 l0 f+ Y* `9 W2 ~+ u0 A' o
- }
复制代码 1 ~5 z. ?7 |! P' @0 ]$ }/ Z
数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)( ?1 E/ V: E& s1 Z1 H3 i2 Z4 t
- {
% g. h. O* y1 W M) G5 ]* Y - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线
, E+ c7 G' S3 V' b [" x& C - if(huart->Instance == USART3)# c. h! P+ ~+ Y7 g# ?- \( I
- {
& R. Y+ b/ J1 K' J% T# l, U - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 ; K. _: w; H, F/ d
- // {
7 C% I/ V% I% W" V+ N( {# P( o) i5 i - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延, i9 w% w* a" C3 ]( Q
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 + B1 c% K# b+ ~8 T0 u9 L) W
- // }
: O* L/ z3 b8 Z2 r5 t+ T - // else8 O: I) l4 v. i9 X& v" r0 `
- // {
' h Y; p/ n5 ]! `2 x" C - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头! L% T p6 T2 q/ M% ~9 @
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
; L' n( x3 x: x. _ - // }* o0 _5 E. Z) K; R1 R3 Q
- 6 F8 ?. U# @; a; q2 n4 |6 a d3 X
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
2 d! s& I7 n, j9 t+ K3 w# s - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
0 u0 u& G6 C7 @( l$ M" o - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收5 x% v' q1 [! g
- }/ ~9 |! H/ L0 t" A, v
- }
复制代码
. G* F% E. L4 b' ?4 O9 }' i在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据 _+ p, r, z/ B
- {; c' ]( t6 p w! B6 k) @0 G
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1: u6 D7 F* y5 J, j" p
- memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除( R( z( p0 i0 c% i. s8 K- {
- Usart3type.UsartRecFlag = 0;//标志位清0
) f* |& t: }! V7 b$ Z6 L - Usart3type.UsartRecLen = 0;//已有数据长度清0# G- X/ f6 Z- }/ y7 N& Z
- }
复制代码 8 l: t* ?/ k# k M. w
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
$ K* H- Z6 X c1 U二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图:
& y- p4 i! K4 r+ h1 N8 g. }
( T7 [1 o& H9 @6 H \8 p) n" m
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志: ! d/ w7 e b- p0 `( w) T
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志. F7 k+ ]0 g4 W
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);5 Z O1 Y% m% h
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
) E7 {. m5 r3 F: x# J$ P - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码
* m0 I2 {/ X& m" o; x* g比如,在我清除串口接收缓存的函数中,我加上了这几句代码:
# @; a7 w8 q1 r C$ r
. I& w7 C- u- C* {, o$ D; {& a
! X1 Y+ c9 s5 U8 A) ]
, S( `& B) ^8 ^( k( Q3 m这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。
+ t$ t. M8 Z) J! W% w; `2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: & N' v" B. i% F
6 i& e: }0 j. Y \( J# }) l
" ^ b( J6 F' L. c5 ^. j* Y7 y
在这个函数中有这么一段: % E% K) q" w/ w) e. i( ~. [$ j
: E. O3 i- U# a; u" t$ B
6 }3 f. J0 Q+ T& {) E& ]5 q
: \ R$ U' @/ Z, D' U$ H里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: 7 e9 A: I$ P' ?
( J, a- {6 U( k
判断的语句如下: - if(huart->Instance == LPUART1){6 v. v2 |* F9 x( w$ b9 u H2 a; A! ~ p
- Enocean_Data++;
& u0 U3 d1 G- A" P F - if(Enocean_Data > 98)Enocean_Data = 0;! p* s0 L, B4 z5 K6 c6 h+ Q! ?
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
6 `4 ]$ M7 H$ p4 m$ t* p- y' h - hlpuart1.RxState = HAL_UART_STATE_READY;
* Q. d+ r3 r6 k& G3 C$ G, D1 h @ - __HAL_UNLOCK(&hlpuart1);9 T" D+ `' C o( v6 }" @
- }) S g x5 R* l! w; Y( }' ^7 P
- }
复制代码
" I ^3 [3 I; U# s如有侵权请联系删除
8 h( k0 ^3 Z" \# q2 j' f 转载自: 矜辰所致 ( c9 l4 i! [1 B! c. H
|