前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! 6 i( o& k+ B$ i/ f0 x
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序
/ a6 k7 G$ {" m0 M: k4 G6 U - {
& v/ r+ b, h; J% Y - u8 clear=clear; //消除编译器没有用到的提醒3 ~( E8 f+ P+ C u; e+ e- |
6 |, J0 z; Z5 _2 R7 }" C0 l- //USART_ClearFlag(USART2,USART_FLAG_TC);9 r9 ?8 \, Z, {; q3 o
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
# L" [" l. q$ F6 z - {+ T4 k2 L- G- @8 t+ s
- USART_RX_BUF[RX_Data++] = USART2->DR;
6 @5 ^, O/ M# {4 Q( F - }
* F# O: {- `4 w7 p - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) 2 f: k8 B8 z0 l3 s
- {
2 s2 k4 i9 E3 m - clear=USART2->SR; //读SR寄存器 可以清空寄存器
- E6 e$ T1 T. l% i - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断) , N! z B) V2 R
- ReceiveState=1; //标记接收到了一帧数据+ b' D$ M7 C) |! i1 Z3 _* ]
- }! ^; u9 D% t) A
- }
复制代码 O! p: A& N# x# u5 A+ V
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)0 z7 m; n( k9 |, R, a
- {
. C1 c: U0 ?7 L - // uint8 i=0;: o7 a' v- L% g; \* p2 L) \. N
- uint8 u8CRC = 0;
1 t0 r- h. d" `& c9 w; p3 Q - uint8 u8Count = 0;
# G, D+ v4 j( f8 H# t6 I$ {# } - uint8 TmpVal = 0;
1 K9 p7 J4 b% @, G( F2 h0 p - uint8 DATA_LEN = 0;
1 i; i2 W. I0 [% z: M# l - uint8 n = 0;
* H) U( S/ c5 C n6 f% x! p9 O' o - uint8 HEADER_BYTES[4];! [8 a% F/ `. }$ m
- uint8 DatBuf[30] = {0};4 z. [8 k% m/ t5 F1 j* H
- u8state = GET_SYNC_STATE;
/ @7 q1 f# S; C - if (Read_pt != Enocean_Data){3 h9 D- w" R! g0 O, i- v* _$ s
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等$ k, h( p$ x) |, w
- while (Read_pt != Enocean_Data)6 t9 P/ |6 L3 N- L w+ x( J7 H8 s
- {
& W) O8 w, A0 L0 ~! g - TmpVal = USART_Enocean_BUF[Read_pt++];9 g4 ?* b5 B4 v( b7 _; P5 C# g
- if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代9 P+ Z: t6 G W7 ]) h s
- Read_pt = 0;
$ |1 C& y% n; B I9 v - switch(u8state)- i; G3 V5 Y! w( k+ I& J3 O
- ...
复制代码
' ^& G$ Y) p9 u, X T# N- h自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){
; ~: ~( M% O: L. ]' [ - HAL_Delay(7);
7 K9 ~8 \0 X& w3 I( E9 P0 _ - // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
% W: x3 U/ U7 p - if(Enocean_Data > 10){
j9 U5 H4 K" ]; j* i4 Y M - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 ) q% B& y: }+ J2 p% L
- }
& E3 s. B( p. x0 P3 {) A - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 ; x" d P d, Z+ x) N. u
- Enocean_Data=0;9 l Y- B+ v" F2 |1 ]1 W8 N
- // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用9 x- ]- v1 D8 F
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错! E- h, I2 m0 W+ V8 Y
- }
. u+ B: P h& e9 X- \ - }
复制代码 # z6 N: h& B% H6 c
在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
/ ]$ [7 o. b3 n: T: O6 N现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);- o O8 ^; Z- @! Z5 W
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 : q g5 o. {- g
在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void); w4 l+ x' G- i, x& u% w2 o
- {
j+ z1 r5 W4 T - /* USER CODE BEGIN LPUART1_IRQn 0 */
, e5 l" D9 }2 ?" d: z& s# Y( a& ] - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
& b( V0 X6 y$ V) Q7 c) m( k5 T - {
' o8 p7 M1 ]- m7 | - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
F: `, i' R) E: Z- X* x - ReceiveState = 1;
, \$ ]' |) i/ S, v9 c - }) ~; t& W6 i/ X
- /* USER CODE END LPUART1_IRQn 0 */
8 S' Z `4 r6 @+ K - HAL_UART_IRQHandler(&hlpuart1);# V. a3 H5 {3 Y* E, O; ^% f
- /* USER CODE BEGIN LPUART1_IRQn 1 */
5 Q% @- n' B& A - " Q* e3 P7 j8 {8 P' ~6 M& I
- /* USER CODE END LPUART1_IRQn 1 */
) J6 ], D( v/ Y0 C/ d - }
复制代码 , a* R! p, i3 D4 U: [2 m
用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
7 K/ I5 o6 h, F3 P1 Q" f - ReceiveState = 0;
9 C j% A7 o1 C; [; R( p - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 0 t3 e. j( `; A/ b
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
' ^' i8 X+ l$ n. A( W' o- _ - Enocean_Data=0;
) N$ R8 M* j; n6 o - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错 A& F6 ]1 C! O
- }
复制代码 # k9 H7 C/ o( L! d6 Z* v6 Q
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。
8 _' z9 z( R. `
7 G, C; _$ H5 ]0 ]# x9 t# B
- G2 @& [. ]8 n% O, l. I5 J实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
+ `+ z4 W! F# m- F9 o - HAL_Delay(7); //测试,重要. P% s! q. p6 E1 I* s4 B, x, x
- ReceiveState = 0;
0 |7 Q8 e3 C+ `% I' W, E1 w - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
5 B9 r( ^$ a1 a) ?& d+ J - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 ! } K- ^: |& v
- Enocean_Data=0;) F& u! P! p! w r! M
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
( n5 L! z0 m0 u: k - }
复制代码 $ Y0 s5 p: I1 ]+ N. f# B) p5 u
发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: 3 C; o: R% g% i
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
$ A- o! M9 X* S9 l V0 j这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯;
, I' {- h) k" Y7 g# K2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)7 }) t* V0 d4 n1 I$ [" A: K
- {; P A1 w [# D- T5 p+ M3 e: x
- /* USER CODE BEGIN LPUART1_IRQn 0 */
( X$ a. M2 j; D- q, A# n! J1 M - 5 K0 \& y. V f: u# Y+ }8 l, o
- /* USER CODE END LPUART1_IRQn 0 */- l7 H V) C4 G3 B
- HAL_UART_IRQHandler(&hlpuart1);+ A6 v( Z- V) N/ r3 a
- /* USER CODE BEGIN LPUART1_IRQn 1 */
8 F% I$ k0 m, g6 U - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
2 o3 ^8 i$ d8 G \7 R - {
# r6 \# {/ Q/ J/ @3 o; w8 K- F/ L c - usartreceive_IDLE(&hlpuart1);
; U1 y# h W' T5 a5 Y/ r4 } - }$ h& y* j0 l/ \
- /* USER CODE END LPUART1_IRQn 1 */0 v' H# _+ [8 q' V3 m
- }2 [5 g U% `8 ~/ G
- 3 U( c5 H. n) \1 ^
- /* USER CODE BEGIN 1 */
+ `5 b" N" r1 I5 L! O. W7 Q - void usartreceive_IDLE(UART_HandleTypeDef *huart). n9 l" i9 _/ _$ a: x3 A. o# O
- {
/ W; s5 {* ^& |8 x1 E6 e - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断
* c3 T" p' g7 g; d2 } - // HAL_UART_AbortReceive_IT(huart); //终止接收
9 ~6 @* r! q: q - ReceiveState = 1;4 E ]' f! I' \; D1 b
- }
复制代码
6 i! @( u- l3 m& e! h6 F1 {用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);
" w! N' Y3 \/ [5 b2 T+ b - printf("send command !\r\n");
: E7 Q }% }7 S8 ~5 L" [% V" X% D - COMMAND_GetmoduleID();. X! P! j. z i" l5 r8 I
- // HAL_Delay(10); //7ms不够 - B" C9 f) c0 ~1 ]" W% d/ _) u( r
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文& _. i' O. F( G7 w
- // while ((Enocean_Data - Read_pt) < ID_blueNum){
+ J" e1 ]) [* j% j* O; Z - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
9 X! A X7 w" [9 x& M, s - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西6 @! h* H E' C3 g! ]' T% g
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
# M! F" O( J' j+ p1 o$ ]
/ L! K& M2 C& ?7 Q* U& E如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
0 Z( M& t% }& g& s g0 N$ a - printf("send command !\r\n");
# D& E! Z# X+ A2 j) `% [# W - COMMAND_GetmoduleID();8 m/ b, E& T0 n( [- J
- HAL_Delay(10);//7ms不够
. [ s4 p5 Y" w& J# N+ ^( O. G - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
" N/ {2 P# y: h7 v6 f4 n( e4 A - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 8 _& J/ t! s" ^+ X8 z3 H) R
- |# ]# H: r- U, W. f6 M( x& h
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
! S/ k6 s; k$ j: c2 S9 A - printf("send command !\r\n");' i5 R0 W S2 K1 G. \' l
- COMMAND_GetmoduleID();
% c% v5 L' _4 D3 Y5 p - while ((Enocean_Data - Read_pt) < ID_blueNum){( m" }, o" u6 e
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");& X( @( s& {& N2 F9 o1 @) J
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西6 P6 o& S" c3 u4 D
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 * R$ N3 }' J+ e1 `7 c9 K. @
& o+ g4 q3 n: u' D& ?- @ 测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) 6 @& C3 I0 r$ ?% X
- p/ }# U( _ e; ?
6 J2 _7 M# b3 _
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
& ^' V( y0 |6 i' E - printf("send command !\r\n");0 `9 G+ @6 V! ~( h$ h# k+ }5 v
- COMMAND_GetmoduleID();# I! ^0 F$ y+ J/ g7 k9 [. f m3 R9 p
- while ((Enocean_Data - Read_pt) < ID_blueNum){+ G; D3 f7 j: n% O4 E- u5 d
- printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");, O! y; Q1 E t1 P, V" O7 k
- //HAL_Delay(1);//延时可以,1ms就可以 F+ g1 b% a+ ~3 Q3 O
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西0 U# @7 r* d m$ `& Y6 c
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
3 k; l" @9 D2 N" q T1 a; t9 \1 m* [/ U7 n7 x# m0 [* }
; i5 Y7 L" }$ I/ j8 _! e' R: k
- c% e$ w b# w: Y- J, l
8 B9 ~" Y' ~5 u! M I
* e6 B/ T Z6 G经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
/ r+ s- Q7 X# i$ l1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
$ d4 K& F' C/ u. v6 r$ l - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据1 S6 R* a% E% `( d5 i+ D# t* P
- typedef struct- s; Q) t4 _& c. r) O
- { ; P# Q, V0 F5 L, d3 k8 j; M, Q2 M
- uint8_t UsartRecFlag; //接收数据Flag% l+ L8 E- e$ E4 q
- uint16_t UsartDMARecLen; //DMA接收到数据的长度
* a; W( W7 y9 I, s+ k2 P' E" g - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
& N6 U5 g q1 ], S; Y, X# ?" ^ - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
7 W/ V! h a7 m+ _# }! h/ Z" ` - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
; {. ^+ Z4 t8 p$ P! U' Q! ^* H5 s1 Z3 M - } Usart3DMAbufstr;, v5 M% x& J" n
- extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)# h& q, d9 B: W: y2 Z, M |4 z
- {
+ ]6 e$ j6 h0 R/ F+ @ - /* USER CODE BEGIN USART3_IRQn 0 */
7 x# L" f$ G1 g% f: P9 F/ } - / E1 R8 H z2 Q5 F9 q- V
- /* USER CODE END USART3_IRQn 0 */ D; d1 S1 i2 M0 c- L8 N! h
- HAL_UART_IRQHandler(&huart3);( }, e1 W$ Y% j \+ b" U/ h
- /* USER CODE BEGIN USART3_IRQn 1 */
, D" j" @$ _1 w3 M - if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断
7 K( k @2 h+ f1 d7 E5 c - { 8 G% t4 d( B( P B a5 J1 o! z
- u16 res = 0; 3 C6 d) T8 j4 x/ M, N
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志+ p% c0 U1 m; F3 i9 y% g8 V
- HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断
4 L9 N3 c& z( ~9 f2 V5 n - res = huart3.Instance->ISR; / {1 B7 s" I) ^ S( o3 ]$ @
- res = huart3.Instance->RDR;
' O5 M4 B' Q& p - res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间
) }6 T, ~% }1 n% p4 G, C - Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小
. w# o& ]# }- a2 S6 c - HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用 S. k: B3 v; I+ c
- }4 `/ k% z! m7 R' o$ C p
- /* USER CODE END USART3_IRQn 1 */
& Y/ y4 P0 @3 i3 p8 L1 C) z - }
复制代码
+ ]4 b' I9 Y0 u! F0 o数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)! j$ ~) \+ v B! v* J
- { ' B2 o$ R: x; r) l, I/ E1 o
- //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线
7 M- K+ `! I" s' O7 |# R - if(huart->Instance == USART3)' \4 n+ [$ {& l( J' H+ v
- {
9 m& o) |/ o9 H9 W4 j$ v9 Y/ \& m - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 ~0 z; y Z( l! e) \
- // {4 E5 M( F' |" P( _
- // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延* N; E& |+ y/ l9 |
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 * m, {: P1 X' F
- // }8 Y6 H4 ?* c' S- ?4 y3 y
- // else* r( ]( b7 \0 r/ O5 R) e
- // {
8 o, E/ z$ v. K1 s - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头; \- }2 ^0 Z1 H: q0 x! H
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
6 o% z/ T$ Y9 \2 V - // }0 ~4 k( z' E" H/ ?* P8 n/ k; U
-
4 d, A3 y1 M1 I* ?$ P* d8 o - memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
5 S, ^* i2 [* X1 D* ^ - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
+ i% ]/ Q5 }8 X9 @ - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收
9 t4 F' I% ^& ^- b5 W% [3 H3 I - }
W& v% Q8 V5 w$ K - }
复制代码
! @5 _+ O7 k) D: D0 C8 P0 z. U在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据( t$ ?8 O9 \- p0 c4 F; t w
- {" ~1 m" E0 @, H% | V& B: X
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口14 R; X" L! v- O @
- memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除) ^6 t! [& v1 K9 q1 f9 c1 t- e
- Usart3type.UsartRecFlag = 0;//标志位清0
! g: ~4 v% `' w, e }* ~7 W - Usart3type.UsartRecLen = 0;//已有数据长度清01 J9 H# L. R* l1 _6 {; a
- }
复制代码 % O' s4 ]* |7 J9 |1 T7 u% ]+ H% v
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
. r$ X3 j6 A+ {; M, z" q' Z二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: ' `' B1 z) W1 O0 D* f* L
+ p6 U: D& w$ F. {5 q% v出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志: : @ V( o( `6 H# K6 I }
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志/ s) W3 D# V! Y, U, P
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);# A$ Y3 D- m5 W6 _9 ]; L3 @
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
, j4 p g8 G! p3 l: M - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 . K0 ?+ ?& _4 S$ V2 Q
比如,在我清除串口接收缓存的函数中,我加上了这几句代码:
( I% q9 F$ _' P8 \) A; l1 a d, Z8 ^4 W; w$ U
i; I8 ]+ E" R, ~* G
2 D( F( y. e4 ^& u5 _# E
这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。
( m1 w9 W+ B7 [# V# {( n2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: , W8 k& u0 g4 D) V/ h* R& n# P
- y3 L% S7 I8 I2 A6 t9 v! |, j- E7 M8 _
在这个函数中有这么一段:
% v& A& E% E! Y- o. H/ W1 W6 W( X+ R: u
6 {& ?5 _ P& c. ?8 \. l
; H' j5 t# F2 q- j3 M' I里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误:
" r& n$ \1 ^7 l$ z
) I- ~) S( c9 c
判断的语句如下: - if(huart->Instance == LPUART1){
* c! i% X: t. A! H7 P* Y' e - Enocean_Data++;
- p/ @! ?2 K* {( A; ^: z - if(Enocean_Data > 98)Enocean_Data = 0;
; S( ?5 n' g% F - while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
3 Q8 R. `- A9 r/ j9 E; U - hlpuart1.RxState = HAL_UART_STATE_READY;: E. A8 {, q/ l% x. n- a5 i
- __HAL_UNLOCK(&hlpuart1);
' [" ^: g$ q( f z/ y/ ~/ F' x! X - }
2 y, w) n& n; l - }
复制代码
L" {: K, a% m% `% y4 J& N如有侵权请联系删除
1 O i% K: o9 J0 e 转载自: 矜辰所致 . G& h5 Y. E Z3 M: f
|