前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考! ; J5 r, v! d6 g0 X
一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序2 r# S# w4 u* I/ U* U2 K
- {
* Y' B8 T, A ^: M: O - u8 clear=clear; //消除编译器没有用到的提醒
2 j |3 D& {, P$ M
, n, }3 E5 b3 Q) V- x+ o8 u- //USART_ClearFlag(USART2,USART_FLAG_TC);* I# [- N8 Q I1 d
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) % c) o: v1 u/ R. P
- {
+ l3 W+ V7 c4 C& W3 p2 L - USART_RX_BUF[RX_Data++] = USART2->DR;
2 Y5 d1 F# K7 ~+ \* R3 ^# s; U - }
* V9 E# R( C) f$ X! S - else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) 2 ]( F6 C/ j6 B
- {" Z) `7 [. N4 V/ }' i1 {- Y
- clear=USART2->SR; //读SR寄存器 可以清空寄存器5 B2 O+ `% R/ P
- clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
: ?. @* M6 L4 o0 d8 x% H4 F$ { - ReceiveState=1; //标记接收到了一帧数据
+ I$ K, p, t$ Y k D( Q4 o - }
9 s) j1 t" g1 X/ Y. Z3 L+ v - }
复制代码 6 H/ h& @6 D# {$ o& Q3 m2 N
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
- G) |4 @8 u. l( w - {9 o" a1 _( Y" k; P B) ?
- // uint8 i=0;! T# F0 O3 S- n
- uint8 u8CRC = 0;# y* e0 P7 i) m( ^' ~# P9 x4 q
- uint8 u8Count = 0;
; Y4 |) S& _% H! i; a! x - uint8 TmpVal = 0;5 V. g4 M" P' s' k& W9 f
- uint8 DATA_LEN = 0;
7 S9 m$ u0 _/ B5 N S - uint8 n = 0;
/ }7 o5 _. A0 I - uint8 HEADER_BYTES[4];( m* A, W3 H9 }/ P( S- X
- uint8 DatBuf[30] = {0};
5 ?/ R0 z( @, s7 m$ o - u8state = GET_SYNC_STATE;
# v1 Z6 c% |6 Q - if (Read_pt != Enocean_Data){
2 X- I3 K0 a* x9 S, l7 `. S- \5 b - delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等, E. s7 Z& G5 {2 N2 T
- while (Read_pt != Enocean_Data)
A3 S v" h9 ^; b; t - {# h! f) B! W+ G( ^) n, F
- TmpVal = USART_Enocean_BUF[Read_pt++];0 a! s6 ~+ [* i5 e% B* R% i g
- if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
& \0 P3 d& P, K% I5 E - Read_pt = 0;" u- t R% T, u& D
- switch(u8state); l# e4 `7 d" J7 _
- ...
复制代码
4 I. G6 a) C, t2 S& D自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){
4 m/ T) B Z; Y; f5 v$ [ - HAL_Delay(7);! u5 m- q! k' {" h `5 r
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);" e8 |7 u P& o
- if(Enocean_Data > 10){
5 @& K# U+ s2 E' Q; q - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
; z6 w! ?; }( O) J3 H - } + u6 Y) X3 H7 ] m0 z& D" Z
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
: |( b$ P/ Z' C( Y# W - Enocean_Data=0;* ^, P4 t# u+ U
- // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用) r0 S# V2 [ g3 z
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错7 {! ? i& [& c; v) l
- }; k0 S1 J1 P( r; B
- }
复制代码
8 F+ P0 x' E( v, E9 ]在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
" B ]8 @4 \3 O& I6 x) b3 k现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);* r; Y" x" t# j. V3 _0 S5 e
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 , z3 R0 f2 I2 ~
在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
3 \5 L) s" o5 V0 q - { {! F2 z& o0 ?' K& }1 C- g* P
- /* USER CODE BEGIN LPUART1_IRQn 0 */
) B; r" B- T: D2 u3 ? q - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))( y# P$ _/ P7 u; p3 R
- {' e% x& t. _5 ~; b# e8 e
- __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);: g( V' A9 v& g0 `
- ReceiveState = 1; 8 i- |& _5 K) V: ?4 z. c
- }7 d% r. y5 S) `2 _4 D* O6 U9 }+ y
- /* USER CODE END LPUART1_IRQn 0 *// u6 {- X' d, ~: v( L+ o( I
- HAL_UART_IRQHandler(&hlpuart1);' [3 {* e9 ?" D* i% s* I
- /* USER CODE BEGIN LPUART1_IRQn 1 */
9 w) ]8 _" Q$ A5 `. x - 6 U+ Z+ N9 ]+ ~2 p4 x2 D- y' k
- /* USER CODE END LPUART1_IRQn 1 */
0 Y- [( Q& y: S - }
复制代码 + ~2 J7 v- ^4 s9 w5 O
用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
9 p: H$ ~$ H+ f) }7 `2 J8 L( ?" ~* B - ReceiveState = 0; - [! n& G) N; ]5 R+ n
- HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
/ x4 I; d2 E- U+ u3 g. I x T0 K - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
7 Y, M6 j0 f4 u# v! J - Enocean_Data=0;/ f+ P! c" Y5 z- ]
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
7 G% o2 M" N. V- }2 H& P& e+ O - }
复制代码
: J% W. N1 Z+ g- r结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。
3 ~0 W) b/ }. h- _) ^/ J/ c4 v/ D8 x
: M' J! W$ M' g, M: d8 N% m! ~( f q
& v% N* }; L4 c8 G& }4 m实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
4 u( T% {* T1 V" ~3 R* J3 K - HAL_Delay(7); //测试,重要
5 J7 ~" ^0 y }1 P, A - ReceiveState = 0;
3 K& |5 I0 A4 c. c - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 g+ ~! ]3 d T& z
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区
* T, n9 Y& |# h, `( s% K - Enocean_Data=0;+ N% a, O4 Z& O# Z$ W4 ]7 h
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
, u$ q1 }2 m$ j: k/ G - }
复制代码
% d. O8 y" t7 Z1 v4 R! f7 G发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: . g1 l0 C0 |# M- f
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
, t2 E% i( ?; R) A7 |) z0 @7 K这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯; . e& {, Y: X' k2 v/ O) Q! B
2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)* `4 y+ E3 J" {
- {* u0 l4 L8 ~- O# s
- /* USER CODE BEGIN LPUART1_IRQn 0 */
& W1 c( b) q* _, N' J! K8 i9 p - " u' T: ], ?" a$ a: A( H6 n
- /* USER CODE END LPUART1_IRQn 0 */& ~- i5 a4 y/ [
- HAL_UART_IRQHandler(&hlpuart1);
( T1 e+ ?/ f U - /* USER CODE BEGIN LPUART1_IRQn 1 */! R8 i1 ^7 L, L- {5 b% t
- if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))
& I, k3 N" E5 u - {
+ g1 ]7 H. b2 s! e7 } R9 W. L* } - usartreceive_IDLE(&hlpuart1);! X4 o' G* V3 |9 {
- }
& C, s# g" h1 E; x' R) q, ~9 h - /* USER CODE END LPUART1_IRQn 1 */
: k# t: a- b+ }% ? - }( t+ m, u0 t/ C& U" ^
; D7 O! H- K8 i8 z- /* USER CODE BEGIN 1 */: A- ? y; K" s5 B$ I
- void usartreceive_IDLE(UART_HandleTypeDef *huart)5 r1 r" Q- c# X+ n9 A4 n0 \0 e! ?
- {
% [# o' L! C& ?4 t% | - __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断" q7 A5 Y6 G6 I9 X% G) `4 w
- // HAL_UART_AbortReceive_IT(huart); //终止接收, {; `9 i" m) }: J. e. F
- ReceiveState = 1;
; L5 a/ e( M& o/ m4 p& M - }
复制代码
# ~! |4 Z* H9 N5 j* `8 g% P用一个标志位表示收到了一帧数据,即便这个一帧不完全,也至少表示收到一段数据了,这里不能加 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);! p( {/ c3 v6 p/ S, ~8 `
- printf("send command !\r\n");
7 X% Z, F* W# r' _ - COMMAND_GetmoduleID();5 V( l, j9 G9 a3 r! s
- // HAL_Delay(10); //7ms不够
/ q4 |9 A: h! C4 S* `- }" g7 p - while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
6 l; K! N$ {. e0 q' \$ r - // while ((Enocean_Data - Read_pt) < ID_blueNum){
) |, h% s2 u3 j5 w- p) d; a - // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
+ b7 p/ y' z+ L; n4 S7 L; O- v - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西) _. {, ^3 Y& h- H# s+ A/ H0 @' a
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码 ) X/ x8 d0 J. [
k; y6 m+ `" n# c- g1 i) _如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);3 l) Q1 n* J; ]7 O" ~/ p1 r
- printf("send command !\r\n");
$ Q, s0 Q" X: H- Y* {& p - COMMAND_GetmoduleID();
. l/ z' f: K' P% F1 H - HAL_Delay(10);//7ms不够 3 F' |2 Q: p3 P- m( v
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文
% ^, B0 @+ K. s- X - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
' M+ w) S! K- ?* _) K8 b
9 T M& } m% c Q/ d测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);1 x/ o# P4 O/ h* I& A! V0 J2 ?8 W j
- printf("send command !\r\n");% Z, v2 d( m$ s7 _/ x- T
- COMMAND_GetmoduleID();3 _. p1 U- x7 N$ Z' Z5 x6 v, {
- while ((Enocean_Data - Read_pt) < ID_blueNum){
" ?3 w* L& {2 u" x% U X5 x$ _ - if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");
Q2 G+ ?0 ~( k o+ Q, | - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
+ l Y k r* h% k& a - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
2 {; n, _0 y& N# ^! V4 ~! Z6 H% o
. Q; M. t% D6 h. T$ z" B& Z测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) 4 h$ n3 q4 l! [4 @
0 q0 N( x( ~0 p, ~- `; O
/ K2 o1 {/ z8 ~ s* a- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
8 A3 F$ u+ g; ~7 f5 c/ j* I - printf("send command !\r\n");
0 q. M7 y+ u! X' |2 l, R3 a - COMMAND_GetmoduleID();
+ i' L; n) D* ]4 K' [ t - while ((Enocean_Data - Read_pt) < ID_blueNum){
l) m& T7 l) w/ Q( _, L$ x - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");" ?8 D0 B6 w; G: ^1 l
- //HAL_Delay(1);//延时可以,1ms就可以
$ n# E# {& G5 J: R8 }5 j - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
4 n& r3 B2 q- p2 X9 G2 p. L - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
* e5 k% O( \& D: P2 i( I5 s6 e6 ]6 j+ m" g n6 t9 R% v
! |% [0 i" I7 [$ O6 S+ {, g" q' n2 W
7 k1 h4 Q& [; J8 f; g
4 `- c9 Z. W, u经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =! ! Z8 [3 g# M+ X. S; {; t
1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小
8 B! M+ G/ W9 {7 i - #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据
3 a- R }8 a/ K9 B - typedef struct' I) v7 w" F$ k, o0 R. S. d' {( R3 _
- { ( b7 U9 ^1 V; O
- uint8_t UsartRecFlag; //接收数据Flag
, f# }0 G' M {( `9 ` A* x6 k - uint16_t UsartDMARecLen; //DMA接收到数据的长度: A7 Y+ r% p: P9 I0 N/ P6 Q! E2 \% }
- uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度: r2 v" C0 t+ x6 u& A; U; \$ [' T
- uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma& Y/ i" h& w" C" Y. l
- uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer3 n# M. q0 a8 h) p( T/ x4 N
- } Usart3DMAbufstr;
, @+ Y9 U( X# ?' n - extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void). [4 {7 R; ^0 D( I5 [
- {* U$ M1 s& s; q* Y) h, |) P
- /* USER CODE BEGIN USART3_IRQn 0 */
. g: A8 L* ^$ M: k5 j - 6 S1 t- e1 l1 [+ n6 L1 y
- /* USER CODE END USART3_IRQn 0 */4 s' G+ s. k# R
- HAL_UART_IRQHandler(&huart3);+ k& b. J7 z; D
- /* USER CODE BEGIN USART3_IRQn 1 */; e: V5 ^3 g) I! y
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断
( x8 U9 E- G% z+ K4 v! A2 D& r - {
$ W7 ?% D1 H! C, e# p, ?; _4 K - u16 res = 0; - J# o5 o4 l1 b' ^
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
6 k$ { y5 k- ~: u# `+ a - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断0 V+ v9 ]; i; @, Y ~6 D! e
- res = huart3.Instance->ISR;
V+ k* u$ u$ E8 e6 | - res = huart3.Instance->RDR; 0 q: H, t) x+ ]2 R) @
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间# v9 R$ [2 R1 V4 Y2 l
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 " [! {8 |/ q8 W2 ]9 [
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
% i7 K0 {9 B4 N8 e4 p0 D+ @6 T - }
X5 q/ ~7 x% C - /* USER CODE END USART3_IRQn 1 */3 G: K! U9 N& o
- }
复制代码
6 g( ~$ D$ w4 L4 H y2 _6 L数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) ^! t3 E: R: D7 [5 }
- {
5 x1 e/ ^7 `! }" ~: {7 p1 K6 d, _& V - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线
3 @9 @2 R3 Y' D- d0 s \. A - if(huart->Instance == USART3)
* X$ y1 ~# H* Q5 a/ z - {3 k2 b# W* V) P; W( [/ r
- // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 ' |: C7 E% G1 {* G" J8 B4 ?& W
- // {: X( N9 }9 K. H. f" p5 }) x
- // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延- |9 R: t# M# t0 n* W% h) \0 d8 x
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 : x; X+ |. }$ |" L3 M& S
- // } g% V3 k- ]! K1 Y! k
- // else
; H! n! K5 O; l* v; A0 @ - // {( t" ~! u7 K5 c% D, S
- memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头4 Y' P9 Q+ X# O" c( C
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度 + N1 r: _+ I+ v5 b. U
- // }3 x; X' w( z% o# A* s
-
% f V/ @9 I0 L3 t: @- V - memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收9 b8 A1 q R, e
- Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据
+ R" Y( r1 |4 Z: f9 l - HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收7 ~+ J6 ~2 |. l/ h3 X; ~3 z
- }: M3 ?4 h/ Z) X; F
- }
复制代码 ; F4 k% \, Q& d7 M8 a. g
在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
/ Q, F. K" @9 k1 r4 L7 v - {
, |+ c! R! y) _* b, ~$ _ - HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1
2 \$ Y' T! {$ Q7 g( x: V& ]% r - memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
4 V1 C: |8 O9 ]% r - Usart3type.UsartRecFlag = 0;//标志位清05 [$ R0 n+ }& |7 K! i7 K1 A' b
- Usart3type.UsartRecLen = 0;//已有数据长度清0
9 m$ \7 ~: @; O8 M - }
复制代码
* v5 { z1 q. F, F1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。 # S% S: v5 d: [) t3 w ^# K$ N4 J+ A
二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图:
) B7 d# i5 a1 I: l' I
r5 \+ H# `) C' s, S
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志: 0 X- }+ j* f9 c
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志$ y3 X; _$ p8 v' i: \5 @ f& ]5 _: V
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);/ j5 v+ T5 X8 @% @: R6 n7 b; d
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
5 A7 n* M6 p7 M8 A8 k& H$ q N B - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码 7 X- y' p+ P5 M, E6 k% X6 d- ^( E) k
比如,在我清除串口接收缓存的函数中,我加上了这几句代码: % X9 d$ O/ \5 t3 T* D
. n: t4 y# L3 I0 C) U, A. V
6 d% E7 N$ S" a. P
2 v9 q4 V* ?& ~" I这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。 0 z. z7 M9 F% w! z
2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: + y& v6 o' P; O/ v/ M2 q
f0 f7 E; w- n+ _- k) D
, B: s: c9 I4 N9 N( w在这个函数中有这么一段: % F& b4 z( k3 |! O8 I8 T1 S" O
. V: f& ~: E/ n5 e/ l
0 a) T: [+ ^5 H9 S
1 {* ]6 S8 w' m9 h里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误:
6 x! {; ?, T$ U
0 W, ?5 G0 A+ K. {8 W
判断的语句如下: - if(huart->Instance == LPUART1){ |9 I5 a: |: K& W5 a' t
- Enocean_Data++;
; [0 B, v1 D% w9 c - if(Enocean_Data > 98)Enocean_Data = 0;+ J, Q0 i; x9 t& o) J( O
- while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
3 L2 O; x' V' f$ N5 h/ u" X - hlpuart1.RxState = HAL_UART_STATE_READY;4 {4 f* l/ _# P; s
- __HAL_UNLOCK(&hlpuart1);
+ P) F# \0 @/ J - }; t1 [! v! }5 s2 [4 b: R! V
- }
复制代码 + P" U$ F( t( _6 E E/ f
如有侵权请联系删除
, U( a; m- L4 W# j 转载自: 矜辰所致
- m. F! E1 C6 E% M, u |