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、串口收发测试

 

  对不对请您试试,我试是对的,谢谢!!!

作者:一名退休教师

物联沃分享整理
物联沃-IOTWORD物联网 » STM32L431串口中断接收与发送不定长度数据实战指南

发表回复