基于中断/DMA方式的串口通信

一、实验任务:

了解串口协议和RS-232标准,以及RS232电平与TTL电平的区别;了解"USB/TTL转232"模块(以CH340芯片模块为例)的工作原理。
使用HAL库(或标准库)方式,设置USART1 波特率为115200,1位停止位,无校验位,分别采用中断方式、DMA方式完成下列任务:

STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;
实验基本步骤

  1. 初始化串口USART1:
    使用HAL库初始化USART1串口,设置波特率、数据位、停止位和无校验位。
  2. 中断方式实现:
    a. 启用USART1的接收中断(USARTITRXNE)以便监听上位机发送的字符。
    b. 在接收中断处理函数中,检查接收到的字符,如果是"stop",则停止发送;如果是"start",则继续发送。
    c. 在主循环中,不断发送"hello windows!"。
  3. DMA方式实现:
    a. 启用USART1的DMA传输功能。
    b. 配置一个DMA通道,使其从一个内存缓冲区发送数据到USART1。
    c. 在主循环中,不断发送"hello windows!"。

二、原理

首先,让我们来了解串口协议和RS-232标准,以及RS232电平与TTL电平的区别。

1、串口协议和RS-232标准

串口通信是一种用于在设备之间传输数据的通信协议。
RS-232(Recommended Standard 232)是一种常见的串口通信标准,通常用于在计算机和外部设备之间建立连接。RS-232标准定义了串口通信所需的物理和电气特性,例如数据位、停止位、波特率和数据传输的电平标准。
RS232电平与TTL电平的区别:

2、RS-232和TTL(Transistor-Transistor Logic)电平之间有一些重要的区别

标准名称逻辑1逻辑0
TLL2.4V~5V0V~0.5V
RS-232-15V~3V+3V~+15V

1.RS-232电平是基于电压的,通常使用正负12V来表示逻辑1和逻辑0。
2.TTL电平是基于逻辑电平的,通常使用0V(地)和3.3V或5V来表示逻辑1和逻辑0。

通信设备之间的电平标准必须匹配,否则需要使用电平转换器或适配器来使它们能够进行正常通信。"USB/TTL转232"模块(例如CH340芯片模块)是一种常见的适配器,它可以将USB接口上的TTL电平转换为RS-232电平,以便与RS-232设备进行通信。

3、"USB/TTL转232"模块(以CH340芯片模块为例)的工作原理

USB主机检测到USB转串口设备插入后,首先会对设备复位,然后开始USB枚举过程。USB枚举时过程会获取设备描述符、配置描述符、接口描述符等。描述符中会包含USB设备的厂商ID,设备ID和Class类别等信息。操作系统会根据该信息为设备匹配相应的USB设备驱动。

USB虚拟串口的实现在系统上依赖于USB转串口驱动,一般由厂家直接提供,也可以使用操作系统自带的CDC类串口驱动等。驱动主要分为2个功能,其一注册USB设备驱动,完成对USB设备的控制与数据通讯,其二注册串口驱动,为串口应用层提供相应的实现方法。

4、DMA

DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节。
我们知道,数据传输,首先需要的是数据的源地址、数据传输位置的目标地址、传递数据多少的数据传输量、进行多少次传输的传输模式。DMA所需要的核心参数,便是这四个。
每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。

三、CubeMX配置

创建新项目
选择自己使用的芯片

配置RCC

配置高速时钟
在这里插入图片描述

打开USART1

在串口配置这里,选择串口1,使能串口1
1、点击MODE为异步通信(Asynchronous)
2、基础参数:波特率115200bits/s,传输数据长度为8Bit,奇偶校验无,停止位1,接收发送都使能
在这里插入图片描述

使能串口中断

在这里插入图片描述

DMA添加通道

ADD->选择
在这里插入图片描述

时钟数的配置

在这里插入图片描述

文件设置

在这里插入图片描述

在这里插入图片描述
打开工程

四、keil中代码

1、DMA方式接口函数

串口DMA方式发送函数:HAL_UART_Transmit_DMA
在这里插入图片描述
串口DMA方式接收函数:HAL_UART_Receive_DMA
在这里插入图片描述
获取未传输数据个数函数:_HAL_DMA_GET_COUNTER(HANDLE)
在这里插入图片描述

2、main.c


/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */


/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口数据存放的数组
/* USER CODE END 0 */
int strEqual(char rcData[6],char rcData2[6])//判断俩个数据是否完全相同
	{
	for(uint8_t i = 0 ; i < 6 ; i++){
		if (rcData[i] != rcData2[i]) return 0;
	}
	return 1;
}
/**
  * @brief  The application entry point.
  * @retval int
  */
uint8_t message[] = "hello windows!\r\n";  //定义数据发送数组
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */
HAL_UART_Receive_DMA(&huart1,(uint8_t*)&rx_buf,5);//设置DMA接收到的数据存放在rx_buf中
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  
      if(flag==1)//接收到start开始发送数组
	  {
	    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)&message, sizeof(message));
	    HAL_Delay(600);
	  }
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rx_buf,"stop!")==1)
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rx_buf,"start")==1)
	{
		flag=1;
	}
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

3、编译

没有错误,没有警告
在这里插入图片描述

4、打开flymcu烧录

在这里插入图片描述

五、连线与测试效果

1、连线

在cubemx可以看到
USART1是PA9为usart1_tx,PA10为usart1_rx
芯片板子上的rx连串口线的tx。
在这里插入图片描述

打开串口调试助手
刚开始没有成功,重新试了很多遍,然后发现我之前在串口助手这儿勾选了加回车换行(这意味着我发送给stm32的数据都添加了换行符\n,所以与发送的不同,所以就没成功,取消勾选就可以了)
在这里插入图片描述

六、Keil仿真逻辑分析仪功能观察串口输出波形

参考:Keil虚拟仿真逻辑仪和真实逻辑仪分析串口波形
在这里插入图片描述

分析:时序状态正确
波特率可以使用如下公式进行计算:

Baud,rate = frac {传输速率} {log_2 (ext {符号个数})}

六、出现的问题

1、端口显示“非旺玖原装的PL2303,请联系供应商”

这是因为我安装的驱动版本过高,选择低版本的就成功了。
下载:
链接:https://pan.baidu.com/s/1uzw4xOeYJb0UEg9w800GSg?pwd=htyx
提取码:htyx
下载安装后
在设备管理器点击端口->非旺玖原装的PL2303,请联系供应商
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
选择版本更低的那个
在这里插入图片描述

2、FlyMcu烧录不成功

在烧录时,BOOT0先置为1,在烧录完成后,需要传输数据时,再将BOOT0置为0,然后按下复位键,才能传输数据
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值