前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! ) {% g6 g4 t% L9 }+ B
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序
* E- { |) q1 a% L3 f) E. I' F4 I - {
, n$ ~( G `" x: Z" Q - u8 clear=clear; //消除编译器没有用到的提醒
1 C/ T# }' g2 g- P) z8 Q4 u# u - * _: b" F' C \. X: ?! h+ l0 N
- //USART_ClearFlag(USART2,USART_FLAG_TC);# Q; }9 x. a! T
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) ! x( f9 V' x& @9 M0 @ d
- {
* ~4 F2 ~8 _- J/ S' h5 d- s - USART_RX_BUF[RX_Data++] = USART2->DR;% G6 E0 r D7 l& E% Y b) t
- }) U& G/ e$ h2 s* ?- X+ K- v6 S4 w
- else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) 6 t' z( b2 y3 T8 M- x
- {# y+ A6 v+ p, a# J" R1 Q1 L3 e
- clear=USART2->SR; //读SR寄存器 可以清空寄存器
; f# F( F" q5 Y: g4 o! ^ - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
4 W! C/ V* k }* `1 \) ?! I) p - ReceiveState=1; //标记接收到了一帧数据
: [% ~- D) D5 f2 _8 F: ~ - }) B" Q5 S$ Y$ \" ]: G+ ~3 A
- }
复制代码
+ ^2 i+ E% d; V! l+ H+ Z不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
& T0 ]3 o C0 K; [& P - {- e& y, J( [' j; f
- // uint8 i=0;
* f$ G& q9 t( R/ q - uint8 u8CRC = 0;
5 H+ ?1 w- f( C" G) ~4 M, b - uint8 u8Count = 0;
) O2 n( l- I) O8 f - uint8 TmpVal = 0;
; [' P+ c& w' `6 m. r; z - uint8 DATA_LEN = 0;3 S4 t6 B! W) @; b9 }6 E: J
- uint8 n = 0;# r. g- m g- f7 s+ k" M
- uint8 HEADER_BYTES[4];: ]3 ^$ B2 M N, u
- uint8 DatBuf[30] = {0};
$ D5 t9 k, s+ J' a - u8state = GET_SYNC_STATE;
: V* Q4 l8 F; z* A2 E - if (Read_pt != Enocean_Data){
9 [6 w& x3 F; J& _- J - delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等
7 x& i* \1 Q5 l' o5 A4 U - while (Read_pt != Enocean_Data)
; [* }- i u- x% K2 z - {
- @& \4 ^8 ^3 V" `5 }8 }5 e - TmpVal = USART_Enocean_BUF[Read_pt++];6 W- |7 J8 k& a7 e/ N% O$ x, z
- if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
9 G! ?4 K8 {7 b, ]# W - Read_pt = 0;
" P: S; N- e' o8 I5 ? - switch(u8state)9 N( S4 M( t; F( b1 A) w6 E- c4 C
- ...
复制代码
' S* I/ f, W: c3 C* V# ~自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){
2 B R6 \% l0 j# Y" [ - HAL_Delay(7);
* C0 c% e" T5 N* [, o - // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);* O$ r" X# b( Q
- if(Enocean_Data > 10){
# G/ W" q/ e+ [* _& c% l3 j- A - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 2 ], o- Z3 u. L7 N1 p
- }
) h1 l& C2 u- X5 L7 s - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 * G d& _& R1 R( R$ n. R
- Enocean_Data=0;' n. ], P5 q1 N1 V5 I" g+ A" M4 B
- // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用% c; q( P& b7 D7 t5 I4 Z
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错: @2 U4 T! Y$ K5 w% s" E3 L0 Q6 I
- }
+ _8 a8 f1 Y9 k3 U: J) G5 h } - }
复制代码
# X! y: u) V' N1 v+ f9 y在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
8 C a( I% i0 ]* V. H现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);' y7 U, _9 R6 ?" Q1 N3 N. D
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
$ j) t4 B( B/ h4 ~) F1 Z/ s' Y在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)$ {, p) C( S( H9 n
- {5 z! W, S, x( {3 K$ n, }+ @& X Z
- /* USER CODE BEGIN LPUART1_IRQn 0 */* V' L; Y1 z* V' ?1 j
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))% Q" @; m: W& {
- {
1 j5 ?& f! V# F4 W7 t - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);% h3 W7 _3 G: V# B
- ReceiveState = 1; 2 M8 R1 H) y4 p5 L
- }% W9 j, d4 w1 Q- e) v
- /* USER CODE END LPUART1_IRQn 0 */
1 C; J$ E: C7 Y - HAL_UART_IRQHandler(&hlpuart1);
9 k# K0 {/ G% { - /* USER CODE BEGIN LPUART1_IRQn 1 */
$ s! @; u: K* k4 q0 u+ g
3 K# d+ [ l0 D* d( N4 f4 X- /* USER CODE END LPUART1_IRQn 1 */
p. g5 B/ u& t - }
复制代码
- K9 v# F( u- o" a. K, S! n用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){# s! Z- p0 K" @% Z- h0 \* o" h
- ReceiveState = 0; * d" q& P0 Z* a
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
/ [$ i6 ^6 p' s. R; G - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
# v* t) {* O; {! w - Enocean_Data=0;7 ]5 A/ v: ?& l8 l
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错: e4 e* |$ |* n, s
- }
复制代码
% ?, e( \! k4 p% D% g* {; P结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。
% T- n. u4 A7 Z% }8 q
' _% Y3 k# c( K: X+ ~
6 @! `$ ^' f& h# y/ v% D实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
; }0 P/ W. t5 \/ J% b - HAL_Delay(7); //测试,重要: |; H1 S' ^. j+ R
- ReceiveState = 0; 0 `. e* I1 u9 i7 D* E4 @
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 : k) F4 }/ w- R6 c( G6 T' m X3 p
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 : ^" g, }% G) R2 Z0 d
- Enocean_Data=0;6 Y: Y, h: W: w: n* G2 j/ Q- w
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
& M# I8 ~& q3 P: {: R3 e* ^ - }
复制代码
9 Z. i( M; y/ y% j发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: ; R/ l: S) n6 U# P
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
9 s$ U( `5 U& ~0 J: R% L4 c这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; ! y! N- ]) u. z0 x" l# P
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)
5 n8 X( k% @: l- b! ]* S - {
) z4 [2 O8 O* ]9 k; `* o) X - /* USER CODE BEGIN LPUART1_IRQn 0 */( p, y7 b( I' @- u" R7 Q
- ( Z# ]# B: P4 s
- /* USER CODE END LPUART1_IRQn 0 */" I0 i( I; h3 H
- HAL_UART_IRQHandler(&hlpuart1);6 G2 N1 {1 ~ V
- /* USER CODE BEGIN LPUART1_IRQn 1 */0 G8 W) @ U3 y9 Z4 t
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
, ~6 d/ p H0 b - {% {. c5 v$ `6 L! Y+ _5 [6 e/ h
- usartreceive_IDLE(&hlpuart1);
" K H( f4 p& v/ p - }4 I7 W& z; o; w5 T4 J2 z
- /* USER CODE END LPUART1_IRQn 1 */
( x: U5 v$ |3 k- z6 I* n- e2 F3 h - }
) G n, C! g2 t5 b$ @, D
! ^' c" o( O" w3 q3 ~) }$ G6 m- /* USER CODE BEGIN 1 */
. A6 s' K% F i& |8 _) o - void usartreceive_IDLE(UART_HandleTypeDef *huart)
) \0 {6 [. I) A+ `3 l* M) ~* u2 k - {
, M$ N, F b8 Z* L5 i, H - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断
7 d4 T4 w: f8 S- h' r - // HAL_UART_AbortReceive_IT(huart); //终止接收
. g( U3 H% g2 f$ v Y( A - ReceiveState = 1;9 V! c% Z2 y0 J* p
- }
复制代码
- S6 ~- D4 G( g; q& |, |用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);
: @2 J( E; U, a- Q8 q - printf("send command !\r\n");. D, {1 S2 M6 z0 J0 n. o
- COMMAND_GetmoduleID();! S; n: e% r, l2 N! @
- // HAL_Delay(10); //7ms不够 9 k' r# e3 n% x0 `6 A3 K% v" \
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文3 h2 J" [ `; |0 }
- // while ((Enocean_Data - Read_pt) < ID_blueNum){
3 D" O6 O7 e6 a8 J/ S* _ - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
, `. e1 T* {* N5 a - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西; |+ e* f* H% B3 t3 C
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 * j/ m4 F/ W- ?0 G# G1 F6 ?. z
( j3 ~2 ~; s/ D* x& _7 ~3 ?: S) d0 ]
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
! x0 M @/ C( Y. Y - printf("send command !\r\n");
% f, C; l# ]5 P - COMMAND_GetmoduleID();6 s5 R" @* ~& i( ]
- HAL_Delay(10);//7ms不够
+ |4 l: k. {6 t4 {8 d% r+ ^ - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文5 ]( S/ x3 `6 r* p5 r
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 # O! M1 }$ ~; I$ A# p
# T" J# a8 P: |) M3 S6 s9 P& @5 Q 测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
- T8 Q) x- G" O1 B% C( b7 Q/ r - printf("send command !\r\n");% V9 N8 k3 S; e; n" A) b* r
- COMMAND_GetmoduleID();& w" G+ X, L4 ^4 y7 C
- while ((Enocean_Data - Read_pt) < ID_blueNum){8 K B; r# E2 C3 F* m
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
! g+ F0 g! w# e9 Q+ c - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西8 I0 a4 T$ t- m7 W
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
( r( z" z+ p' _+ m/ Y' D4 @+ k
0 Y5 E( v- i; u, h 测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) & b0 f2 r6 \; M9 \" B& @
- |$ [" n" m$ d' |7 K2 ?( L( D r* D$ j+ i% B
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
5 z8 m7 E% r0 t5 c, E4 s - printf("send command !\r\n");
5 Z$ v6 n! p8 \4 G3 p' I - COMMAND_GetmoduleID();. p$ e3 w" V- g* t" n( G/ l
- while ((Enocean_Data - Read_pt) < ID_blueNum){3 H7 A3 k3 x: N' Q9 C8 x" h
- printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");
7 d) L# Z5 R3 r" l6 P - //HAL_Delay(1);//延时可以,1ms就可以
0 L9 y2 J9 U2 E - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西2 B+ x( s# z$ M; Y; v1 I; K5 Q
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
# u# ]4 `) h& q m Y" d
$ h. w1 Y: m. d# s i
/ b- f5 S Z( J9 L
& B# {( E& M+ \( y9 X2 Q/ |( [! m2 j
/ Z6 Q% G) |- Z! o) a" v6 @) q# T
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
. ~. L6 x. x0 ]' u/ M1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
4 y4 E2 p* w# {$ O/ O1 V+ w - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据" m3 D* Y# U( ^$ {
- typedef struct
* g# `2 ]9 Y" k2 M8 | - {
6 O" G% i) n) { K4 f/ a a - uint8_t UsartRecFlag; //接收数据Flag: }8 b" B. Z$ k0 |$ x0 Y
- uint16_t UsartDMARecLen; //DMA接收到数据的长度' L5 I+ t2 Y2 E) \2 T' z6 U0 s
- uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
9 A! K3 S) Z! O3 K - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
2 e: O! a- x6 H1 p6 y2 G - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
@ n1 J9 i6 d9 @- h* w - } Usart3DMAbufstr;
) G4 \& ]4 U" w0 M - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void): e1 J2 A5 ?: w+ L
- {
! ?1 n/ J3 ^% c! r7 {/ {3 ~, W - /* USER CODE BEGIN USART3_IRQn 0 */
' O9 L! e: M$ y" _: [3 J - ; B( K1 r8 {5 P; S9 ]# E
- /* USER CODE END USART3_IRQn 0 */
. J9 c) u- f2 }1 F( K4 l1 k - HAL_UART_IRQHandler(&huart3);
6 N: H# \* @" Y4 @2 U - /* USER CODE BEGIN USART3_IRQn 1 */' d1 s! u6 G+ X
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断2 N# B! d3 q$ [( k9 Q1 `! c/ S+ Y- t- r
- {
2 B7 l3 R' {. U% v4 T: {0 ~ - u16 res = 0; 3 l4 n8 v! t$ u9 y
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志% ?! u$ X: `! [6 f/ \2 Y4 w# W
- HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断
5 v: ?1 Z8 G- C! Q" u - res = huart3.Instance->ISR; : K' K/ z8 s k* ~$ f- b" L6 a
- res = huart3.Instance->RDR; . r& x7 D+ w9 q( } j8 M
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间
3 x1 ~4 R6 d7 }6 S5 D# l- s; u - Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 " M) J- E) d A
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
) I: U+ R$ Y$ N0 |9 I, ~( z - }( E; M, w) C" O1 q" @/ S3 y% U& @
- /* USER CODE END USART3_IRQn 1 */
6 n0 T+ Q% `- ^* P( Z& E - }
复制代码
% l4 x9 T: o: g) O! _数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart): X5 x, }. x0 a3 n3 S7 @# h, p! L
- {
$ C; Z$ c) ~. W3 f' h - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线* Z' m* l( L' K* L. x& {! ^
- if(huart->Instance == USART3)
X% g4 G9 j. M* E; E - {
+ \8 l$ |* g% e* t H - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 4 _6 m4 q, K& B& s# |
- // {; c! X4 H* }. y
- // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延$ v3 b$ [9 K) z# P, t; H, x( @
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 6 c0 s4 _2 [5 D+ Z
- // }
; _1 j0 W3 k* w1 C* `: ] - // else
5 p4 `6 m6 w1 P1 b- c# D( Z - // {
5 B4 M4 E2 S& R- i( D - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头/ c/ T# a$ ?2 p* G1 {$ F
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度 0 c: d0 P' ~% U" O
- // }0 O- v$ O1 O6 r' S& ~7 g
-
0 B2 b" ~+ L7 a% f - memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
B, \; z1 v! W- c T - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
+ H4 y- X/ T& j6 I" K1 Y: K# d - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收
& i8 Y, z; t" P" Q9 v; q; B - }
+ l+ X( d; T, Q: a1 Y - }
复制代码 6 Z! s/ y4 k1 p& V- V/ `
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据. N8 z5 l W2 F4 o9 F' r8 g! [
- {7 x7 x1 Y8 V( q; @: Y8 N
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
( C$ ^, x6 J& J7 D! Q+ y- A - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
- A0 `7 Y: W& _9 K( I* x - Usart3type.UsartRecFlag = 0;//标志位清0
9 ]0 [5 |, x9 \# f) b! Z1 o - Usart3type.UsartRecLen = 0;//已有数据长度清0' _: e1 Y( A) ^% d
- }
复制代码 ; g6 _" a7 Z8 e" R- v2 A r" C
1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
* h* k4 |( E4 ]! K; D) C二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图:
8 v4 t3 X( L! M1 F4 _
2 P8 X; W# q0 [) J; _出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
2 R! ?8 L3 c& V) L+ Q, g% m4 q+ x- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志
4 f9 t0 _1 B. g% e% C, F, F - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);: y7 a( |7 A& a* g' ^7 Z
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);/ d# a/ @* D8 g
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 r3 ]0 U& ]: O6 v
比如,在我清除串口接收缓存的函数中,我加上了这几句代码: ( z) |. i8 }9 X& z q$ o
' H: n6 @8 o- m8 z4 a/ X
9 o# H# i) Q! V
" W, G* g0 A) W6 k" s! `
这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。
+ _$ v$ ?! G8 p' _2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: % s: N7 A4 M% C
# s* P% F. v) L& U# C+ t: _: _* o- |# X# h! H; G' a+ E5 O
在这个函数中有这么一段:
. m7 J; m$ v# O7 l4 | [0 n
- A) _! J1 {, ?0 ~# l& J ]
. [/ u$ ?4 E" G# y+ @4 z
6 S% ~8 t) d! z7 B5 O里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: 0 w6 s+ j4 g- c8 u
3 m' l1 z! M4 D
判断的语句如下: - if(huart->Instance == LPUART1){, E: x( ~. p6 N: `4 {5 I# L" V7 r
- Enocean_Data++;) u8 s0 z6 \' F* k1 J
- if(Enocean_Data > 98)Enocean_Data = 0;( b* a4 L) [) w# Y1 }5 I
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
" v0 i+ b# x: ?& Q - hlpuart1.RxState = HAL_UART_STATE_READY;
1 M" ]0 Q1 E1 Q: t+ _ - __HAL_UNLOCK(&hlpuart1);
% f1 j2 \9 c, `5 p - }
4 {- v# T K# ~ - }
复制代码 2 `% |" U. H; U `& c/ ~
如有侵权请联系删除
* k. U' [: @' b* b. q 转载自: 矜辰所致 6 A) B$ v2 M/ G ^. C% W
|