前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考!
U" P9 C b: O一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序3 Z$ k# t9 y/ K' \
- {
, L4 [: J3 M# N) { - u8 clear=clear; //消除编译器没有用到的提醒
* I2 F! x% E Z5 U/ `2 N) J1 @1 D - 0 p, a9 V. q9 @) @) ?" w& A3 s
- //USART_ClearFlag(USART2,USART_FLAG_TC);( a+ H( ?; ?7 }4 b% W& i8 u, i
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) ! x7 S" H3 |" d6 P' S4 c
- {
% @. L5 r. _/ u S. V; R! [% M% V - USART_RX_BUF[RX_Data++] = USART2->DR;/ D2 b6 r$ o5 f* l$ a2 _
- }
7 I! |8 m# ]; O5 I% O* P' w3 e - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) ; o( W2 V5 G, `7 H2 {( _, p
- {
" M" u7 @/ {4 H- o' h2 @/ y1 [ - clear=USART2->SR; //读SR寄存器 可以清空寄存器
6 i+ I/ K& X& ]+ e! _, ]3 T - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
& j0 V% V0 J0 [ - ReceiveState=1; //标记接收到了一帧数据( q5 B# S. w6 l% h& ^ L
- }
0 m* H( [5 X1 u& g - }
复制代码 : q, m; V2 F/ O0 z+ R
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)8 O. V# M7 Y6 |; d
- {- i a0 N% b! S: h$ | A" z% f
- // uint8 i=0;
8 _% J, [' ]5 z$ V! ^- R. S - uint8 u8CRC = 0;: s- t4 C7 ]6 \* J
- uint8 u8Count = 0;
4 J) I" i% j7 ~" Z$ p5 g$ f - uint8 TmpVal = 0;
/ d- r3 U) m0 I, A - uint8 DATA_LEN = 0; I: c0 ?6 K) r
- uint8 n = 0;
* W8 t7 g/ q6 y0 P - uint8 HEADER_BYTES[4];4 H; X- u" N( I- J# E& z, {
- uint8 DatBuf[30] = {0};; v% I5 u" l. u/ r) C/ W( L: J
- u8state = GET_SYNC_STATE;
F4 \$ P4 I* }7 Q+ i - if (Read_pt != Enocean_Data){/ a: A! a; ~3 B( ~
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等 z* V8 g0 S3 ?) ^5 N
- while (Read_pt != Enocean_Data)$ o0 H$ p) w. P* Y4 s4 }/ i
- {
% F' l/ O6 f2 M6 ~ - TmpVal = USART_Enocean_BUF[Read_pt++];
8 |- A6 q9 C+ }# c& N - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
3 a. t; U. l8 O d: E' ~ - Read_pt = 0;
: g' e) C+ ~" U& v6 V* f - switch(u8state): N! N* l0 g4 G& l9 _; {' v
- ...
复制代码 7 F! w, V1 ^: \- {- V, j
自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){0 K; @0 W: A1 o, h# J% w5 T
- HAL_Delay(7);: X9 Y8 H: H" x7 `8 X
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
% ~7 [7 F3 h3 f/ D0 \8 f - if(Enocean_Data > 10){6 C& K( e, A$ t- B2 B" }% t
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 5 E/ H9 j, v5 r
- }
# U. v, ]$ B$ E3 s9 A - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 ' X# H) k+ \6 A$ N# s4 M* }. n( ~) _
- Enocean_Data=0;
U r$ O1 `' B# r! k6 L8 _ - // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用/ f# D9 i) {$ g; ~4 G* l2 ~. S
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
4 R4 [0 f( A8 J! W( v4 \3 W - }
, W& g2 Q* m3 A1 B6 y" z - }
复制代码
) F) O. |7 U% {# D& W L0 z在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
3 q, v' k# `! W9 H现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);- }4 n/ v5 q" z, u2 X2 B6 M
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
. l" C( q6 ]0 k' w% A! `9 D在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
# g7 T5 f; j9 V/ t - {
" `( E( s2 P" K. h; l/ Q3 Z4 B! w - /* USER CODE BEGIN LPUART1_IRQn 0 *// r# g, `- c& n6 Q
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
) c" f: B, c" S8 q - {
( d/ f4 i0 G) b. b* K' W9 f% D" ~ - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);/ {/ ~) ?- q+ M* k- H) ?2 ~. `
- ReceiveState = 1;
) a7 q& F' M5 \! B - }
' \5 C# I" C' S A7 ^ - /* USER CODE END LPUART1_IRQn 0 */
0 K d) y9 s$ ` w6 b8 C - HAL_UART_IRQHandler(&hlpuart1);; }4 G: a6 ]% y
- /* USER CODE BEGIN LPUART1_IRQn 1 */
3 K+ q. ? |) x5 ~: h9 E - , h' J, ~: n( N( ~/ I# K/ l$ x
- /* USER CODE END LPUART1_IRQn 1 */" x/ k) O1 e' Z# ]) ]' O2 D
- }
复制代码
/ b" G4 D8 c$ P: d; N( Z! J3 F8 J用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){) X0 X, r" d3 g/ v
- ReceiveState = 0;
: u& w0 V* j, i1 T% V5 V0 ~9 T - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
, W' O! C4 n0 o5 N* r - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 7 D4 ~5 b7 X' [9 T2 F4 d8 m, y
- Enocean_Data=0;9 d2 v: Y+ K& I0 v4 t' A4 ^; [/ `
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
3 G! W, K+ v) J8 a. {. R3 s7 F, e - }
复制代码 / h" r( I2 n: o9 G% b9 T- |
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 9 F0 J* _- j! c- R8 R- H
0 T( q4 P/ o9 J$ {
; @/ \( a9 {1 i& H
实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){4 Y$ }4 w: y: @% F: @. K
- HAL_Delay(7); //测试,重要6 u* z6 H4 o4 Y+ t) ?; J E
- ReceiveState = 0;
1 e- {" ~& y+ _* n/ U - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 2 ~5 w0 J( o6 p1 \' u6 |8 A
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 " o$ w/ z% I' ^# [0 U
- Enocean_Data=0;% F4 P9 W* w0 r# n9 b+ r) o, c
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
% V' D. C# J1 @$ Q4 { - }
复制代码
3 C$ P% e0 u% Z$ _, K发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: % X9 c/ h1 _: I F- \
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
/ V8 X' e7 e7 O% g( n( C- X0 h! _$ l这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; % R' P, T. ~9 g# a3 g
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)
$ b2 r. Y9 Z; S2 h+ _" l. f2 K - {' Q6 h2 E/ J2 s8 T
- /* USER CODE BEGIN LPUART1_IRQn 0 *// K( w4 u f9 }
- . ?7 ~+ i( ~, @: {$ ]0 b
- /* USER CODE END LPUART1_IRQn 0 */' O. _: s9 v3 O+ o! g1 ]
- HAL_UART_IRQHandler(&hlpuart1);
$ k) j" p0 c" G' [8 m& ~7 C4 B - /* USER CODE BEGIN LPUART1_IRQn 1 */
7 G% d3 A* r0 I9 r! Y - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
! s, l+ I \1 H; w$ V, a: w* J - {
/ W Q7 Q# G7 f% m - usartreceive_IDLE(&hlpuart1);6 q* h2 P; L& v5 r
- }2 @# s. H1 {/ O6 m( ?4 y# W' V9 [: Q
- /* USER CODE END LPUART1_IRQn 1 */
4 S ~! d% E+ V* P) M - }0 k+ n" w& y+ f- \& v1 L
$ D# h$ ^5 T& y" D9 j- /* USER CODE BEGIN 1 */" l( A) g, J9 s+ s( P8 w8 C# J J- N
- void usartreceive_IDLE(UART_HandleTypeDef *huart)
$ A5 ~4 R% h1 x- w% i - {& j% }: q! ~ k+ S, ]2 S
- __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断" J6 L1 j' @1 n
- // HAL_UART_AbortReceive_IT(huart); //终止接收 K/ p2 C3 r9 [# {! X# S
- ReceiveState = 1;: W4 E$ |0 h2 s
- }
复制代码 ' ]8 o7 x4 x v* k1 d- U& Z
用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);
8 ~9 z" Z, G* u: G I1 r% _$ P" W - printf("send command !\r\n");
) U5 F9 U; S) Y! m - COMMAND_GetmoduleID();
. n+ r$ B5 I4 n! x - // HAL_Delay(10); //7ms不够 / D% z# x/ {7 r+ k1 u2 ^
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
+ G. ]1 d" Z/ Z+ A8 V2 g$ V* p0 H - // while ((Enocean_Data - Read_pt) < ID_blueNum){
T8 m; g9 G( i, S - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);3 R9 `) b- C, Y: w( T3 \
- // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
2 I9 O; s8 J6 l# D$ o" B3 d' z# ^$ l - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
0 j/ E5 H& N& a# w { U
- ^/ W v% M: Y3 t
如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);* C! h' v6 k# [
- printf("send command !\r\n");
6 N3 t9 _- X. a. C& f- x - COMMAND_GetmoduleID();
! u2 b L0 o: ` l - HAL_Delay(10);//7ms不够
5 {# M9 x, @; W: i! ` - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
# y! @3 L. ^. v z0 _7 q - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 # \- K, h+ b8 L" f* Q# |3 Z
8 t6 p. Z- ^9 y- k: r
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);' ~7 H G$ C4 _1 L7 O `# g
- printf("send command !\r\n");/ s- P) X* ~0 ~- P; G- q {
- COMMAND_GetmoduleID();
' H) _( a- k# F2 @( B - while ((Enocean_Data - Read_pt) < ID_blueNum){! q! N" e0 O3 n
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n"); n; { q7 {! w# U0 r. R4 ]
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西1 L7 U, I# d: M- j# [- B
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 4 h$ J9 |- r& M! _
- E: `) a. X0 @+ X3 F
测试代码3 , 4(不加条件随便在等待中打印 , 延时 )
$ f; Y7 H. {" q ]# {, v
2 C: p0 k- }* K1 r. g+ G
' L4 i9 J" x- {' V8 b' n- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);9 G4 U, @1 J6 x$ U: E
- printf("send command !\r\n");2 v B$ t) V3 ]
- COMMAND_GetmoduleID();/ |6 ]$ H0 ]) M0 ^1 C8 U% K/ f
- while ((Enocean_Data - Read_pt) < ID_blueNum){( E; {2 @. W4 x* ~) C7 _- c
- printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");
1 g$ [8 z+ _: e5 `6 e; U1 Z - //HAL_Delay(1);//延时可以,1ms就可以
( a" n b/ D' x$ O$ u8 L; @ - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西- C9 \. n0 [- c" H
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
! x3 b- |1 w3 q
$ M! f( O, ]* H5 |; Y \3 K/ M U) q7 V. A
3 Y6 c- m8 L& }+ o8 r& f* s( q
' f( R$ O5 |. j% k# ]
1 E7 X/ J5 L/ Z5 _' z经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
% L, s8 m6 L2 K6 g7 K* d/ `: v1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
' m# i+ |- c* F9 g3 m - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据; f/ [! n" T8 L7 s) j R6 W! K
- typedef struct
: M8 o& s5 J2 J0 P8 a - { + }/ K* b8 ]5 V! D* V, X W" M
- uint8_t UsartRecFlag; //接收数据Flag
$ ]6 e/ H* x6 l8 J1 V- y* f" T, C - uint16_t UsartDMARecLen; //DMA接收到数据的长度
8 l2 V _( L& ^9 d2 u3 M; H - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度
3 D0 Z4 B3 I/ _- C; p - uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
5 C" j, u2 @2 h( O - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
8 R4 A# B7 w/ ]0 D6 B) [ - } Usart3DMAbufstr;
4 o$ N) V' [' _, W4 n9 a - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)' @4 B8 l2 }6 [
- {
! W5 |2 Q5 l( p# i4 C$ a: ]" W - /* USER CODE BEGIN USART3_IRQn 0 */
# N4 Q$ L, A2 @# R - 3 v( U8 T" \: ^
- /* USER CODE END USART3_IRQn 0 */( u2 P- R% D' _) B2 C
- HAL_UART_IRQHandler(&huart3);
+ E s/ f; ^7 Q; h& n - /* USER CODE BEGIN USART3_IRQn 1 */
, D5 M7 w! x5 A1 z - if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断9 t+ Z! c( ^( ]# `
- { 5 J& g: l6 K R+ _) F+ J" Z" \, J
- u16 res = 0;
( n; G8 ` f9 O% B - __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志4 I3 o. r% H# |' F: C) q8 ?
- HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断" M" u+ G, k( I' A
- res = huart3.Instance->ISR;
7 f6 {3 B% A3 b - res = huart3.Instance->RDR; ( G$ i2 L1 h. F2 B- o @
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间 S( h+ N7 B# T$ P
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 ; u) W+ j2 z$ i7 N6 U; C; M; N7 z
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用! B& S0 ?) q* x+ u9 V
- }4 A* F0 O8 F& D1 Q5 V
- /* USER CODE END USART3_IRQn 1 */7 O' ?* |3 W2 l1 J4 g1 w3 x
- }
复制代码 2 O3 E0 m+ E J# _" Q
数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
4 F1 L2 C' d& F( M4 z- X. g - {
' j/ w. g$ A+ h/ J6 v) a - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线1 ~% J7 [$ B0 d' S3 m. K
- if(huart->Instance == USART3)+ s5 G I+ L7 F# U
- {
. `* G; K5 S0 a) r5 J - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取
) ?) G5 M% Z+ V" p2 G - // {
8 f# X# O% P) S ~ M& p# X9 i. r4 z - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延
- y. O* X9 k: j - // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 8 k" I! W2 k+ t; H
- // }" _ U1 h- Y, T6 M
- // else% p8 D- }7 S* c" C# q4 ]9 o4 w
- // {
9 z4 j. @! K w6 g9 E: F8 I - memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头
- @! `4 b4 b) R7 a - Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
) h& _, a t0 b2 ^8 b( O% ?$ w - // }
- W3 B; {, I' }2 D9 e$ {" \, i( m - * q4 u; C) n! `! A
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
: }$ `0 j% B, u' r3 m4 h2 Y- |" i9 o7 i - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
6 ]$ t. `9 C3 |' `9 y - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收
+ `$ w& f. T. B" P x4 i; c - }- T `- c3 p+ f
- }
复制代码 ! Q: f9 ?1 {5 i* W
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据! ^% e7 V. N7 m2 e9 y: d
- {" ~1 Y& Q- A. ~2 f
- HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
) D# L4 v0 K# n - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除! `7 |* x3 t; n/ z2 ~7 s
- Usart3type.UsartRecFlag = 0;//标志位清0
; U" Y+ v# F. t% B! ~; t - Usart3type.UsartRecLen = 0;//已有数据长度清0
0 ^* \/ o6 o/ c3 n - }
复制代码
4 j. B# J: y" x. C) _' {1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
* ?6 l1 f, C" h- R1 k2 b0 d; a二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图:
* I% G% h& n1 E' U# R4 C( t! o
Y1 F9 `; s; W% |
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
4 Z+ i( t- V& |% t) `- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志 [1 A+ Q4 t6 ?$ I% g" w% ?0 y
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);5 E' l4 t9 E1 g3 u- P3 {
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
& a5 ]0 a/ `4 X9 R7 E6 E! n7 M. g - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 6 z$ ^, S1 g& W9 O4 R, z' Q% a
比如,在我清除串口接收缓存的函数中,我加上了这几句代码: ; R( X3 g: U; D% y" k! I
8 v$ {# ], L9 k, x/ v M5 y
- u; G# R4 y7 B, x# m0 W) a3 X3 [
. V7 C$ C* K; }! n/ U5 u
这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 - u( p+ a; y H% S
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断:
7 u4 Y+ ^% t( ?& x
3 ]5 }( R8 n" ]; M: E1 v: u3 j e7 e: A# w
在这个函数中有这么一段: o2 T! a8 ?' Q0 I( S
* \4 B: N) L2 m) f1 A- }8 t, b
7 V" ?( O" h) T( l0 O
* U3 t+ _3 j+ X' {+ c0 }里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误:
1 S8 Q/ V0 l0 d* y
% G* S8 N- y7 L6 V5 ~判断的语句如下: - if(huart->Instance == LPUART1){% I+ t+ C, E5 f3 j
- Enocean_Data++;
J" j2 F5 y- C2 K8 B9 y5 P T; u - if(Enocean_Data > 98)Enocean_Data = 0;; R6 b- n+ J# \
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){9 Z8 a9 ^9 S/ [7 K- j/ G
- hlpuart1.RxState = HAL_UART_STATE_READY;
: n1 K5 z0 x% j5 D/ n% a" ~ - __HAL_UNLOCK(&hlpuart1);) c( P1 Q' l- f; @0 o& C
- }
* B2 L) w/ G0 g' t - }
复制代码 7 ^: t: K0 E9 J+ f
如有侵权请联系删除
1 A0 u! I, w$ ~ 转载自: 矜辰所致 - J& E; h# G2 g5 ?
|