|
前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! ! y; U4 k, a6 Q& T9 U( v1 r, I
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序7 c5 c f) M' n1 p3 m2 T" H
- {
) i* F; w' \$ x0 Y - u8 clear=clear; //消除编译器没有用到的提醒
# C6 q; N! Q4 r2 W/ @$ h! l" v - 2 X/ m. ~4 T& r! e
- //USART_ClearFlag(USART2,USART_FLAG_TC);
/ H% ]9 e3 d' H9 j; H - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) " v& ~6 C; S" p6 M- F
- {, D1 m1 G$ H: U& B
- USART_RX_BUF[RX_Data++] = USART2->DR;
c/ U: ~* |7 Y9 I! r - }
7 I, W7 e0 K1 k3 \ - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) : a( s3 e/ ` M$ P
- {# z u. a; N7 `8 g
- clear=USART2->SR; //读SR寄存器 可以清空寄存器
7 M2 R$ d2 b7 B - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
( K5 I6 S% c) n/ \6 t( z - ReceiveState=1; //标记接收到了一帧数据
4 @- ]: F) C# V; @( B8 }- n - }$ T/ K! @8 E5 N8 Y$ W+ m( O
- }
复制代码
, n& @5 a$ s& Q不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam). y* k1 T+ N1 j3 A0 O: u! ?4 o
- {2 T3 B$ H3 Q- |
- // uint8 i=0;
! I9 D9 g0 ~. y' I l' k - uint8 u8CRC = 0;
0 M% D) c& K4 s5 V$ I5 F& a - uint8 u8Count = 0; 6 p/ A& p3 f. J. e
- uint8 TmpVal = 0;
# B; C1 Y* y# |( s5 i$ R0 ]5 o - uint8 DATA_LEN = 0;3 F: a, z5 a. z5 M7 i' ?8 W1 ^8 Y
- uint8 n = 0;
* d+ r6 u" l9 g2 e; j; S/ B3 u - uint8 HEADER_BYTES[4];
' D/ y5 `0 U; a8 S8 _9 z - uint8 DatBuf[30] = {0};
, L* ]6 {9 C- s' ?* i, w) N - u8state = GET_SYNC_STATE;
4 l2 J2 P; A$ g& P2 _! S# E - if (Read_pt != Enocean_Data){( D# B4 ^/ l: a! {- o; a: o
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等
, n6 c5 \# T" k) x - while (Read_pt != Enocean_Data)" r0 U- E9 a. v8 d! Z
- {
; j% X9 t! E2 j3 e' z - TmpVal = USART_Enocean_BUF[Read_pt++];
' ~( h1 }% J A3 |5 t+ u7 t - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代9 n5 {; F( O/ {8 s+ Z1 ~
- Read_pt = 0;
1 {. v |, I- e3 s( q5 F - switch(u8state)
% } P$ @* A) v3 h4 ? - ...
复制代码 * t4 H5 J# k- N5 s1 }' e
自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){4 G! `# [* t6 u6 e. F
- HAL_Delay(7);
; P& e/ _7 Q0 z* e4 L6 }( X8 A - // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
, `% b" ~6 ^ K$ G - if(Enocean_Data > 10){$ a8 U8 Y. N. d* T. g7 M$ s7 l" P
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
- {( u( v! i$ d6 x5 C - } % N2 {' d) i% Z7 _; a7 }; H8 \3 u
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
% I: z" ^3 F$ i8 {0 L% b) j7 P - Enocean_Data=0;
3 \, w: u9 w5 E4 }' S2 P8 \. m - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用
2 F. L) `2 x! v/ [ - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错( P! t1 m+ e* j& x1 }" y
- }
" [1 F8 f( x" `8 [ - }
复制代码 4 b, |; Y3 |1 v! R
在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 {: {' o0 s. [/ @( S
现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);: W& v/ G+ J- S' D
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 3 L$ n t8 Q6 E6 u- r# e
在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
# K; q0 ~ u/ M7 D! C! S7 E - {
2 R2 | R) c6 J! e2 C - /* USER CODE BEGIN LPUART1_IRQn 0 */5 Q* E2 }. H5 z) B" x
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET)), `7 V3 d6 ]; I, Q9 x2 L o
- {% M. o3 F3 v% G# P7 i. T( C
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);9 X; u( ?% F+ g$ \- ^1 M* c1 i
- ReceiveState = 1;
. r* n8 b8 L# {+ e9 z - }
, k- R2 e" S, h) f& ?, Q- c - /* USER CODE END LPUART1_IRQn 0 */
( t& x8 ]/ \7 j2 Z8 k) [ - HAL_UART_IRQHandler(&hlpuart1);
5 b: v9 i7 I& J: h' S1 f4 f - /* USER CODE BEGIN LPUART1_IRQn 1 */
( D/ ?, ~/ F0 y4 m* w Z
) B4 Y9 [% p7 E: a1 l* ]- /* USER CODE END LPUART1_IRQn 1 */; M/ Q# f5 B: c7 c0 {; [/ ~
- }
复制代码 % l. ~* C( R4 C. s% z
用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
7 Y. S# q+ t- Y; x- w - ReceiveState = 0; 2 t# Z S2 p& \, \8 i/ `
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 ' q1 c$ a) `3 q& p' Y2 {3 V
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 - C- @- w& S# R4 B
- Enocean_Data=0;9 D1 u# h% s/ ^, f! v
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
g6 A3 x X" @, C8 K - }
复制代码
" ~+ F1 ]% D5 r0 g8 v结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 7 n* f: x: ~8 r [) k& a# z
5 O. i4 z- r5 ^- d: Q) I1 ^3 b
$ K/ Y/ X' l2 Q! Z7 \& i4 D4 o
实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
5 n l: c% L p( \ - HAL_Delay(7); //测试,重要5 z% I; ?% N0 E( D* X8 s
- ReceiveState = 0;
9 Y; A6 x( O1 Z( x2 W4 X - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 1 @; p4 ]9 O! z l$ L
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
( [. x9 R* p4 v - Enocean_Data=0;9 g5 N4 U# t G }" j5 ~9 C' m
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错+ w ]: R* K) e O
- }
复制代码 2 A0 k) C6 [& j0 S6 Y
发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断:
, \7 k8 D9 B8 z9 L- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码 * a! u1 Y# Y4 {7 Q0 p
这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯;
6 I7 r: q* q) [& j2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)( ?+ y% _5 A( e4 L
- {
+ M! p* I. d/ ?: o0 C2 b; | - /* USER CODE BEGIN LPUART1_IRQn 0 */! Q$ U( _* z0 l4 Y
- ; y8 t- o1 @. B4 _
- /* USER CODE END LPUART1_IRQn 0 */
1 b I( t+ a- r) g6 U+ W- y - HAL_UART_IRQHandler(&hlpuart1);. y& f! U6 E- g# X
- /* USER CODE BEGIN LPUART1_IRQn 1 */! Z6 m4 ]5 I& R. Y" _4 R
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
2 q2 Z4 ?6 v' E" D/ h - {; G" }* d9 {( ]: ` ^. e! R6 m
- usartreceive_IDLE(&hlpuart1);
4 H. L& X* N$ v# B8 f- r - }9 I7 e+ R. `" O7 M, ~
- /* USER CODE END LPUART1_IRQn 1 */& d0 Q# X( Q4 K7 E/ z3 J, D6 l4 D
- }
: g4 _3 A" F5 v
: f s4 R7 x7 O1 \- /* USER CODE BEGIN 1 */% F, R" Q! w- R+ _
- void usartreceive_IDLE(UART_HandleTypeDef *huart)
2 C: C$ f0 c6 Z* M3 p- g - {9 P6 C7 q' X! D- ? L4 R
- __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断: S( z6 b5 |9 I$ j* ~# G" H% u
- // HAL_UART_AbortReceive_IT(huart); //终止接收) m+ m5 [" `8 f0 z8 L8 u* L
- ReceiveState = 1;0 |9 w$ Y/ h4 U% e3 {/ f) H; X
- }
复制代码
& j: ]4 ^& c& ^# c4 H# H+ A用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);
: G; s6 ?: Z/ E' z' ^ - printf("send command !\r\n");+ M' Z5 b- |1 ?) X) ~
- COMMAND_GetmoduleID();+ w7 z( h, [0 Z" E; t w; I
- // HAL_Delay(10); //7ms不够 0 ]$ ` A; X: Y
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
* H" c, ]# C& E" C% \9 l - // while ((Enocean_Data - Read_pt) < ID_blueNum){
0 V0 r" z: ]' g$ G3 a - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
7 T0 m0 _8 s) @" S; m - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
- M+ Q, O. D: i4 J" A( t* \ - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
9 H8 n q$ [, @% @8 E% T. G
0 j! ?$ z! s7 ?* n7 a* s如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);& } i% e% h' W- M* T7 m q+ R# ?
- printf("send command !\r\n");. R. J9 c, o' S* @4 S
- COMMAND_GetmoduleID();# {% y- @& C4 @4 I% y0 a6 N
- HAL_Delay(10);//7ms不够
4 N8 R( @0 P) P/ @: I - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
6 o2 |+ j! L1 \) \' ~- H2 B# c - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 $ k- Q1 h8 h# A8 c* f" Y
- ?5 v8 `# a9 r& D* J: y& H
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);9 `: j: A6 \: f. _% p
- printf("send command !\r\n");: \( s0 r/ |% V7 s/ X/ Q
- COMMAND_GetmoduleID();. }& I$ E4 V# {
- while ((Enocean_Data - Read_pt) < ID_blueNum){; ?+ G$ x, ~2 p; \: N: e$ p
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
3 l$ o( b& q6 I& e. t; Z! r - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西( K3 L; Q& P6 n; D' B: K1 `
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ) x9 x3 u o3 m( r- c
4 j' n9 x: p2 d$ V* g9 G! \2 q 测试代码3 , 4(不加条件随便在等待中打印 , 延时 )
* n* T; W% f" u) W4 L `; h) e2 [) Q
+ r+ j Q9 t# q' U* w
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
, m2 [5 b7 s1 Q+ E0 x) C - printf("send command !\r\n");0 t4 X' s2 i8 F. P! f( o! V ?
- COMMAND_GetmoduleID();
: ^, M' b: E3 W$ k) p: T - while ((Enocean_Data - Read_pt) < ID_blueNum){
# i0 E a% z* H1 W) r& C. O - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");) y2 _; s0 R+ l: o
- //HAL_Delay(1);//延时可以,1ms就可以: a& Q; S& a; E9 Q0 L8 ~5 l! ~
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西0 Q i; z' \8 u4 n b2 b+ S
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ( D) v+ K7 h& X
+ ^3 n1 V3 u- c8 H" i% g, D) m
! @$ ^5 C$ ^* t5 p
7 W4 m6 y# @- _! n. `7 B
9 O! I3 A( R8 _8 q2 Q' G5 Y
$ g3 b7 \7 c( e, H P! o# h" E
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =! % _) M& t: ?+ x( H* G& y* f
1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
! ~8 z* ~8 v. k( p - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
) }- J5 Y" ^& p L4 J - typedef struct
% o2 C1 d+ B V4 A- z - { / f5 h( U) l, v$ q1 O1 o
- uint8_t UsartRecFlag; //接收数据Flag
% m- n! P, Y; B m$ N2 T" M9 F - uint16_t UsartDMARecLen; //DMA接收到数据的长度& \# k( v* a, n( q! s% g* [* S" B
- uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度- Z: Q, a' W. e4 I# S& k) [! \
- uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
- W, t' }/ k& X( {3 } - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
G0 P) g0 F8 l) G. w- I. W - } Usart3DMAbufstr;
* \1 ]8 S9 P- t! i$ Z - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)
7 y" m4 W/ E; |% { - {
' _1 R4 C! }. |+ E4 k9 S - /* USER CODE BEGIN USART3_IRQn 0 */7 [2 J* Q. z) K/ b$ P7 n
- 5 {" A* o, b! X' K
- /* USER CODE END USART3_IRQn 0 */
2 q) a o+ L6 z - HAL_UART_IRQHandler(&huart3);; L/ s& T W9 J$ u m& z4 w
- /* USER CODE BEGIN USART3_IRQn 1 */
, h0 S2 { o& W' q0 O - if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断' V% j9 Y# U. D+ r
- {
6 C# i. s, Q+ F& `8 N1 ~ - u16 res = 0;
" b H( [5 `0 D; H4 T7 H( g - __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
, ~9 M! \6 c# x# x* O' R/ E - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断
2 @& u9 e. S$ k3 D& Z, ~ - res = huart3.Instance->ISR;
" s/ ] r1 U; ?7 L - res = huart3.Instance->RDR;
# `9 S, ]5 ], m( n R - res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间. b" Y8 l# n/ ~6 c
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 2 V. p% {: j7 q& Q& e4 X: v
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用, T) u1 l; ^; Z5 T' Q
- }0 i" {/ [7 h, D1 w9 w, P
- /* USER CODE END USART3_IRQn 1 */
3 N, C4 y0 I. G6 A2 h8 O; i& m - }
复制代码
7 B0 ?5 ]9 e) r+ k7 S* E( M数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) D! g2 U$ z& H+ ^/ X
- { 7 }) [5 n7 ^4 d& Z
- //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线4 V* ]6 H& K% G, x
- if(huart->Instance == USART3) F0 u/ e3 A' h7 `2 t" [$ g
- {/ }5 M% c t) A% ]& i& l6 N! L
- // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 5 ]# M- s5 A. ?* s0 O8 C' V8 r
- // {
0 C# d2 ^3 x4 y) a6 G9 z- l! P$ @ - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延; P; V5 ]2 y5 W& i! i$ \; U
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 9 L& h, r: v) B/ s& }# p% i1 `
- // }
9 s0 j5 }5 _! J* U' ]; c: L - // else
5 g5 O& n e# u5 K3 t - // {
( g" r: L( p# g$ p6 w - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头: }- j) [3 s7 S# V$ O
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
& r# f# F; Y2 J4 N - // }5 Q: |. `( q5 q* a8 _
- ( N/ b( l1 f) w( N2 \
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收+ ~8 c& j$ T% p' O. n7 w
- Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
& h5 v1 B: I6 H2 i! {; z - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收& }1 h% u2 Q0 n4 I; s; v. q
- }
' y% U9 e) [# D! q) h, f* e - }
复制代码 2 v1 C+ S. o- Q! w" t" S
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
m" S; Y, c& P# ~8 \9 E3 i - {/ c7 ~) b- D, j3 y& [ |
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
" b: [) H' M/ e3 l) i: K& q) Y - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
1 I! n9 }1 \5 N& Y# z4 F - Usart3type.UsartRecFlag = 0;//标志位清0
# f7 q+ h1 F2 l4 r& v$ u9 K$ W N - Usart3type.UsartRecLen = 0;//已有数据长度清0" n, ]9 A0 y) i! b/ I, B
- }
复制代码 , E& @8 e4 W0 v- L+ k- ~* }8 a" S
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
! @4 K! F* s( M: M& T% O二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: $ a: _% h/ a* s3 g3 t5 y
* Q5 X: Z* d: j- @
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
0 [' y/ d- v; K3 l2 c6 y8 a$ \1 P- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志
4 {+ W+ F, x5 I9 H* K e% o - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);; y) a$ u7 G8 G5 P- i- K# s
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
- B/ Z2 v6 ~/ q# y: Y5 ? - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码
* f# }1 c+ w8 D/ ^3 I4 o比如,在我清除串口接收缓存的函数中,我加上了这几句代码: 8 J1 a1 s* j. m: M/ O4 i+ Y
% R& c _3 |9 ~& t8 w/ f6 |
C2 E% D0 w E0 B
; V% R: z5 d# K# S这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 ( F& G7 }: [3 J [# R# z
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断:
4 v3 ] f3 @/ i! G0 I3 F; E
7 b# n6 }/ Y# G$ X6 [+ J3 p+ B1 v& L: U
在这个函数中有这么一段:
1 D. t8 @& ]' h" Q' f
- q3 t3 [8 d8 K% o( m" p
3 _; V1 v8 `- d. n! d
4 |" ]. R6 c9 t; I8 @
里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: 1 `1 ?3 ]3 l6 \/ b$ W% g6 J2 B S
' M; `) R% x: _ D/ L判断的语句如下: - if(huart->Instance == LPUART1){, m0 Y. s3 T+ {# y5 \+ V
- Enocean_Data++;! I% d. P0 e1 c3 y& h& H# a8 q( P
- if(Enocean_Data > 98)Enocean_Data = 0;; I8 D9 \% q% b
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){/ v( x W; h& S9 m) f& _" u
- hlpuart1.RxState = HAL_UART_STATE_READY; [! A3 ?. m; m1 N9 R5 ?/ J
- __HAL_UNLOCK(&hlpuart1);, D% [/ N5 M7 j: E
- }
~ q: H' p0 j7 @0 A2 G - }
复制代码 ' b ?3 B( M" a) E3 C2 ~, z2 \: G1 ?
如有侵权请联系删除% n* k; K5 P- O
转载自: 矜辰所致 " h' l# D$ G w
|