|
前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考!
' B/ q; }" ~. `* s一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序2 u) F5 E& N7 p& P
- {% q* B' _) Z. V7 N8 O
- u8 clear=clear; //消除编译器没有用到的提醒" Z7 v- n3 W$ y
- ( h+ `$ t: M( c* Y/ t1 u' u% s
- //USART_ClearFlag(USART2,USART_FLAG_TC);
: ~/ d- Y0 E; Q: o! k% b - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
. q* l; `3 k5 d! w+ g% j* Q- P - {" J( t' F7 L4 k" H
- USART_RX_BUF[RX_Data++] = USART2->DR;2 |) D3 a( B; Y& B9 i6 R) r& P- @
- }9 L( {7 w" F% ?+ F3 }' o
- else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) , r: a2 k L2 Y+ l6 O
- {
7 H' U) y" e) h, j - clear=USART2->SR; //读SR寄存器 可以清空寄存器5 t0 o$ j3 F% s
- clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断) " x M1 _& l6 q: D+ @' G% f+ s
- ReceiveState=1; //标记接收到了一帧数据
) z0 T6 }: d$ d; W5 G8 m - } a( ?5 I0 W, X: Q; l) e! `6 k
- }
复制代码 . F9 t+ b" v; n4 ~& D, O
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)+ V( R2 ?0 X$ `6 I" [) w
- {7 E& `8 \3 F1 F8 N* K# U! w, J* e h
- // uint8 i=0;
! C% i' K7 ^3 A7 r4 {5 V. Z - uint8 u8CRC = 0;
$ x* d' V0 s8 P; o, d% _- K - uint8 u8Count = 0; . j# \2 k6 M; Y- o/ R0 l
- uint8 TmpVal = 0;
' V; N9 g, z8 |4 ? - uint8 DATA_LEN = 0;
* B& {" M# P, ?# M; R3 \ - uint8 n = 0;
8 g( ^% x G. o3 ^- V& \' v - uint8 HEADER_BYTES[4];. Z% n6 g I& A* A3 ?" V7 V1 o
- uint8 DatBuf[30] = {0};+ }. S7 g% Q$ a$ a& U ?
- u8state = GET_SYNC_STATE;
4 x4 }8 P8 {. _8 i/ o - if (Read_pt != Enocean_Data){4 I6 ~7 J) q$ e% Z: p+ k
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等6 |: l. B; J) ~2 l* F, a# r
- while (Read_pt != Enocean_Data), y; k! X I8 s- D2 P
- {! ?# D" _ e* Z, o$ {* e; i/ v, \
- TmpVal = USART_Enocean_BUF[Read_pt++];
' N/ E% G9 }+ |" I6 _ - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代* S6 E' ]" F; N# x% k$ B
- Read_pt = 0;
: p- K: D" G8 i M2 e" k# \ - switch(u8state)" v' d3 @0 g, V& V
- ...
复制代码
y, W% c g% | B8 t自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){
- E& s4 a7 p5 s/ h1 W - HAL_Delay(7);
. Q' Z8 n+ w% a( i - // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
3 I% r1 z* D; ~* q3 t - if(Enocean_Data > 10){3 g$ H0 }: f5 l% x' a7 s
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
. S- e; k, ?+ t - }
4 s0 p+ b1 k8 v* {1 R h - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
& X8 q7 l) D- {. ?/ J$ p8 @ - Enocean_Data=0;
. O+ p0 P r- F; {9 W* Q4 K K w - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用
9 v7 j0 g& c/ j: Y. C - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错0 U% G/ P1 D3 h O& [' U
- }
& J6 V y* A% }1 d3 u+ [$ u - }
复制代码
, W5 ?" d) y* n1 X: I3 G" {+ X! p在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
+ K4 W4 N' @4 |' n* t现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
' \( w; A' X( Y3 I - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 * u+ e4 g: y* x- |/ w+ N0 \
在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
# l( d! I" p9 a1 M* k - {4 t, q$ j$ k) w6 s( b! t
- /* USER CODE BEGIN LPUART1_IRQn 0 */5 e( L$ n i% a+ J o8 a
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
6 w1 K" [$ p# o - {) K! c* h ^$ Z$ E3 b& }+ G
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);- q" T {7 w `& }' c4 E
- ReceiveState = 1;
1 h+ O, L) e3 e( K7 o# Z, \ - }9 r7 I& p+ Y: y: x1 t& L4 k( n& W
- /* USER CODE END LPUART1_IRQn 0 */; W4 n2 ^8 C* [: t7 E
- HAL_UART_IRQHandler(&hlpuart1);
; v7 H3 E' h1 z+ e - /* USER CODE BEGIN LPUART1_IRQn 1 */6 m. m$ X$ {9 y' r
- E( u+ |( P. U! u+ @5 P8 v! ]' g9 p
- /* USER CODE END LPUART1_IRQn 1 */9 x! k& o& Q1 i& d+ A
- }
复制代码
: z4 b& v4 K3 o$ Y用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
' m/ T$ e$ e4 a& w. H - ReceiveState = 0;
% }. u2 Z' b9 ?1 R9 L) P3 O& T, a - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
6 o4 H G- B% \- t, |! _* P - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
Z8 h% o6 `$ v. S, z0 ~ - Enocean_Data=0;
8 ]% b& V/ E$ U, r/ k" a# d3 J- H7 G# l) G - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
. Y5 \( p5 S- S$ a - }
复制代码
. S: r* |, ?2 i% z8 ~结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 $ z. n: N! `3 @+ h
~" N3 n! n% r' q$ }
7 x) j' L" _+ k# O2 Z4 L) _! g实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
6 W" g" Y f5 q9 F+ a - HAL_Delay(7); //测试,重要
2 ^& m: T1 v8 W$ P4 L3 Y - ReceiveState = 0;
: Z( c- n" C" e9 s- d3 X - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 4 N% r& } g1 V( R4 H
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 ' G0 T0 U! s0 a% Z( M/ O5 q8 ?
- Enocean_Data=0;& @$ S4 z+ \) g
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
# t- r: [- g% ^; k - }
复制代码
9 w2 b+ q j& j! f/ G1 ^发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: 6 E, Y+ h. V6 x* Q. P
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码 # p, Y% ?* f5 e
这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; ( K$ q! l. W8 ^! R
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)2 V+ i- P; v( z$ p
- {# ~5 v0 c( o% r' d% D. N7 {
- /* USER CODE BEGIN LPUART1_IRQn 0 */% _- w' v+ d/ {# h% \
- 3 H `5 |: `3 Q7 `8 X
- /* USER CODE END LPUART1_IRQn 0 */- s) F7 @# m7 h. o( G
- HAL_UART_IRQHandler(&hlpuart1);
8 E4 V* T; {: L+ M2 G g - /* USER CODE BEGIN LPUART1_IRQn 1 */* N0 G/ @8 x% @
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
; [2 @: U+ O- c) X - {
3 a/ b$ E* ?) k4 L2 U9 ^ - usartreceive_IDLE(&hlpuart1);
% w8 L I6 T/ N - }. V6 ~4 F) ^, s. N4 X Y# r
- /* USER CODE END LPUART1_IRQn 1 */8 K/ Z. Z* Q- {3 N
- }
8 O/ {/ \. j# E' k - 9 P7 j$ {% T: Y1 b2 ?3 q' u: R
- /* USER CODE BEGIN 1 */, Q; X* [/ Y9 `; b& N8 Y
- void usartreceive_IDLE(UART_HandleTypeDef *huart)
( v& g* _$ ^" v1 K$ d7 g - {
+ g7 S* n9 M7 s/ |( u1 r - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断, L3 J2 s a* V3 N# W( R# u: S9 C
- // HAL_UART_AbortReceive_IT(huart); //终止接收! B+ u; m: s& i) _6 @5 B; f0 k
- ReceiveState = 1;2 B" i& c6 K6 F3 b2 r
- }
复制代码 " c5 Q c8 r" w3 l% e( S
用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);9 U n! @0 u1 a% {
- printf("send command !\r\n");0 ^' W2 v8 o" ]: W' I+ U7 p8 v
- COMMAND_GetmoduleID();* A, ?/ a `/ }% ~- @
- // HAL_Delay(10); //7ms不够
( }6 z5 U; ]1 L1 u" b9 {9 u% q - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文3 {: _4 _, j. h
- // while ((Enocean_Data - Read_pt) < ID_blueNum){
# ]; ~3 |* r- W2 C) { - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
1 D4 ~% H/ W( m - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
9 r- V$ R! D: c; b! F6 j - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 - B" Z* w1 D1 w/ {; F) d
+ d& {& A0 L: N3 A% Z+ s
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
2 Y( a: j- v B$ \1 @% \5 ?9 }4 w, J N - printf("send command !\r\n");
: z% e) F0 U" q2 w* B - COMMAND_GetmoduleID();" o; v* j _, `* T' Q5 E2 ]
- HAL_Delay(10);//7ms不够
T. v5 S, t' r$ T4 J6 s+ z, b - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文, L' M7 L: U4 m S
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ! l, G. a0 U" U; d8 `$ l2 a
8 n6 [8 v y. {. b( O# M 测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
/ G9 J; c6 x f- \' h& @! Z - printf("send command !\r\n");
5 l; r2 ]4 |2 Y j5 H - COMMAND_GetmoduleID();
' y L; C4 e$ H# ]! x) M8 E - while ((Enocean_Data - Read_pt) < ID_blueNum){; V( k( q3 P8 q0 {
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
8 Q r5 T- d; b$ A! k& f; y7 J - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西1 j+ ?9 o; [- m \* a, d/ L
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 6 e4 U. W, S6 [7 {) k0 v# B
- Z9 H4 |- G$ d3 `! [4 Y) X
测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) ( n3 \( q& ?5 d0 h7 B l! ~ N
# A: z/ J. {( t: ~; V* [; n+ `- U) u( C3 e) v J
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);% X4 ?3 \& y6 G; I0 ^3 o8 ]
- printf("send command !\r\n");
Z$ r$ l a+ }( H5 Q# `5 g/ F - COMMAND_GetmoduleID();
5 F; l- P) G1 |" Y5 i- G - while ((Enocean_Data - Read_pt) < ID_blueNum){
; |5 B6 f; o# I - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");2 y s- h# W: T2 G: ^4 U9 `& B. ~
- //HAL_Delay(1);//延时可以,1ms就可以+ p1 s6 @& {+ p: x
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
( D) F3 @/ Z9 W& y5 l: b - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
" a( \8 G( P& o1 J+ X1 C! y+ P7 e: Q0 o
+ o( `% @/ Y1 N
2 ~) ~: B3 x/ C8 R% i
, k- Q! w9 t% \ d, o
& v% ^% P2 z0 b2 I* n2 N: Z2 \
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
g6 o$ ]& m: Y: }1 E2 ~1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小) `5 i7 w0 M( G5 h4 v5 K; ^# B
- #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据 v2 T1 R2 f- i8 N, R% Z
- typedef struct
$ ] C% m7 }6 @ - {
% C8 b/ U( Q Y) x* j X - uint8_t UsartRecFlag; //接收数据Flag, [* l6 n& H4 [) Z0 w) t9 \ A
- uint16_t UsartDMARecLen; //DMA接收到数据的长度4 l! M% ?, n- ^% i& D
- uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
7 l+ `/ Q7 q9 u9 ^ - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
4 E1 A/ ?2 q- G; Q% M - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
. M( }1 g# l* a8 D, b: k: M - } Usart3DMAbufstr;/ V7 r" O4 y3 v, ^: i' a' I `
- extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)0 ]' V" V" ]5 D
- {2 _" q! P$ k0 u3 ?/ ]& Y; ?
- /* USER CODE BEGIN USART3_IRQn 0 */- T. {& K3 v1 [
1 |8 J6 E* F5 T) [5 G8 D- /* USER CODE END USART3_IRQn 0 */* C. t" P! O8 v# s
- HAL_UART_IRQHandler(&huart3);8 T" \' ]3 ^7 H& k$ l m8 g, H
- /* USER CODE BEGIN USART3_IRQn 1 */+ c6 r6 {8 G6 }) ~) {
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断
- n1 ~* K( u X9 c( i - {
4 k$ K5 `/ o7 F# M - u16 res = 0;
# e* v, ~, q5 n8 y" A; `6 v - __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志( d Z: u% H) P+ F
- HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断9 C2 w& G8 f0 o( j5 X
- res = huart3.Instance->ISR;
8 X5 b# O% t- g; f2 [ - res = huart3.Instance->RDR; / H0 X' S# O& L: k9 b' Y
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间
' l( } x0 c! y7 R# y - Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 0 [1 g' S9 p/ ], q1 q. Z2 r$ q
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
4 L' G9 P, Y+ c - }' x/ P5 c$ ]0 j
- /* USER CODE END USART3_IRQn 1 */
6 t5 y* t$ w3 p2 b! u - }
复制代码
9 R! g$ B% `) T* [数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
, p" O0 V/ ]! F& } - { & s( L& \- H' t3 S
- //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线( s/ n: j. O$ |7 \
- if(huart->Instance == USART3)
- v" v3 d1 `# O$ u0 t, n. s# \ - {
4 D6 `" A6 C6 t9 o3 ^ - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 6 z# }! ]0 S. k7 F; k* Y, d
- // {
$ H$ A( b6 v1 G1 X - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延
( L& G& R# j H# ` - // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 ( G0 @ x& l& R
- // }
( ~7 J3 Y6 M+ u9 z, H# H7 | F - // else
% `: e2 J- o; E2 s$ C x - // {
' q/ D. N0 V0 ~& w% X/ L - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头4 [4 q* I0 X2 h9 B2 p# G0 L* P
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
) S8 h6 H, `8 s - // }; n& w1 u$ D7 @" @
- & V4 D. J* C, l
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收$ [, }' c+ B! i# Z) k
- Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
% J; `- a$ F. u5 s- Z$ p$ g2 X' w: \ - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收4 H5 }; x. F0 I6 Q$ J3 S6 j) i+ Q3 o
- }
4 ^" X; G, u" Z: |% `/ K - }
复制代码
`* ^0 D* w8 Z- m7 {4 _在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
) i5 q/ Y5 X3 u, A1 C ~, ^ - {
9 o& Q8 s& }4 q" E5 ^ - HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1. L' r6 j3 b4 ^3 |+ r' u' ]" k [
- memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
% s x* |0 t; J- U; i9 G - Usart3type.UsartRecFlag = 0;//标志位清0
# e# p# {! X; _! N - Usart3type.UsartRecLen = 0;//已有数据长度清06 @1 {; q- N( J2 ]# Q: l! G3 D
- }
复制代码 # \- i7 @* g& V& O& }
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
G2 J" M) O- G8 T; d$ U" U二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: 4 V7 R L: c* d, ]1 v9 A0 l) A. }
- O1 v Z, M! d
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志: : Q4 Y3 I: _* G- o" F1 |
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志6 M2 {* j& d, q: ?: [9 {/ L( l
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);4 |0 u1 a% v( K8 @
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
* _! c( Z" a# b% V* r& o - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 / C- y! z0 |2 F9 v c
比如,在我清除串口接收缓存的函数中,我加上了这几句代码:
4 p# E$ }" F( g1 N9 K) Z
* [& K- I" N. z4 R% h* P
3 p4 |: @ p- N7 J4 } C* ]& F% J: @
这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 1 M6 y% J B. S: y6 s* s! e( ?
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: }: u V' z2 {( ]3 M6 n
# P- B: \, ?+ _6 C( C1 g8 Q, P
" N: a* R7 V4 {; u9 u在这个函数中有这么一段:
* B' r8 N1 U* l: L$ Z
' y b' S) B4 s
1 g4 Q; S$ G- z2 x4 J
' {0 E8 U+ M) b8 ^8 u0 |
里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误:
: j. U6 ] ~, @) u' a \
' t |1 s6 Y0 Z, N3 D6 e: B+ ?判断的语句如下: - if(huart->Instance == LPUART1){$ f6 d# e9 n( v/ F$ m) B
- Enocean_Data++;3 k( W! @( t2 @- Y/ m6 H/ Q/ e
- if(Enocean_Data > 98)Enocean_Data = 0;
3 p3 ^8 o7 H( o0 g! c - while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){; n) U4 s- `; I4 q2 n
- hlpuart1.RxState = HAL_UART_STATE_READY;
4 j5 V: C$ L& s - __HAL_UNLOCK(&hlpuart1);
+ ~) q# V: ?' {5 E& r# q, c - }3 |- F% T! j7 A7 U* s) Y+ j9 l
- }
复制代码
& a, k3 \) c; X如有侵权请联系删除+ n5 U/ `, m) y0 e
转载自: 矜辰所致
0 [6 r# S7 s9 t2 p A" U |