STM2G031-使用STM32CubeIDE进行串口通信实验(串口收发、接收不定长数据)

STM2G031-使用STM32CubeIDE进行串口通信实验

一、基础配置

模式选择 : 配置为异步模式Asynchronous

  • Asynchronous : 异步, 整个过程,不会阻碍发送者的工作。
  • Synchronous : 同步, 同步信息一旦发送,发送者必须等到应答,才能继续后续的行为。
  • Single Wire : 单总线, 半双工。
  • Multiprocessor Communication:多机通信
    在这里插入图片描述
    基本参数配置: 参数配置里面主要关注一下基本参数配置,其他的一些配置默认即可。
  • 波特率:可以根据实际情况进行选择
  • 字长:一般选择8位即可
  • 奇偶校验:一般选择无
  • 停止位:1位
    在这里插入图片描述

二、printf 重定向

在 Private includes 中引入头文件:

#include <stdio.h>

在 USER CODE BEGIN PD 添加下面代码:

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *F)
#endif

PUTCHAR_PROTOTYPE
{
    
    
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}

使用printf输出浮点数:
Project–>Properties–>C/C++ Build–>Settings–>MCU settings,如下图所示,勾选上即可。
在这里插入图片描述

实验一:通过串口发送数据

  • 基本配置
    在这里插入图片描述
  • 添加代码:
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

/* USER CODE BEGIN PD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *F)
#endif

PUTCHAR_PROTOTYPE
{
    
    
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}
/* USER CODE END PD */

/* USER CODE BEGIN 3 */
	HAL_Delay(500);
	printf("Hello World!\r\n");
}
/* USER CODE END 3 */

  • 实验现象:
    打开串口调试助手,即可看将以500ms为周期输出Hello World!
    在这里插入图片描述

实验二:接收数据

  • 基本配置:注意需要开启串口中断
    在这里插入图片描述
    在这里插入图片描述
  • 添加代码:
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

/* USER CODE BEGIN PD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *F)
#endif

PUTCHAR_PROTOTYPE
{
    
    
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint8_t RxBuff[1];		// 用来接收串口2发送的数据
/* USER CODE END PV */

/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, RxBuff, 1);
/* USER CODE END 2 */

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    

	if(huart->Instance == USART2)	// 判断是由哪个串口触发的中断
	{
    
    
		HAL_UART_Transmit(&huart2,RxBuff,1,100);	// 接收到数据马上使用串口2发送出去
		HAL_UART_Receive_IT(&huart2,RxBuff,1);		// 重新使能串口2接收中断
	}
}
/* USER CODE END 4 */

实验现象: 将串口接收到的数据,又通过该串口发送回去
在这里插入图片描述

实验三:串口接收不定长数据(定时器方式实现)

  • 基本配置:
    关于定时器的配置,在这里使用通用定时器3。如下图所示,配置一下时钟,内部时钟我们配置为64Mhz,定时器1ms产生一次中断(可以根据实际情况增大时间)。
    定时器的配置主要有两个:定时时间与是否重装定时器。
    定时频率 = 定时器时钟 / (预分频 +1) /(计数值 + 1 ) Hz。
    定时时间 = 1 / 定时频率 s。
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    添加代码:
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* USER CODE BEGIN PD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch,FILE *F)
#endif

PUTCHAR_PROTOTYPE
{
    
    
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
	return ch;
}
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint8_t RxBuff[1];      //进入中断接收数据的数组
uint8_t DataBuff[100]; //保存接收到的数据的数组
int RxLine=0;           //接收到的数据长度
int Rx_flag=0;					//接受到数据标志
/* USER CODE END PV */

/* USER CODE BEGIN 0 */
void printf_usart(void)
{
    
    
	printf("length=%d\r\n",RxLine);
	DataBuff[RxLine] = '\0';
		printf("Rxdata:%s\r\n",DataBuff);
	memset(DataBuff,0,sizeof(DataBuff));  //清空缓存数组
	//memset()作用:可以方便的清空一个结构类型的变量或数组。
	//例句:memset(aTxbuffer,0,sizeof(aTxbuffer))  用memset清空aTxbuffer。
	RxLine=0;  //清空接收长度
}
/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2,  (uint8_t *)RxBuff, 1);
  HAL_TIM_Base_Start_IT(&htim3);//开启定时器
  /* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    

	if(huart->Instance == USART2)	// 判断是由哪个串口触发的中�?
	{
    
    
		RxLine++;                      //每接收到一个数据,进入回调数据长度加1
		DataBuff[RxLine-1]=RxBuff[0];  //把每次接收到的数据保存到缓存数组
		Rx_flag=1;
		if(RxBuff[0]==0xff)            //接收结束标志位,这个数据可以自定义,根据实际需求,这里只做示例使用,不一定是0xff
		{
    
    
			printf_usart();
		}
		RxBuff[0]=0;
		HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuff, 1); //每接收一个数据,就打开一次串口中断接收,否则只会接收一个数据就停止接收
		__HAL_TIM_SET_COUNTER(&htim3, 1); // 计数清零,从头开始计
	}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    
    

    if (htim->Instance == TIM3){
    
    	// 判断是由哪个定时器触发的中断
    	if(Rx_flag==1)
		{
    
    
			printf_usart();
			Rx_flag=0;
		}
    }
}
/* USER CODE END 4 */

实验四:串口接收不定长数据(DMA方式实现)

  • 基本原理:
    DMA方式接收不定长数据要用到IDLE空闲中断,

空闲中断是接受数据后出现一个 byte 的高电平 (空闲) 状态, 就会触发空闲中断. 并不是空闲就会一直中断,准确的说应该是上升沿(停止位)后一个 byte,如果一直是低电平是不会触发空闲中断的(会触发 break中断)。所以为了减少误进入串口空闲中断,串口 RX 的 IO 管脚一定设置成 Pull-up<上拉模式>,串口空闲中断只是接收的数据时触发,发送时不触发。

说明白一点,就是我们在发送数据的时候,是一直在发送,不会有一帧这么长的空闲,当发送数据结束后,就会产生空闲中断,所以,可以通过空间中断来判断数据是否接收完成。

  • 优势:

相较于上面用定时器实现的方式,这种方法不需要一直产生中断,而占用cpu的资源,也省去了一个定时器。

目前使用DMA和空闲中断的方式,不知道遇到了什么问题,就是看不到现象…参考了很多资料,暂时还没实现,具体错误不知道出在哪里了,找了一天的错误了,快吐了,以后再说吧…

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/113754863