|
前言 早些时候写的一些测试记录的文章,并没有完全是以那种教材的方式写的,所以相对于最新的文章,整体格式框架可能没那么好。 好的一面是,我是实打实的实际应用记录,每次遇到问题会来重新更新文章,做新的记录! 经过前面一段时间的测试,我们把STM32L051 的需要用到的基本功能都测试过了,这次我们得把产品替换成L051了。 基本的IO使用都没问题,数据存储EEPROM和flash也没有问题,测试过正常用就可以了(实际上后来 EEPROM折腾了好久),在串口的使用上,也需要测试一下。 这里贴的代码是我测试流程使用过的代码,当时的文章以记录各种测试数据为主,仅供参考!
0 t Q0 v5 v* Z5 O5 o/ | V一、串口接收处理的几种方式1.1 串口接收发送不定长度的数据(非DMA方式)以前在在标准库STM32F103标准库的使用上用到的IDLE中断接收一帧数据,测试用起来确实好用 - void USART2_IRQHandler(void) //串口2中断服务程序! m7 k2 y3 v/ `# m/ f% d
- {
' N9 W* A! v% ~3 Q Z# S, H2 _ - u8 clear=clear; //消除编译器没有用到的提醒6 P5 e4 m5 l1 }$ ]
- 3 |. j% D$ d( t# ?" s9 Z
- //USART_ClearFlag(USART2,USART_FLAG_TC);
# K$ H* I0 J4 @& o6 D4 r2 a - if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
9 a8 D: w( I; ~1 s9 Z - {
, ?) A, U; K, H% B$ r) N" F( H - USART_RX_BUF[RX_Data++] = USART2->DR;" J* a* D: N9 j! T: a
- }9 j G L" _2 k4 \# v
- else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
$ V# e% u" V% J& n - {
/ j1 C& @ [/ V! n: t - clear=USART2->SR; //读SR寄存器 可以清空寄存器
& X8 d8 y/ w; D9 z - clear=USART2->DR; //读DR寄存器(先读SR寄存器,再读DR,为了清除IDLE中断)
" b7 I$ d3 Z6 J7 W& I0 { - ReceiveState=1; //标记接收到了一帧数据0 [- G* I8 l$ ]% I
- }2 H0 v% g- E6 y, `
- }
复制代码 6 B4 C& S: p; } c
不需要自己做延时处理,在某些情况下,比如通讯模块的串口3使用中,还是用到了延时处理,具体就是判断串口是否接收到一个字节的数据?如果接收到一个字节的数据,那么延时一定时间,一般是几毫秒(这个延时时间就是干等,等待这一串数据全部接收完成,所以这个时间需要实际使用中不同的测试优化,干等时间太长了不太合理,太短数据接收不完全处理起来会出错),如下: - RETURN_TYPE blue_getTelegram(TEL_RADIO_TYPE *pu8RxRadioTelegram, TEL_PARAM_TYPE *pu8TelParam)
! z1 U# R9 v' v+ j( k; d - {! \* \3 O0 `! ]! Z
- // uint8 i=0;. h6 d% n3 w+ D( r
- uint8 u8CRC = 0;
3 G: |& {7 i7 _( `/ H - uint8 u8Count = 0; 6 Z; e8 [; K! X) y
- uint8 TmpVal = 0;6 a1 W9 G/ M' }8 z
- uint8 DATA_LEN = 0;( W. D& J& N( C/ f4 I! E6 l7 K/ `
- uint8 n = 0;9 z$ U- T) M: m( p' t D/ z
- uint8 HEADER_BYTES[4];
$ c/ ?, D, Y: G, |* g - uint8 DatBuf[30] = {0};$ t; Y4 U% j, L; v* M) Q
- u8state = GET_SYNC_STATE;
# D! N' I9 d0 K - if (Read_pt != Enocean_Data){$ R1 d7 N h9 a* ]$ i
- delay_ms(7); //这里的等待就是干等!从收到第一个字节开始干等' u- W9 D( h, T3 _! G
- while (Read_pt != Enocean_Data)
& s7 k* [. t4 z5 E3 d$ q: M- y - {
" M" m1 t2 |/ M! X$ j - TmpVal = USART_Enocean_BUF[Read_pt++];
/ n* K- j" V) Y/ `5 p - if(Read_pt >= 100) //定义缓存大小,测试时候用100直接替代
- w! u* ^! l' k* O x) r - Read_pt = 0; A) _3 D) F, v* z/ p
- switch(u8state)# O( M% X! r2 {" ]
- ...
复制代码
$ l8 D6 i0 {1 C$ ]5 R3 y自己也使用过环形缓冲区等一些方式进行优化,可能目前的处理方式能够满足大部分功能需求,所以也没有确实的花心思正真去测试优化一个环形缓冲区的使用。 那么现在再L051下面,我们怎么来使用 IDLE 中断呢? 当然我先去网上查找了大神们的各种帖子,然后还是得自己修改一下程序,来一步一步做测试,先回到串口接收简单的测试: - if(test_data != Enocean_Data){
. A" j6 }' \2 `& P - HAL_Delay(7);4 W: y U; S9 g6 L! @
- // printf("Enocean_Data ID is: 0x %d test_data is : 0x%d \r\n",Enocean_Data,test_data);
}! A3 v& p9 b4 t1 Q+ f' D8 Q - if(Enocean_Data > 10){
# W, M5 E6 ?: ^ - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 ( U+ g. j3 n8 V1 ~. z2 q: t& o
- }
7 I, \ q3 W. e! I: b - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 % Q I. D+ q2 C6 R
- Enocean_Data=0;/ U" Y' ^9 T; Z \, d. W
- // (&hlpuart1)->pRxBuffPtr = &USART_Enocean_BUF[Enocean_Data];//用下面的简洁,数组名就可以当做指针用
9 \! c! N4 \3 a" b @& K - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
% j1 \/ _7 H0 w" ?) T) F - }
, b1 z4 M8 [' o - }
复制代码
: j" u/ H P: i8 T: Q在主函数初始化串口后,有这么一句,打开串口中断(这里的中断可不可以理解为打开IT中断) - HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码 % Z. @6 ?* @, ]2 q- j
现在我们要开启IDLE中断 - __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);# i% x8 i& k$ L4 Q% _
- HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[0], 1);
复制代码
5 w- y; ^& V7 K, \在stm32l0xx_it.c中找到 LPUART1_IRQHandler函数,因为所有中断先是进入 stm32l0xx_it.c 中相应的IRQHandler函数中,调用对应函数,最后才会进入到 自己设置的 Callback函数中,我们这次测试直接在void LPUART1_IRQHandler(void)函数中系统设置的函数前写一个 实现 IDLE中断后操作的函数: - void LPUART1_IRQHandler(void)
' C/ Y$ {( ?% h( [' M/ Z/ V - {& g2 H) c7 C$ q+ r- E3 N
- /* USER CODE BEGIN LPUART1_IRQn 0 */
# e( ?- L6 X% ]6 Z' Y& D3 @* y - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))- \( r- ~0 R- X
- {
: k2 k! @, X( H2 R - __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);+ j. }6 r/ e5 D0 P' h
- ReceiveState = 1; # V7 ?8 m' ^) \% O6 l9 S
- }
0 `; @1 }2 r7 V8 \ - /* USER CODE END LPUART1_IRQn 0 */
( m3 P6 u6 g9 ? U W. P! `) X - HAL_UART_IRQHandler(&hlpuart1);6 c% e/ D, y+ U. i' M
- /* USER CODE BEGIN LPUART1_IRQn 1 */0 `1 ~# }5 [6 A% {
- % g' d( I- I8 i1 e$ d
- /* USER CODE END LPUART1_IRQn 1 */' n% _& i( S. g' d
- }
复制代码
4 q" g) T; v" e' {9 i- C$ m用 ReceiveState 来标志是否接受到了一串数据,然后打印函数变成 - if(ReceiveState == 1){
7 N* K& _' F: K0 n1 y+ k! k - ReceiveState = 0;
2 r$ G! _6 u t( F3 m5 A - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出 ; }/ n) g6 o+ x, y
- memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 9 p& N! Y" g: A) G0 a6 t; ?
- Enocean_Data=0;
% u/ a2 M" _$ F' m5 |# ^2 z - (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错
z Q& _! C8 N9 E* e - }
复制代码 ' x. C! K* e8 P- d
结果, 数据异常,就是收不全,结尾会有问题,明天得继续测试优化了,先上个正常的接收,这是上面用HAL_Delay(7)测试得到的正常结果。 * U) P: Q( M! Q" ~4 A% h8 r
) o1 l9 \/ v3 V* x
7 c+ o9 }! g6 D( ?, }9 i. j实际测试,数据会断接收不全,考虑了一下,是否本身我的通讯模组串口给MCU的时候一包数据就是分两次发送(其实就是一包数据中间有个时间会长一点,然后MCU判定为2帧数据,每次能够收到开头的一段), 我测试的打印函数每次收到一包数据,都会把数据全部打印出来,然后清空缓存区,所以下一包数据直接被清掉了,因为在进行输出的过程,可能下一包的数据中断进来了,为了验证一下,还是加上了一个延时,代码改成如下: - if(ReceiveState == 1){
* l; c4 P1 g# L - HAL_Delay(7); //测试,重要
4 t/ C2 f9 f: J - ReceiveState = 0;
6 v1 A/ n% C, f' P - HAL_UART_Transmit(&huart1,USART_Enocean_BUF, Enocean_Data,0xFFFF); //将串口3接收到的数据通过串口1传出
1 X( i# k; _* I+ D1 J; j( g - memset(USART_Enocean_BUF, 0, sizeof(USART_Enocean_BUF)); //清空缓存区 ; ]9 f' J* g1 l+ a' J
- Enocean_Data=0;6 r% H7 u$ |; d) Y
- (&hlpuart1)->pRxBuffPtr = USART_Enocean_BUF;//这一句很重要,没有这一句,后面接收会出错5 r1 o: Y! M- q" [) {. e
- }
复制代码 5 z$ \5 o7 ~2 G. v7 Q
发现加上延时后,数据就正常了……,其实中途测试的时候在IDLE中断处理时候,我打印过 测试语句,发现我正常的一包数据,都会打印2次测试语句,也进一步的证实了,测试模块给MCU的正常的一包数据会被MCU认为是 2帧数据。那么我们怎么来解决这个问题呢,还是直接加一个ms延时吗?至少IDLE中断在这里用不了,一帧数据会触发2次中断,可能和通讯模块有关,因为以前测试的时候也遇到过类似情况。 但至少IDLE中断我们可以用起来,在这里做一个小结:1、正常初始化串口以后,打开IDLE中断: / O+ `$ q: e: L" ^5 ]% k$ Y
- __HAL_UART_ENABLE_IT(&hlpuart1,UART_IT_IDLE);
复制代码
2 Q0 a6 M% V3 D7 x! I这个语句可以放在串口初始化函数MX_LPUART1_UART_Init中,也可以放在main函数后面,看个人习惯;
0 @% V. ^- f0 Z: P. @7 h, L- U$ r2、在stm32l0xx_it.c 文件中响应的中断相应函数LPUART1_IRQHandler 中加入关于IDLE中断的处理于语句: - void LPUART1_IRQHandler(void)
" |8 c6 Z! n' ~1 [* C - {
5 X; W5 ?$ K8 U1 U8 W - /* USER CODE BEGIN LPUART1_IRQn 0 */
1 B: e: Y! R, c/ ^/ [ - I$ {! l' e4 Q' z4 M( Z
- /* USER CODE END LPUART1_IRQn 0 */
, t# O; b7 a ? - HAL_UART_IRQHandler(&hlpuart1);' S9 ]" ^6 Z4 G, t+ Z
- /* USER CODE BEGIN LPUART1_IRQn 1 */
; ?% s6 Q. j& A - if((__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET))# i2 l' |3 y: U5 d0 Q
- {
+ F$ h# K: d( R l# y0 O2 D. r; ? - usartreceive_IDLE(&hlpuart1);
. c1 l2 |! @3 a+ A, D6 h2 V# | - }8 F& k8 {) `5 m \' o, v' {9 c
- /* USER CODE END LPUART1_IRQn 1 */
* D% O: b! ?& g/ a; ?4 a$ X - }! ~! P6 I1 i, g* t. E5 Y3 f* m, U
- & \- h% B% Z2 q9 |8 d
- /* USER CODE BEGIN 1 */$ Z3 F1 o( C* q! w6 J/ a5 m
- void usartreceive_IDLE(UART_HandleTypeDef *huart)
- N% L+ l+ Q Y8 z5 p/ J - {. j3 ~( m0 ^. ~
- __HAL_UART_CLEAR_IT(&hlpuart1,UART_CLEAR_IDLEF); //清除中断
3 K4 Q3 c4 D, R - // HAL_UART_AbortReceive_IT(huart); //终止接收, n! U, ^$ k2 t5 m; K% F
- ReceiveState = 1;4 K" a4 l m% v# a) B# p3 Z# ?
- }
复制代码
7 l# e; G! {5 \9 o2 n/ H1 ]+ 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);9 s$ ?( Z0 [, L1 N
- printf("send command !\r\n");' g! t: I: o0 U* @
- COMMAND_GetmoduleID();
" h3 J- H# r. Y) @ ? - // HAL_Delay(10); //7ms不够 ( p+ t0 [; ?, r) m+ v: f, g, ~9 b2 b6 Q
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文, `. k# i8 o, x- A
- // while ((Enocean_Data - Read_pt) < ID_blueNum){' }) W& l O' z2 v) x" @
- // HAL_UART_Receive_IT(&hlpuart1, &USART_Enocean_BUF[Enocean_Data], 1);
$ ~1 L7 B2 k' j( U3 i$ m( b - // }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西' E- \% w9 M: E/ ~) }
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
* U$ Q$ ]4 m% O) }( C6 E
/ x7 z1 V5 l/ }4 x- E }如果按照上面的代码,会一直卡在这里过不去,其实就是while ((Enocean_Data - Read_pt) < ID_blueNum);过不去,开始怀疑是不是通讯模块本来就是坏的,我们有多种方式测试: 测试代码1(好理解,发送命令了,等待一会,让串口把数据收完): - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
6 I& S b0 ?" b0 O# f9 A - printf("send command !\r\n");
: }+ v$ G w! n: n - COMMAND_GetmoduleID();1 {7 U9 [) _( c6 J
- HAL_Delay(10);//7ms不够 + n- j" {4 Y% |2 c& b* j2 L
- while ((Enocean_Data - Read_pt) < ID_blueNum);//加了这句不需要加上面的延时,这句就是等待,这句很重要,能够过滤掉不相关的报文: g% y1 @3 W# x
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
3 v9 K, S3 a7 O: Z
+ t8 H Q& G- b
测试代码2(本来是想看看等待时候打印什么东西,加个条件怕一直循环):
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);* W# q9 G! j0 D! y1 [
- printf("send command !\r\n");. l: ^% Z0 @2 @. e7 s- f8 T
- COMMAND_GetmoduleID();9 |/ c- w \3 `6 G
- while ((Enocean_Data - Read_pt) < ID_blueNum){' ^" B' e& d; P. \. u. B
- if(Enocean_Data < 10)printf("测试过随便打印点东西都可以!\r\n");! r2 Y( Z+ p0 }; K/ ] A
- }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西
. c$ R. C* F3 a/ d, l - printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
9 W2 M5 Q5 M' {4 p+ Y
+ {: Z/ E! w& O2 H( I7 D: e
测试代码3 , 4(不加条件随便在等待中打印 , 延时 ) / w+ n3 @" I) J; m* B1 L! O
e$ { e# ~1 o2 \7 w( }( A6 z$ K7 A$ j( ~9 x6 d! H
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
4 h. |0 l: }7 s; [( i# v - printf("send command !\r\n");+ y1 I3 s9 \- V5 u* N; o3 r
- COMMAND_GetmoduleID();. z0 {' B3 J6 X- _& |$ r( f
- while ((Enocean_Data - Read_pt) < ID_blueNum){
" V, F: _, F+ Q- F+ N - printf("不要条件乱打都可以测试过随便打印点东西都可以!\r\n");
+ \" x) C0 a0 e9 |" W9 u9 k- f - //HAL_Delay(1);//延时可以,1ms就可以
8 H) y: {6 _# K. R( L* [8 p - }//2021/8/11 不是很懂为什么这里这个while等待必须得加点东西1 x* x, Y c: U& r
- printf("Enocean_Data is :%d Read_pt is :%d\n\r",Enocean_Data,Read_pt);
复制代码
% S! ` p9 O+ k/ t# Z) W( A. U# f) a3 n B( x( r4 ?5 w
# Q* q9 M6 O' A2 i
' H' t9 B0 R2 Z' z" e% v- U
7 v) p5 t2 W$ T' {
: x* h1 p& b0 z- n9 Q8 B( z
经过测试发现,while中虽然是干等,但是没有语句就会有问题,不是很明白,因为以前都可以,虽然知道解决办法,但是不知道问题原因,所以也不知道哪种是更优的解决办法!= =!
& e: s* u+ X! X4 ?# F4 C1.2 串口接收发送不定长度的数据(DMA方式)在实际使用中,我没有在L051下面测试使用DMA利用 IDLE 中断进行DMA接收发送,实际上,通过我们上面做的实验和测试,对于目前使用的通讯模块,一包数据会触发2次IDLE 中断的情况,也不太适合。本例程是以前STM32L1系列中使用的例程,以前也是在网上参考过其他人的设计然后自己用起来的,正好也在这里做个笔记 - #define USART3_DMA_REC_SIE 256 //DMAbuf空间大小! U, g X2 L8 s. B6 ?
- #define USART3_REC_SIE 512 //接收区空间大小两倍,以保持两条历史数据4 [0 e4 ^5 @- k' j: e2 S9 z
- typedef struct( T8 S, M$ W9 d5 y7 d2 E
- {
4 P- A! v$ \. l+ G - uint8_t UsartRecFlag; //接收数据Flag, | T% S+ l) r5 m1 r$ b1 T
- uint16_t UsartDMARecLen; //DMA接收到数据的长度
; v/ H/ M( F# G( B% Y1 q2 h - uint16_t UsartRecLen; //RecBuffer中已经存储数据的长度, D1 P6 C" ~9 n3 u1 e
- uint8_t Usart3DMARecBuffer[USART3_DMA_REC_SIE]; //dma
+ Z3 x6 m9 m/ U - uint8_t Usart3RecBuffer[USART3_REC_SIE]; //RecBuffer
' z* B) M. r$ b; V8 `% D, Q, \ - } Usart3DMAbufstr;( \3 W7 M( q: t" D( c8 U8 x
- extern Usart3DMAbufstr Usart3type;
复制代码- void USART3_IRQHandler(void)3 F8 w* K+ F7 t+ A* _9 V6 w
- {
# n2 z6 C# w3 E, G/ p - /* USER CODE BEGIN USART3_IRQn 0 */. p1 o) d- u3 O
- # a7 ~! N+ X- H3 [9 q, k4 V8 }
- /* USER CODE END USART3_IRQn 0 */, N$ k+ m9 ^9 f3 h$ j
- HAL_UART_IRQHandler(&huart3);
; f2 j9 [5 [. J3 f1 [1 F - /* USER CODE BEGIN USART3_IRQn 1 */+ e% B2 u6 O0 L' K( y
- if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET) //判断空闲中断, x7 H; |/ U5 x( D/ o. r
- {
. N* n1 r' n' X8 }6 b5 ?, _5 V - u16 res = 0; 9 e4 }& k' {) X- |7 D
- __HAL_UART_CLEAR_IDLEFLAG(&huart3); //清楚空闲中断标志
3 M, I+ d! E# D' i. _ - HAL_UART_DMAStop(&huart3); //暂时关闭DMA中断3 h- |+ a9 j3 `7 |' T$ z
- res = huart3.Instance->ISR;
% ?3 U2 S# `7 z6 i - res = huart3.Instance->RDR; 9 k* D* j6 g' m' l5 ?
- res = hdma_usart3_rx.Instance->NDTR; //读取DMA接收的那个buf还剩多少空间8 P* |2 b0 i7 c4 ^ ~: g, i- ?# j+ Z
- Usart3type.UsartDMARecLen = USART3_DMA_REC_SIE - temp; //总空间减去还剩下的空间等于存入数据的空间大小 2 P' r; v% t, g" T7 u
- HAL_UART_RxCpltCallback(&huart3); //手动调用中断回调,因为空闲中断不会主动调用
. s; j0 ~' \) Y# I2 Z - }# N+ o1 `- ~7 w: r
- /* USER CODE END USART3_IRQn 1 */1 b8 B) D' ~1 R7 E7 n
- }
复制代码
$ S: v8 C1 O1 F, {数据最初都存储在DMARecBuffer中,然后转存到RecBuffer中。DMARecBuffer中的数据每接收到新的数据都会清空。 - void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
+ |. m( u6 z% d; L1 U - {
' _2 l7 B) ]4 l" N% d2 }1 | - //可行选着是否开启多条数据缓存,默认不开启,开启删除下划线# m" f8 d# d0 S0 }
- if(huart->Instance == USART3)
% D5 I" U" c3 z% e# {6 X - {
9 H2 B5 D2 ?( C2 ]1 U* L/ D1 K - // if(Usart3type.UsartRecLen>0)//判断RecLen是否清0,如果没有清零代表上一个数据没有读取 ( A. B& e, V) D" q8 g1 y0 w
- // {
+ F2 p3 ]) f! x9 N - // memcpy(&Usart3type.Usart3RecBuffer[Usart3type.UsartRecLen],Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把数据顺延/ U+ x3 q+ e( Q4 I$ z
- // Usart3type.UsartRecLen += Usart3type.UsartDMARecLen;//数据长度增加相应的位数 1 M( v% k i* y
- // }
! h1 s9 F1 f7 L4 x - // else' w2 v" R" g Z4 ?
- // {& k8 ~: J% G3 e, y
- memcpy(Usart3type.Usart3RecBuffer,Usart3type.Usart3DMARecBuffer,Usart3type.UsartDMARecLen); //把输入放入buf开头+ r: N* M1 K$ t- L l% c+ T2 K
- Usart3type.UsartRecLen = Usart3type.UsartDMARecLen; //记录数据长度
/ B5 W, U T5 ?# l - // }
1 p' B5 _. G! ^ - J0 K- r4 [ t/ b
- memset(Usart3type.Usart3DMARecBuffer, 0x00, sizeof(Usart3type.Usart3DMARecBuffer)); //把DMA缓存中的数据清空,方便下一次接收
; O5 [5 `- L4 m5 { l - Usart3type.UsartRecFlag = 1; //RecFlag置1,代表RecBuffer中有数据, U% _% ~- }, J8 f2 ]
- HAL_UART_Receive_DMA(&huart3,Usart3type.Usart3DMARecBuffer,USART3_DMA_REC_SIE); //重新开启DMA中断,方便再一次接收0 O- \1 m3 j3 S6 p, l
- } j8 M9 H" n/ v+ w( v. f
- }
复制代码
7 _* g! U: C8 v/ m) Q! w在相应的程序中把上面的代码添加好,然后在主函数循环中使用,RecBuffer中的数据会一直增加,直到用户读取以后才会清空: - if(Usart3type.UsartRecFlag == 1)//如果Recbuffer中有数据
8 Q/ P/ _7 U$ T3 Y& r4 j - {
6 K1 q' o/ l, o1 W8 `/ z# L - HAL_UART_Transmit(&huart1,Usart3type.Usart3RecBuffer,256,0xffff);//把RecBuffer中的数据发送到串口1- L- e" P7 L1 a- e8 [
- memset(Usart3type.Usart3RecBuffer, 0x00, sizeof(Usart3type.Usart3RecBuffer));//读取完数据后记得一定要把RecBuffer中的数据清除
8 S" n% C7 n7 _, E; ?8 O% v - Usart3type.UsartRecFlag = 0;//标志位清0
5 M6 l( \- Z2 @/ v - Usart3type.UsartRecLen = 0;//已有数据长度清0/ L: @. o* {8 p/ ], K( e9 n8 d; q
- }
复制代码
1 ?$ s" I% ]% ~3 N+ d& d1.3 环形缓冲区因为单单靠数组方式,接收处理,总感觉不是那么聪明,有时候需要干等,所以还是得花时间研究下环形缓冲区。。。
8 d/ R" i2 j0 t+ n; d二、串口接收卡死处理在使用了了段时间后,测试部反馈偶尔会有串口卡死说明,最终就是接收不到串口数据,但是轮询发送是正常,后来查阅了一些资料,找到了需要处理的地方,这里特此来记录一下。 2.1 清除错误标志位在使用 HAL 库的时候,有4个错误 flag,如下图: " w# n( R+ Z( z+ g% Q {
% B7 T( ~8 ~8 b- y, y F1 ^' ]
出错的时候,HAL库会把以上flag置位如果不清除就再也接收不到数据了。 所以我们可以在需要的时候使用,下面的语句清除错误标志:
3 v7 z) b1 K8 c2 L& k2 N- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_PE);//清标志) q+ z2 `4 L' J
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_FE);, `( M3 u5 t0 r$ A
- __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_NE);
( ?( W0 u6 m$ [3 G - __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_FLAG_ORE);
复制代码
1 T% z5 |; |7 _, z t. p B2 `比如,在我清除串口接收缓存的函数中,我加上了这几句代码: 0 ~8 j5 R* p% v9 z) f/ [) d) L" K
) S) i4 ]7 ^# R! G/ }, d( O
( u; D+ r( M1 L) ^
0 e/ K3 g# @2 R9 }) d这是实际使用的情况,我并没有详细的测试到底是哪一个错误置位了,在自己了解的简单产品上,直接一步到位也是一种方式。
7 o! A/ z8 [. Q# s2.2 HAL 库函数问题产品使用了上面的串口清除错误标志,在压力测试下下面还是有问题: 现象就是串口发送正常,但是永远接收不到数据了。 实在是没办法,后来继续找答案。 最后确实发现网上也有小伙伴遇到过相同的问题,我使用的是 HAL_UART_Receive_IT 开启中断: . B; a3 w" z: M
, y$ _0 a& d4 }' B
]$ ~2 [" B/ `7 ^, m, x在这个函数中有这么一段:
1 P# X( P( k' X# B! Q% @7 N0 b' j- t4 n9 b( y! m! p; |! U
4 P8 b3 O' v; v5 v8 l: \1 o) ]
6 m1 z7 j8 F d" Z% r# i6 r
里面有个加锁操作,正式因为这个加锁操作,如果收发同时进行,会有概率卡死。 这个时候的处理方式就是手动解锁,每次接收到一个字节后需要再次开启中断接收,判断一下是否有错误: $ l* J( v5 E c8 C9 B- K3 D$ }
K0 j1 G& F' H- `3 E4 t, v5 J, x判断的语句如下: - if(huart->Instance == LPUART1){
3 g" _" h2 e4 [+ J2 p8 E+ O7 R' z - Enocean_Data++;
- j# ~* Y8 L5 }$ Y* } - if(Enocean_Data > 98)Enocean_Data = 0;
6 R& O! _/ n; | W% Z7 L5 ^ - while(HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&USART_Enocean_BUF[Enocean_Data], 1) != HAL_OK){
$ i/ |: P* I- @$ e( ~ - hlpuart1.RxState = HAL_UART_STATE_READY;
# v: F7 c$ ~+ ]) F. n - __HAL_UNLOCK(&hlpuart1);
* L5 ?$ Q0 t1 ~" k6 F% N - }' M: Z( e" c6 s+ d( O
- }
复制代码
) K [. ]! k- w6 s, F如有侵权请联系删除
- h5 T3 ^, E* c. _9 B- I* N 转载自: 矜辰所致
2 \1 {# @: h6 r" V |