STM32L431串口中断接收与发送不定长度数据实战指南
stm32L431 串口中断收发是一个编程中经常遇到的问题,理想的结果是:
接收时:
只关心串口接收缓冲区中是否有来自主机或从机发来的信息,如果有就去读并处理。接收的具体操作由接收中断服务程序完成。
发送时:
只将要发送的写入发送缓冲区后就不管了,剩下的事由发送中断服务程序完成。
下图为本人上51单片机课时的PPT,说明环形收、发缓冲数组与输入、输出指针的关系。程序是51单片机的,
以下代码为STM32F103单片机中断程序(不是stm32L431程序,不要复制粘贴!!!)
void USART1_IRQHandler(void)
{
if(USART1->SR&(1<<5))//接收到数据
{
Recv_Buf[Recv_In_Pointer]=USART1->DR;
Recv_Data=Recv_Buf[Recv_In_Pointer];
Recv_In_Pointer=(Recv_In_Pointer+1)%RECV_BUF_LEN;
Recv_End=1; //置串行口接收完成标志
}
if(USART1->SR&(1<<6))// 发送中断
{
USART1->SR&=(~(1<<6)); //清中断发送完成中断标志(TC)
Trans_Out_Pointer=(Trans_Out_Pointer+1)%TRANS_BUF_LEN;
if (Trans_In_Pointer==Trans_Out_Pointer) //发送缓冲区中没有数据
{
Trans_End=1; //置串行口发送完成标志
USART1->CR1&=(~(1<<6)); //关闭发送完成中断允许(TCIE)
}
else //发送缓冲区中有数据
{
USART1->DR=Trans_Buf[Trans_Out_Pointer]; //发送缓冲区中的数据
Trans_End=0; //清串行口发送完成标志
}
}
}
1、由STM32CubeMX生成Keil工程“STM32L431_Uart”
2、新建“STM32L431_Uart.H、STM32L431_Uart.C”两个文件,添加“STM32L431_Uart.C"至Keil工程。“STM32L431_Uart.H、STM32L431_Uart.C”两个文件的代码(在后边)为:代码7、代码8
添加"McroLIB"
添加头文件"STM32L431_Uart.H"及源文件"STM32L431_Uart.C”所在的路径
3、在STM32CubeMX生成文件中添加代码
***所有添加的代码均放在 /* USER CODE BEGIN ———- */和/* USER CODE END ———- */之间,以防止STM32CubeMX生成文件时修改***
在“main.c”中添加代码1、2、3、4
代码1: 增加所需的头文件
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "STM32L431_Uart.H"
/* USER CODE END Includes */
代码2:初始化中断收发服务程序所需的收、发缓冲区数组及全局变量
/* USER CODE BEGIN 1 */
unsigned char Temp_Data;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
Init_Serial_Port();
/* USER CODE END 2 */
代码3:简单的测试程序。收什么发什么,ASC字符、汉字、控制字符均可。
/* USER CODE BEGIN WHILE */
printf("HelloWorld!\n");
while (1)
{
if (Recv_End == 1)
{
Temp_Data = Serial_In();
Serial_Out(Temp_Data);
}
/* USER CODE END WHILE */
代码4:支持printf()函数;
/* USER CODE BEGIN 4 */
int fputc(int ch, FILE * f)
{
Serial_Out(ch);
return ch;
}
int fgetc(FILE * f)
{
unsigned char Temp_Data;
Temp_Data = Serial_In();
return Temp_Data;
}
/* USER CODE END 4 */
在“stm32l4xx_it.c”中添加代码5、6
代码5:增加 包含void USART1_IRQHandler(void)所需”数组及全局变量“的头文件
/* USER CODE BEGIN Includes */
#include "STM32L431_Uart.H"
/* USER CODE END Includes */
代码6:串口1中断收发服务程序。
其中“HAL_UART_IRQHandler(&huart1);”由STM32CubeMX生成,由于在其前面有”return ;"存在,永远不会被调用,会产生本工程编译时唯一的警告“../Core/Src/stm32l4xx_it.c(233): warning: #111-D: statement is unreachable”,不用理会(管也没用,STM32CubeMX还改回来)。
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if((USART1->CR1&USART_CR1_RXNEIE)&&(USART1->ISR&USART_ISR_RXNE)) //接收到数据:CR1_RXNEIE和ISR_RXNE同时为1
{
Recv_Buf[Recv_In_Pointer]=USART1->RDR;//将接收到的数据存入接收缓冲器Recv_Buf
Recv_Data=Recv_Buf[Recv_In_Pointer];
Recv_In_Pointer=(Recv_In_Pointer+1)%RECV_BUF_LEN;//接收缓冲器输入指针加1
Recv_End=1;//置串行口接收完成标志
}
if((USART1->CR1&USART_CR1_TCIE)&&(USART1->ISR&USART_ISR_TC ))// 发送中断: CR1_TCIE和ISR_TC同时为1
{
Trans_Out_Pointer=(Trans_Out_Pointer+1)%TRANS_BUF_LEN;
if (Trans_In_Pointer==Trans_Out_Pointer) //发送缓冲区中没有等待的数据
{
USART1->ICR|=USART_ICR_TCCF; //写ICR_TCCF=1清中断发送完成中断标志TC
Trans_End=1; //置串行口发送完成标志
USART1->CR1&=(~USART_CR1_TCIE); //关闭发送完成中断允许(TCIE)
}
else //发送缓冲区中有数据
{
USART1->TDR=Trans_Buf[Trans_Out_Pointer]; //发送缓冲区中的数据(同时清中断标志TC)
Trans_End=0; //清串行口发送完成标志
}
}
return ;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
建立"STM32L431_Uart.H"并添加代码7
代码7:头文件
#ifndef __STML431_Uart_H
#define __STML431_Uart_H
//***************************用户设置串行口参数开始****************************
#define RECV_BUF_LEN 512//设置接收缓冲器长度
#define TRANS_BUF_LEN 512 //设置发送缓冲器长度
//***************************用户设置串行口参数结束****************************
//函数定义
extern void Init_Serial_Port(void);
extern unsigned char Serial_In(void);
extern void Serial_Out(unsigned char Serial_Out_Data);
extern unsigned char Hex_To_Ascii(unsigned char Hex_Data);
extern unsigned char Ascii_Char_To_Hex(unsigned char Ascii_Char, unsigned char Base);
extern void String_Out(unsigned char *String_Pointer);
extern unsigned char getch(void);
extern unsigned char Recv_2Byte(void);
extern unsigned int Recv_4Byte(void);
extern void Send_2Byte(unsigned char Temp_Send_Data);
extern void Send_4Byte(unsigned int Temp_Send_Data16);
extern void Send_6Byte(unsigned long Temp_Send_Data32);
extern void Send_8Byte(unsigned long Temp_Send_Data32);
extern unsigned long Input_Data(unsigned char Base, unsigned long Default_Data, unsigned char Input_Data_Bits);
extern unsigned char Input_String(unsigned char *Input_String_Pointer, unsigned char Sel_ECHO);
//变量定义
extern unsigned char Recv_Buf[RECV_BUF_LEN]; //串行口环形接收数据缓冲区
extern unsigned char Trans_Buf[TRANS_BUF_LEN]; //串行口环形发送数据缓冲区
extern unsigned int Recv_In_Pointer, Recv_Out_Pointer; //串行口环形接收缓冲区输入、输出指针。全局变量
extern unsigned int Trans_In_Pointer, Trans_Out_Pointer; //串行口环形发送缓冲区输入、输出指针。全局变量
extern unsigned char Recv_Data, Trans_Data; //串行口接收、发送缓冲器。全局变量
//串行口工作标志
extern unsigned char Recv_End; //串行口接收完成标志。全局变量
extern unsigned char Trans_End; //串行口发送完成标志。全局变量
#endif
建立"STM32L431_Uart.C"并添加代码8
代码8:C代码。本工程仅用前三个函数
void Init_Serial_Port(void)
unsigned char Serial_In(void)
void Serial_Out(unsigned char Serial_Out_Data)
在void Init_Serial_Port(void) 开“接收中断”(始终开“接收中断”);而“发送中断”在void Serial_Out(unsigned char Serial_Out_Data)中有发送数据时开“发送中断”,当发送结束时在串口发送中断服务程序中关“发送中断”。
#include "STM32L431_Uart.H"
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdlib.h"
#include "stdio.h"
#include <string.h>
#include "ctype.h"
unsigned char Recv_Buf[RECV_BUF_LEN]; //串行口环形接收数据缓冲区
unsigned char Trans_Buf[TRANS_BUF_LEN]; //串行口环形发送数据缓冲区
unsigned int Recv_In_Pointer, Recv_Out_Pointer; //串行口环形接收缓冲区输入、输出指针。全局变量
unsigned int Trans_In_Pointer, Trans_Out_Pointer; //串行口环形发送缓冲区输入、输出指针。全局变量
unsigned char Recv_Data, Trans_Data; //串行口接收、发送缓冲器。全局变量
//串行口工作标志
unsigned char Recv_End; //串行口接收完成标志。全局变量
unsigned char Trans_End; //串行口发送完成标志。全局变量
void Init_Serial_Port(void);
unsigned char Serial_In(void);
void Serial_Out(unsigned char Serial_Out_Data);
unsigned char Hex_To_Ascii(unsigned char Hex_Data);
unsigned char Ascii_Char_To_Hex(unsigned char Ascii_Char, unsigned char Base);
void String_Out(unsigned char *String_Pointer);
unsigned char Recv_2Byte(void);
unsigned int Recv_4Byte(void);
void Send_2Byte(unsigned char Temp_Send_Data);
void Send_4Byte(unsigned int Temp_Send_Data16);
void Send_6Byte(unsigned long Temp_Send_Data32);
void Send_8Byte(unsigned long Temp_Send_Data32);
unsigned long Input_Data(unsigned char Base, unsigned long Default_Data, unsigned char Input_Data_Bits);
unsigned char Input_String(unsigned char *Input_String_Pointer, unsigned char Sel_ECHO);
void Init_Serial_Port(void) //串行口初始化函数
{
Trans_End = 1;
Recv_End = 0;
Recv_In_Pointer = 0; //接收缓冲区输入、输出指针
Recv_Out_Pointer = Recv_In_Pointer;
Trans_In_Pointer = 0; //发送缓冲区输入、输出指针
Trans_Out_Pointer = Trans_In_Pointer;
USART1->CR1 |= USART_CR1_RXNEIE;
}
//*****************************************************************************
//*****************************************************************************
unsigned char Serial_In(void)
{
unsigned char Temp_Data;
while ( Recv_Out_Pointer == Recv_In_Pointer )
{
}
//没有数据等待。
Temp_Data = Recv_Buf[Recv_Out_Pointer]; //从接收缓冲区中取数据
Recv_Out_Pointer = (Recv_Out_Pointer + 1) % RECV_BUF_LEN; //
if ( Recv_Out_Pointer == Recv_In_Pointer )
{
Recv_End = 0;
}
return Temp_Data;
//返回接收数据
}
/******************************************************************************
函数名称: serial_out
功能描述: 从串行口输出一个字节,如输出缓冲器中有数据则写入缓冲器;没有则直接发送
入口参数: 待发送的数据
返回值: 无
调用函数: 无
全局变量: trans_end;
trans_in_pointer;trans_out_pointer;
trans_buf[];
局部变量: 无
******************************************************************************/
void Serial_Out(unsigned char Serial_Out_Data)
{
while ( ((Trans_In_Pointer + 1) % TRANS_BUF_LEN) == Trans_Out_Pointer )
{
//发送缓冲区满时等待
}
if (Trans_End == 1)
{
USART1->TDR = Serial_Out_Data;
USART1->CR1 |= 1 << 6; //发送完成中断使能(TCIE)
Trans_Buf[Trans_In_Pointer] = Serial_Out_Data; //发送数据进入发送缓冲区(在中断程序发送数据)
Trans_In_Pointer = (Trans_In_Pointer + 1) % TRANS_BUF_LEN; //发送数据加1
Trans_End = 0; //清发送结束标志
}
else
{
Trans_Buf[Trans_In_Pointer] = Serial_Out_Data; //发送数据进入发送缓冲区(在中断程序发送数据)
Trans_In_Pointer = (Trans_In_Pointer + 1) % TRANS_BUF_LEN; //发送数据加1
}
}
unsigned char Hex_To_Ascii(unsigned char Hex_Data)
{
unsigned char Ascii_Code;
if (Hex_Data <= 9)
{
Ascii_Code = Hex_Data + 0x30;
}
else
{
Ascii_Code = Hex_Data + 0x37;
}
return Ascii_Code;
}
unsigned char Ascii_Char_To_Hex(unsigned char Ascii_Char, unsigned char Base)
{
unsigned char Outpt_Data = 0;
if (Base == 16)
if ((Ascii_Char >= 'A') && (Ascii_Char <= 'F'))
Outpt_Data = 10 + Ascii_Char - 'A';
if (Base == 16)
if ((Ascii_Char >= 'a') && (Ascii_Char <= 'f'))
Outpt_Data = 10 + Ascii_Char - 'a';
if ((Ascii_Char >= '0') && (Ascii_Char <= '9'))
Outpt_Data = Ascii_Char - '0';
return Outpt_Data;
}
unsigned char Recv_2Byte(void)
{
unsigned char Temp_Recv_Data, Temp_Data_H, Temp_Data_L;
Temp_Recv_Data = Serial_In();
Temp_Data_H = Ascii_Char_To_Hex(Temp_Recv_Data, 16);
Temp_Data_H = Temp_Data_H << 4;
Temp_Recv_Data = Serial_In();
Temp_Data_L = Ascii_Char_To_Hex(Temp_Recv_Data, 16);
Temp_Recv_Data = Temp_Data_H | Temp_Data_L;
return Temp_Recv_Data;
}
//*****************************************************************************
unsigned int Recv_4Byte(void)
{
unsigned int Temp_Data;
Temp_Data = Recv_2Byte();
Temp_Data = Temp_Data << 8;
Temp_Data = Temp_Data | Recv_2Byte();
return Temp_Data;
}
void Send_2Byte(unsigned char Temp_Send_Data)//ok
{
unsigned char Temp_Data;
Temp_Data = Temp_Send_Data >> 4;
Temp_Data = Hex_To_Ascii(Temp_Data);
Serial_Out(Temp_Data);
Temp_Data = Temp_Send_Data & 0x0f;
Temp_Data = Hex_To_Ascii(Temp_Data);
Serial_Out(Temp_Data);
}
void Send_4Byte(unsigned int Temp_Send_Data16)
{
Send_2Byte((Temp_Send_Data16 & 0xff00) >> 8);
Send_2Byte((Temp_Send_Data16 & 0x00ff) >> 0);
}
void Send_8Byte(unsigned long Temp_Send_Data32)
{
Send_4Byte((Temp_Send_Data32 & 0xffff0000) >> 16);
Send_4Byte((Temp_Send_Data32 & 0x0000ffff) >> 0);
}
void Send_6Byte(unsigned long Temp_Send_Data32)
{
Send_2Byte((Temp_Send_Data32 & 0x00ff0000) >> 16);
Send_4Byte((Temp_Send_Data32 & 0x0000ffff) >> 0);
}
void String_Out(unsigned char *String_Pointer)
{
while (1)
{
if (*String_Pointer != 0x00)
{
Serial_Out(*String_Pointer);
String_Pointer++;
}
else
{
break;
}
}
}
/******************************************************************************
函数名称:input_data
功能描述:从串行口输入指定进制的数字,以回车为结束符
入口参数:
返回值:
调用函数:
全局变量:
局部变量:
******************************************************************************/
unsigned long Input_Data(unsigned char Base, unsigned long Default_Data, unsigned char Input_Data_Bits)
{
unsigned long Temp_Data = 0;
unsigned char Key_Input_Char, First_Data_Flag = 0, i = 0;
i = 0;
while ( i < Input_Data_Bits )
{
Key_Input_Char = Serial_In();
i++;
switch ( Base )
{
case 0 : //BCD码
if ( isdigit(Key_Input_Char) )
{
Serial_Out(Key_Input_Char);
Temp_Data = Temp_Data * 16 + Ascii_Char_To_Hex(Key_Input_Char, Base); //BCD码
First_Data_Flag = 1;
}
break;
case 10 :
if ( isdigit(Key_Input_Char) )
{
Serial_Out(Key_Input_Char);
Temp_Data = Temp_Data * Base + Ascii_Char_To_Hex(Key_Input_Char, Base);
First_Data_Flag = 1;
}
break;
case 16 :
if ( isxdigit(Key_Input_Char) )
{
Serial_Out(Key_Input_Char);
Temp_Data = Temp_Data * Base + Ascii_Char_To_Hex(Key_Input_Char, Base);
First_Data_Flag = 1;
}
break;
default :
break;
}
if ( Key_Input_Char == 0x0d )
{
Serial_Out(Key_Input_Char);
break;
}
}
if ( First_Data_Flag == 0 )
{
Temp_Data = Default_Data;
}
return Temp_Data;
}
unsigned char Input_String(unsigned char *Input_String_Pointer, unsigned char Sel_ECHO)
{
unsigned char Key_Input_Char, First_Data_Flag = 0, i = 0;
while ( i < 200 )
{
Key_Input_Char = Serial_In();
if (Sel_ECHO == 1)
{
Serial_Out(Key_Input_Char);
}
if ( Key_Input_Char == 0x0d )
{
*Input_String_Pointer = NULL; //字符串结束标志
Serial_Out(Key_Input_Char);
break;
}
First_Data_Flag = 1;
*Input_String_Pointer = Key_Input_Char;
Input_String_Pointer++;
}
if ((First_Data_Flag == 0) || (i >= 200))
{
return 0;//返回0表示输入字符串无效
}
return 1;//返回1表示输入字符串有效
}
//------------------------------------------------------------
4、编译后下载
5、串口收发测试
对不对请您试试,我试是对的,谢谢!!!
作者:一名退休教师