|
我这里使用的芯片是 F1 系列的,主要是利用 DMA 数据传输方式实现的,在配置工程的时候要注意配置好 DMA,并开启中断。 5 G# p" s x2 f V9 `; [! |& u. n
如果出现数据长度对,可是数据接收不完整,把Memory勾选即可:
" J) F. J: ~: }0 z* N1、利用STM32 cubemx 建立一个工程 2、利用STM32 cubemx 生成代码后,我们先定义一些变量来使用 - /* 自己添加代码部分 */1 e0 g; C% K2 ]$ \" K! o
- volatile uint8_t rx_len=0; //接收数据长度
4 s9 `9 ?* s7 ]% d( h+ G - volatile uint8_t recv_end_flag=0; //接收完成标记位9 l" z5 X }$ X, \
- uint8_t rx_buffer[100]; //接收缓存: P4 ]( P& [- l3 N, D" t
- char BUFFER_SIZE=100; //不定长数据的最大长度,设置为100则最大长度为100
复制代码
$ @9 n7 v9 A: P m( g' X& C7 F# |这里为什么要定义volatile 关键字呢? 主要是因为volatile 关键字提醒编译器定义的变量是易变的,编译后的程序每次需要存储或读取该变量时,会直接从变量地址读取数据。在中断或多线程中使用volatile关键字可以避免不同优化等级时程序出错,提高程序的鲁棒性。 接着对串口初始化添加一些代码,程序如下: - /* USART2 init function */) n% b& I0 |# f$ o( b
- static void MX_USART2_UART_Init(void)' J/ s, p+ F0 ~1 C
- {
0 v: t o2 U& k* s8 c$ ~ - . N/ |6 M" A* C+ Y2 E
- huart2.Instance = USART2;
4 q- }( {7 E1 W8 n$ b. U - huart2.Init.BaudRate = 115200;/ H4 I& f) S$ E$ T: z0 {5 o
- huart2.Init.WordLength = UART_WORDLENGTH_8B;' I) t( t5 N' l4 ^( N( `, q% f
- huart2.Init.StopBits = UART_STOPBITS_1;
. E8 [1 b( F: V/ \4 H - huart2.Init.Parity = UART_PARITY_NONE;
" K# G# s5 j$ k# o: ~ - huart2.Init.Mode = UART_MODE_TX_RX;+ {0 G7 [- f; u+ I: X* o! [
- huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;6 H; q* ]' z! p% i8 S1 U: n
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
; D( r* R+ B) V" w2 R' l( m$ z - if (HAL_UART_Init(&huart2) != HAL_OK)
4 d5 E; f' o J E3 @ - {
4 S* w, @/ a4 ^+ K, |( O5 ~ - _Error_Handler(__FILE__, __LINE__);
/ y. }+ B' W! D7 f5 y3 D; s - }
% k+ y+ E8 V: l" C* B S -
# i2 f# I/ i# v& v0 B' O$ P' M% t - /* 自己添加代码部分 */
5 N M z- Z6 g - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); //使能idle中断
1 X* ^" l5 a+ \/ k# `* N& ` - HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //打开DMA接收,数据存入rx_buffer数组中。
+ Y+ Y6 {0 s5 Y - * N! D+ _: o8 P- _( H6 Y: f
- }
复制代码3、接收函数我写在了另一个文件上,其他文件要用到上面 main文件里面定义的变量就要声明一个外部变量 - extern volatile uint8_t rx_len;
! z2 d3 L0 B! W" O+ ` - extern volatile uint8_t recv_end_flag;
]4 {. r6 p* u p6 o6 q6 v - extern uint8_t rx_buffer[100];- T2 @0 _9 N/ ~8 ~3 \) ?
- extern char BUFFER_SIZE;
复制代码 7 j1 ~9 Y, c+ g1 u" h! n, W" |
4、接着修改串口中断服务函数,在串口中断服务函数里添加接收代码,代码如下: - void USART2_IRQHandler(void)" ]( N( y, x- ]9 K+ K4 a
- {
2 I% k) d; u2 [) E - /* USER CODE BEGIN USART2_IRQn 0 */9 \: e }0 y1 U7 m/ @' V
- $ q9 e$ W( {, ~- X6 L
- /* 自己添加代码部分 */
, U1 _, _* K$ } - uint32_t tmp_flag = 0;
2 X2 ?2 {5 @5 ]4 a# k- P8 r - uint32_t temp;% Y% r3 `& k4 _! h8 h
- tmp_flag =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); //获取IDLE标志位+ t, a% W0 k8 S$ R6 z
- if((tmp_flag != RESET)) //idle标志被置位
$ x$ d8 [( Q5 E, j! ]* z - {
% q2 {) N* O e - __HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除标志位
2 F7 s' t6 T8 ], P$ e. U - temp = huart2.Instance->SR; //清除状态寄存器SR(F0的HAL库USART_TypeDef结构体中名字为ISR:USART Interrupt and status register),读取SR可以清楚该寄存器( Y. U0 ^/ |7 |* B( o5 ?
- temp = huart2.Instance->DR; //读取数据寄存器中的数据,读取DR(F0中为RDR:USART Receive Data register): o+ Z) ^1 p- t% U9 x
- HAL_UART_DMAStop(&huart2); temp = hdma_usart2_rx.Instance->CNDTR; //获取DMA中未传输的数据个数,NDTR寄存器分析见下面
+ R4 ~, Y: \: y. {3 B) W - rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数$ O) d% V7 }3 Z
- recv_end_flag = 1; //接受完成标志位置1 $ A4 n6 ?7 j4 k+ N/ \! r. W
- }* N, c7 U1 l. E4 w |* r/ [
- $ C. j! k$ f9 |4 \; l5 x
- /* USER CODE END USART2_IRQn 0 */ # D: F1 r4 ]# |9 T
- HAL_UART_IRQHandler(&huart2);! v$ \# s, f* w! o: x5 @
- /* USER CODE BEGIN USART2_IRQn 1 */
' r9 c( r, y3 F9 k0 \% |$ H+ x g8 \
* {. e# V3 a( v6 _- /* USER CODE END USART2_IRQn 1 */
6 N! S) S: T# m3 q - }
复制代码 ( _/ i! T5 e, X0 }3 }* v5 ]
上面的 CNDTR 寄存器在 DMA 通道结构体中定义了 CNDTR 寄存器,这个不同的芯片HAL库里面定义的命名有点不同,有兴趣的可以自己去查看一下,那为什么是未传输的数据数呢,STM32的中文手册给出了该寄存器的具体说明。(注意:建立工程的时候要添加串口TX RX 的DMA通道,以及打开DMA中断) - /**
' L& j! x5 u: J' Z% E - * @brief DMA Controller ~" v0 N6 c4 K- g" e1 A6 j
- */
2 c0 R2 g5 o j5 v3 { - typedef struct
: C- {) }; M* S1 A; _2 X# U - {
+ Q" d2 {7 R. }4 X8 A: r# K) S - __IO uint32_t CCR;
* w% Z% X8 u: }% t2 G* W - __IO uint32_t CNDTR;
* }' b& R4 z$ K J; N - __IO uint32_t CPAR;" V c- `, u [' W, M2 N g( c
- __IO uint32_t CMAR;
$ B' y0 B y7 s( i/ } - } DMA_Channel_TypeDef;
复制代码 0 _* `! [, c4 n+ f# r8 i
寄存器说明如下: 4 A; B. z0 T5 `" l' U# q; I
9 ?2 L5 H: z5 p R" Z7 {; F1 m5、接着编写接收处理函数,代码如下: - /***************************************************************( |! Q9 K x' c: L4 G, N0 u
- *函数名:Data_Turn) X4 ?4 f. {1 A+ l
- *输 入:无
. |# n& p/ Z6 v2 `% C$ M - *说 明:串口接收完成,返回串口查看接收情况) _, F$ P: G# {& N, ]
- *返回值:无
m$ d3 B& D" \/ w - **/$ |4 C; ]6 L6 Y( z' a6 H. ]& G. F( h1 |
- void Data_Turn(void)5 |$ r% h. I. U2 S1 ^3 u0 R* O$ t5 o
- {
' h% S1 M3 E3 b9 w7 ?$ `* c x; S
: N: P5 |! P- I- G7 u/ [: Z- if(recv_end_flag ==1)
3 {2 B+ ]: n6 q2 {$ ~6 `" O. J1 Z - {
. i, h4 Z/ |6 y - printf("rx_len=%d\r\n",rx_len); //打印接收长度, a. @# a3 k4 f' z
- HAL_UART_Transmit(&huart2,rx_buffer, rx_len,200); //接收数据打印出来3 w5 c7 b3 m" T+ o4 E" Q
- for(uint8_t i=0;i<rx_len;i++)) A! U( {, J! r& G
- {1 G% \( W+ N. H j0 M( k
- rx_buffer[i]=0; //清接收缓存
, i5 F" {1 ^* z' Z) c3 j" n4 ` - }" c; ?7 J! K8 P% C+ Q) ]& k
- rx_len=0; //清除计数
- C. \2 o Y) `- f - recv_end_flag=0; //清除接收结束标志位
: a' z0 B- T8 l& Y* @8 v - }
/ E9 g9 ?( y' S/ K - }
复制代码
& |5 r/ n$ u- d# \ 6.再主函数里的的while循环里再次打开DMA中断接收 - HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE); //重新打开DMA接收
复制代码 ! ^0 m8 K2 b k% t
运行效果如下图,我的代码是接收到Do-0:1字符串,判断字符串,返回我需要的字符串,效果正确。 - K R' ?5 b$ r
补充一点最近新发现的关于串口中断接收的问题: 串口中断接收如果使用HAL库的中断接收函数,接收到的数据量远小于设定要接收的数据量,串口一直处于Busy状态,会出现接收死循环的情况,接收的数据跟设定不符会出错 注意: 测试过程中发现,用接收串口助手的所有数据都没问题,不过接收模块的不定长数据时,如果这个数据之间包含回车换行符会接收不全,例如:AT\r\nOK\r\n,这个就只能接收到AT\r,具体什么原因造成的还没找出原因,有知道的可以告诉我一下。
- A m, @) @ }$ P |