|
前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! ! K4 M' V5 H) ^. Q; ~7 G& v. `8 a
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序3 C8 m2 }/ A7 z" _& A
- {
2 g! l* y( A5 Y+ I3 x5 o+ t- j u% x9 U - u8 clear=clear; //消除编译器没有用到的提醒) a2 h# |6 ?9 V& W3 J
- 5 R# A$ L E0 l$ y9 j1 C
- //USART_ClearFlag(USART2,USART_FLAG_TC);
3 C }$ t. l5 u. t; m - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
! M3 c3 Y/ j/ d - {) r3 W2 g2 e# v, r# _! A/ j
- USART_RX_BUF[RX_Data++] = USART2->DR;2 B! G& A: i, a
- }
0 p+ Q4 \% o# } - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
$ }5 r! ~& ?. F$ V2 O - {
: u* e: T& a* ~- y+ Q1 Z3 Q* m - clear=USART2->SR; //读SR寄存器 可以清空寄存器
- x- |* Y# k+ Z: b1 d% p- V* x$ g - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
, M3 m; m! i2 r* v( R - ReceiveState=1; //标记接收到了一帧数据
; l# b/ N! I3 t1 M2 w2 R - }
8 z$ n/ V4 y, S8 l1 n9 G& L - }
复制代码
) Z6 ~; g: R% E3 H3 s4 A不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
8 {! Z( Z5 b" P( G - {
$ p- f! M0 Q- ?( ~7 o; ^4 C2 [ - // uint8 i=0;6 c: G i; W* g
- uint8 u8CRC = 0;$ T. f5 H8 W$ T+ j. V
- uint8 u8Count = 0;
% l9 h$ O: k2 ] F) l8 i! |- p - uint8 TmpVal = 0;
1 s) ]' I5 P6 d8 t - uint8 DATA_LEN = 0;8 Q# ~+ [; ~) p8 M' }; ]
- uint8 n = 0;& w2 \) B6 v( k6 `. H
- uint8 HEADER_BYTES[4];
; s! U3 B* m3 h& C; S7 D - uint8 DatBuf[30] = {0};
7 G) M0 d1 u8 G/ F$ s - u8state = GET_SYNC_STATE;
/ U. ~" W" ]9 L; i/ B - if (Read_pt != Enocean_Data){
' r. g+ J8 j, }4 z - delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等" t- R0 {2 ^* l+ Q0 {) P
- while (Read_pt != Enocean_Data). F/ N' v. S( A7 ]8 w1 V
- {! X$ s6 u) h9 q/ Q6 l+ V# k& o
- TmpVal = USART_Enocean_BUF[Read_pt++];
" G/ M1 c6 Q+ M9 F7 n. J - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代* H9 U5 @4 k4 y
- Read_pt = 0;9 M1 R0 o/ C- \( S+ X6 ^
- switch(u8state)
" @2 H( `& m y - ...
复制代码 8 C9 O7 B' W* |5 l/ A5 t- o" ~
自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){2 s- P# X. H* c! G5 T6 V5 u
- HAL_Delay(7);
4 w# t1 h) t+ U; e8 Z - // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);8 J7 W/ }' P/ T. Y ] ?! d. }$ |
- if(Enocean_Data > 10){
5 ^; \! H6 Y. ~ b: u; t" t - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
, }5 v k3 S; @ - } 7 n& C- S ?3 A/ Q
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 * a2 H8 L# f3 e4 x: `
- Enocean_Data=0;
) Y ?4 {" A5 T E2 k# M. D$ I: Z% A - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用/ z% P, D- E, W* Q
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
' L- S9 V# m' `* Y3 S% [& ]% ~# X - }* [, e G$ w0 {/ R7 S1 x( \" X
- }
复制代码
% J: ]$ H8 ?, ?在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 4 ?0 s9 }$ m4 `, k
现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
! K6 j" Q, A- v- N - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
, W2 {6 I) [. Z$ X' Z) s0 q在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)5 E+ n2 V5 ^7 b/ L+ F: I1 ], ?
- {
2 ?0 g0 |$ [4 a2 n - /* USER CODE BEGIN LPUART1_IRQn 0 */# u; `3 U/ c K9 W8 X6 L+ }8 ^; `
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET)): v) b4 J7 g; S
- {$ [, p: g' ?+ C9 K" ~8 w
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
: ?. F; e% {( i; o/ K& G9 ? - ReceiveState = 1;
( N2 r9 h5 @: O' q( B; m - }
2 ?/ @* L! | E - /* USER CODE END LPUART1_IRQn 0 */) u! A3 C, F) \6 w$ t! S
- HAL_UART_IRQHandler(&hlpuart1);
# k% @* U: t* }$ @ - /* USER CODE BEGIN LPUART1_IRQn 1 */0 ~6 O. v. d" g+ }; Z; K
( G7 l( i7 i# w/ Q- /* USER CODE END LPUART1_IRQn 1 */
2 m( ~7 g0 z) C. D# g - }
复制代码
h/ Y2 R( `' w* a, s3 {) F: |用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
) ]2 w; q& ], k7 w' N* N - ReceiveState = 0; , E) G# U& L5 d$ X, [! T$ b
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
. t, P6 X3 F, \7 l - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
; U, d5 Z5 g* J+ e& @9 B# h2 x - Enocean_Data=0;- P2 a2 {( f- \ S* m/ b. G5 B- F! ]
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错5 G# @5 P9 H" A, i
- }
复制代码 / z8 \4 E) J/ u/ Z( v
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 5 T: R/ Q0 v6 e5 U- ]1 }
9 L8 R& Q. X! q4 g; S# o
) T$ s( j, j4 ~
实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
3 h# Y* E/ S5 h0 D1 x2 d+ o - HAL_Delay(7); //测试,重要
: y, A2 H) s! q( q* J0 H - ReceiveState = 0; 9 p4 m! C7 P8 ?1 i# N1 Q# T2 v
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
2 Y( g: F4 R& ?6 x' {) G/ K. f# i" \ - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
S k( F& r- E, Y' D/ Q* Y - Enocean_Data=0;
6 b5 O6 Q. Z z, V9 {1 C - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
; [5 j" y& N9 g6 g - }
复制代码
3 y; s4 {% f" g. {( y( O9 a发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断:
! J6 T* k/ y' I" \( m- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码 # J" ?0 t2 ^6 t0 m0 g% v- l
这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; % S4 z- H4 U( {* R3 W! S
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)
) Q+ K; @! x/ g# d2 u - {/ `; J+ ^9 t( m# b6 x) g9 w
- /* USER CODE BEGIN LPUART1_IRQn 0 */
- z& d# J7 v' h* C1 b9 s6 g( @ - 1 {- r* \6 E3 ?6 W/ t1 @
- /* USER CODE END LPUART1_IRQn 0 */: z/ h7 u/ r% B
- HAL_UART_IRQHandler(&hlpuart1);; H9 [/ J0 f$ O' k+ o Q
- /* USER CODE BEGIN LPUART1_IRQn 1 */
$ ^/ t- D" ^ d0 C; R% W3 H8 e5 U - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))! M- A9 R v, _: f, N, a
- {
1 _! M3 ^9 _2 Q) w7 h5 z - usartreceive_IDLE(&hlpuart1);2 p* Q- O7 K* |) k- ^
- }# q9 {( Z/ a1 Q
- /* USER CODE END LPUART1_IRQn 1 */
& l! I" U) v; n h' g y, w - }* u# o( K3 O' A+ v5 q
- / c- R1 N* C0 O5 t+ c
- /* USER CODE BEGIN 1 */
) a1 X1 p1 T) [3 ? - void usartreceive_IDLE(UART_HandleTypeDef *huart)
1 j( N* W; f5 H/ Y8 {! w - {: C" L0 H" J% |* f! Q
- __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断
& X* p; g) j% \+ Z) t9 e* r/ f4 | - // HAL_UART_AbortReceive_IT(huart); //终止接收* Q# F0 s2 l! N3 J: G; H
- ReceiveState = 1;3 V6 F r+ H# z
- }
复制代码 / x3 f; Q4 ^3 O, }1 T: U9 D; 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);
! \ F6 q i7 T' V. \ - printf("send command !\r\n");# z4 @2 _6 g" B& c7 U
- COMMAND_GetmoduleID();
& X' }- G5 C! Y - // HAL_Delay(10); //7ms不够
) Y5 U1 T- r: H, z" }7 J1 G& u3 [ - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
$ K6 ^5 \# I0 s! P - // while ((Enocean_Data - Read_pt) < ID_blueNum){
% j% g1 s) ~# r7 q( C - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);( Y) l; y- p( L n9 [$ `
- // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西; M! `# p& U: R0 x
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
- ]) @3 g, K H2 L7 H5 k0 K7 X& e
; w+ L3 k2 R& d" A, D8 Q6 N
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);5 n# J- f. d9 s M; U7 ~
- printf("send command !\r\n");
' @8 `- ], |) z% G) y - COMMAND_GetmoduleID();6 i. U* s1 m- p, Y
- HAL_Delay(10);//7ms不够 / Q4 D/ Q! v2 _! r& v
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
' v* p$ @( I1 Z9 O2 p& n$ E - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
. }. p! q" l9 U3 E! z6 ~1 | S3 r
8 G5 L. [+ R0 I
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
/ J; u) G m5 a8 B4 x - printf("send command !\r\n");% O' W: I3 n3 t7 v( b+ l3 K O- e
- COMMAND_GetmoduleID();
3 H% W8 R+ _4 p5 L1 v& d. Z - while ((Enocean_Data - Read_pt) < ID_blueNum){% `7 ]0 p X5 M3 F9 y! P
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
9 c/ r; s( {$ `- X - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
) ^& i( P" w) E& w9 ~0 b& B& M - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
7 l8 s7 {3 Z4 r5 b( D; x G
/ I, B# M5 J* G+ i. U 测试代码3 , 4(不加条件随便在等待中打印 , 延时 )
# | y9 J- K0 g7 w8 a9 |! E! r* ?
: e" U, ~! B) |/ Y) ~# k! W) _+ f8 k) ? J: D' W7 m s$ L' ` u
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
/ V+ V2 z9 {- ^0 t8 f1 \* J; G) ] F - printf("send command !\r\n");
" `4 {6 ~) _# z4 S5 \% [ - COMMAND_GetmoduleID();
; W" Y9 E \; W# g& m - while ((Enocean_Data - Read_pt) < ID_blueNum){
0 T" q0 X- m: w8 W1 d - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");" h# w# G/ H2 v& I! P- q' c4 O
- //HAL_Delay(1);//延时可以,1ms就可以
+ |; c: d; t/ H9 T9 `* [ - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西3 B R$ S3 Z$ U7 U) M9 g1 V+ c) i
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
R% L5 X6 ?! J/ f4 E* p3 h
+ U, ?) N: w" x4 E3 [7 i8 w9 ]
8 _- o" e6 x& `2 V1 K
; |1 ~6 x# U! o1 h
" v7 j/ E9 S- m5 f$ S2 Q6 c0 ~) l. o: x* m7 z% N# Z+ Z! M! c
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
' o/ @1 I% Q# H) Z1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
% Y8 C5 ^, x" }* Y# j# \* O, H$ d - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
3 p: N- c" H/ l% S - typedef struct* l6 E" g: A8 q/ M, S
- { Q/ ]; `# Y- S/ B5 m
- uint8_t UsartRecFlag; //接收数据Flag& V, T# h" e, v( f( W1 U+ B
- uint16_t UsartDMARecLen; //DMA接收到数据的长度
1 ?# R* b# r% H. p8 A) B; F4 { d - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
' |* K7 k/ R! a B& ?8 ~ - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
# H5 @$ D6 l8 @: }( c4 G/ i - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer6 S/ w; U% |: @4 F2 A! X
- } Usart3DMAbufstr;" N5 D" f/ n0 |! w$ O/ @9 o
- extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)* t! X% `- t0 f# n5 O
- {
/ N; a% T" Z+ ~' P - /* USER CODE BEGIN USART3_IRQn 0 */- @, x' _# S, M; \
4 k, c: v6 t/ r: f+ d- /* USER CODE END USART3_IRQn 0 */
( y2 {% P0 L& B( N0 X$ r3 v - HAL_UART_IRQHandler(&huart3);
* G7 x- K. j2 O7 I' m# F - /* USER CODE BEGIN USART3_IRQn 1 */
- H, w2 y& }! q; j: ?5 X. l - if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断" ], }2 _+ w6 w
- { 1 F& i1 B {1 T
- u16 res = 0; # I* Z* {8 B" C& h& V4 N( U1 G
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
6 z: L# a6 K' |/ a# P! L - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断
# h+ D# }0 e+ O6 S - res = huart3.Instance->ISR; 5 z, S ]- Q. g0 n+ V/ f) c! S
- res = huart3.Instance->RDR; " F# C6 |) D8 A' T
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间% c; t( G* x7 m' ]8 u7 F, e+ ^5 X5 z
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 ' I: B, l+ c* O
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
3 L7 W4 ^9 c0 j! ^6 u - }9 A/ [& \, D+ h1 c# Q4 G% {* [
- /* USER CODE END USART3_IRQn 1 */
+ M7 E1 j, j5 s; Y# s' p& k: ?' g5 f - }
复制代码
, B/ m0 [& n- j% D+ H. l) } v: j' M数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{0 u8 i# Y6 h) ^# M - {
; @# i: A! M# i+ @1 u5 ]) R0 N - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线4 }( g2 j" S) y. t% [
- if(huart->Instance == USART3)% ]& \# g" ]3 ^ X
- {# c/ `- L) H8 u9 N
- // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 ' N# ^" ^+ {0 f/ O2 c
- // {
) u" N& O+ E+ T" i0 V( L( E - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延
) ^! l3 x2 ^9 N6 p3 a1 e - // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数
4 I+ W' Y q8 A' R, i - // }
9 z- a& U/ X$ P+ D( G8 {7 e. o - // else) z/ }) ~( i! _7 P" E7 o1 ]: W
- // {
$ W7 u; f$ H; E% V# c6 k - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头
6 w' D8 p- J2 g; E - Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
. j5 I$ c1 y! o4 z6 V$ x - // }
: \; R0 C3 y. f8 N J6 x -
' ~: A7 c# W$ h/ m% a - memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
7 l9 X# B1 W9 L0 ^. X - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据: q& v' l$ e) N
- HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收
$ b0 U0 L: m6 p# o* p. o* n4 P - }1 G7 u5 n. j, y/ [$ t! M
- }
复制代码 2 x9 f- Z9 g" i4 p7 X% L
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
; T( H* J7 P% b0 j - {
6 _1 `% l; X: C9 o: x - HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
: X1 o% f+ V1 J: K' Z6 s. H - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
1 J( {" s g$ W* K1 k- ~" ] - Usart3type.UsartRecFlag = 0;//标志位清0) ]! \. ?, U4 E- D+ l7 q# q1 ]0 T
- Usart3type.UsartRecLen = 0;//已有数据长度清06 K2 }% o: _$ a: m1 g$ R
- }
复制代码 ) ^! ]- n+ E2 @5 f' x: d
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。 * `7 D' d+ l" l4 q
二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: 0 N. g% h6 A2 |7 j" n( [, d
9 N' s8 [, D% U! a$ D* i1 S/ T出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
; H! [5 t- y" ]6 E, H- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志 g5 \; c1 w" q
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);
t4 H# P1 i9 I0 V3 M3 m2 m# M* O - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);# ?" r, C2 ~2 _( r- ?1 C! M
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 5 H3 N. f0 W/ F! g
比如,在我清除串口接收缓存的函数中,我加上了这几句代码:
; D% Y3 I, ]' v a) o/ X9 B: Q3 O+ w9 i) u
a5 d3 I6 ]5 W- \
2 Q, y$ @ {% a4 K7 B( w3 L这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 & E) d' F* u5 M" D& K
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断:
: u* z. }$ S5 \: o7 i1 U
, }+ v) E- i0 z! f% M2 S6 l. E- @1 \, f9 [ j0 W# ~- Y$ g
在这个函数中有这么一段:
/ q5 V! a/ H: O9 \, C. |& w x' r% R
& Q( m4 @3 Z; u6 l+ P- r
& }4 @) U7 k! \. ~6 L; `# _2 Q% F里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误:
U+ W* F& U7 J8 I ]
# a9 ]4 l- F5 J判断的语句如下: - if(huart->Instance == LPUART1){
* @- k& L8 o/ x3 k& u9 o - Enocean_Data++;
+ }4 |7 y& T. H1 F, N0 ^7 C - if(Enocean_Data > 98)Enocean_Data = 0;" G# ]: @" n E9 L1 h) ^/ L8 [( D
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
; @" `) m4 K6 A/ M - hlpuart1.RxState = HAL_UART_STATE_READY;$ L$ w* T/ O6 y9 e& H& _
- __HAL_UNLOCK(&hlpuart1);
; H# H4 C. O" |# ~* ~ - }
" \8 \4 D/ A* F6 ~2 ~& L - }
复制代码 % L8 V I- A x" m! H2 P
如有侵权请联系删除! _4 i# M2 Y( k, e
转载自: 矜辰所致
' y( j4 n9 R; s$ V7 D7 G- e* t |