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

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

  • 前言
  • STM32串口通信硬件以及通信协议
    • 串口协议
    • STM32F103串口通信协议
      • 数据帧格式
      • 通信流程
      • 错误处理
      • 示例代码
    • RS-232标准
      • 电气特性
      • 信号传输方式
      • 接口连接
      • 应用领域
    • RS232电平与TTL电平的区别
      • RS232电平
      • TTL电平
      • 对比
  • 基于HAL库中断方式进行串口通信
    • STM32CubeMX配置
    • 程序
    • 实验效果
  • 基于HAL库实现DMA串口通信
    • DMA(Direct Memory Access ) 简介
    • STM32CubeMX配置
    • 程序
    • 实验效果
  • 用Keil的软件仿真逻辑分析仪功能观察串口输出波形
  • 总结

前言

STM32是一款基于ARM Cortex-M处理器的32位微控制器系列,具有丰富的外设和强大的性能,广泛应用于嵌入式系统开发。串口通信是嵌入式系统中常用的通信方式之一,可以实现与外部设备的数据交互。
本实验旨在介绍如何在STM32微控制器上使用中断和DMA(直接内存访问)方式进行串口通信。中断方式通过使能串口接收中断,当接收到数据时,通过中断服务函数进行数据处理。DMA方式则通过配置DMA通道,实现数据的直接传输,减轻CPU的负担,提高通信效率。
在本实验中,我将使用STM32的内部串口模块和相关外设,通过中断和DMA方式实现串口通信。首先,我将介绍STM32串口模块的配置和初始化。然后,我们将详细讲解中断方式和DMA方式的实现步骤和原理,并给出相应的代码示例。最后,我们将进行实验验证,通过发送和接收数据,检验通信功能的正确性和稳定性。
通过完成本实验,够掌握STM32中断和DMA方式的串口通信原理和实现方法,为后续的嵌入式系统开发奠定基础。同时,也将深入了解STM32的外设配置和使用,提升对嵌入式系统的理解和实践能力。

STM32串口通信硬件以及通信协议

串口协议

STM32F103串口通信协议

STM32F103微控制器的串口通信协议,该协议使用UART串口通信进行数据传输,并定义了数据帧格式、通信流程和错误处理等内容。

数据帧格式

数据帧采用以下格式进行传输:

起始位 数据位 奇偶校验位 停止位
1位 8位 1位 1位
  • 起始位:逻辑低电平表示起始位,用于标识数据帧的开始。
  • 数据位:8位数据位用于传输实际的数据。
  • 奇偶校验位:用于检测数据传输过程中的错误,可以选择奇校验、偶校验或无校验。
  • 停止位:逻辑高电平表示停止位,用于标识数据帧的结束。

通信流程

  1. 初始化串口通信参数,包括波特率、数据位、校验位和停止位等。
  2. 发送端准备数据,将数据按照数据帧格式组织,并发送给接收端。
  3. 接收端接收数据,检查起始位、校验位和停止位的正确性。
  4. 如果数据校验正确,接收端将数据提取出来进行处理;如果数据校验错误,接收端发送错误消息给发送端。
  5. 发送端和接收端通过该协议进行连续的数据传输。

错误处理

在数据传输过程中,可能会发生以下错误情况:

  • 起始位错误:接收端未正确检测到起始位,导致数据解析错误。
  • 校验位错误:接收端在校验数据时发现错误,表明数据传输过程中发生了错误。
  • 停止位错误:接收端未正确检测到停止位,导致数据解析错误。
    在出现错误时,接收端应向发送端发送错误消息,以便重新发送数据或采取其他纠错措施。

示例代码

以下是使用STM32Cube HAL库配置STM32F103串口通信的示例代码:

// 初始化串口通信
void UART_Init() 
{
  huart.Instance = USART1;
  huart.Init.BaudRate = 9600;
  huart.Init.WordLength = UART_WORDLENGTH_8B;
  huart.Init.StopBits = UART_STOPBITS_1;
  huart.Init.Parity = UART_PARITY_NONE;
  huart.Init.Mode = UART_MODE_TX_RX;
  huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&huart);
}
// 发送数据
void UART_SendData(uint8_t* data, uint16_t length) 
{
  HAL_UART_Transmit(&huart, data, length, HAL_MAX_DELAY);
}
// 接收数据
void UART_ReceiveData(uint8_t* data, uint16_t length)
 {
  HAL_UART_Receive(&huart, data, length, HAL_MAX_DELAY);
}

以上代码演示了如何使用STM32Cube HAL库配置和使用串口通信功能。你可以根据具体的需求进行修改和扩展。
以上就是基于STM32F103的串口通信协议的介绍,通过该协议可以实现可靠的数据传输和错误处理。你可以根据实际应用需求进行相应的调整和扩展。

RS-232标准

RS-232(Recommended Standard 232)是一种常用的串行通信标准,用于在计算机和外部设备之间进行数据传输。它定义了电气特性、信号传输方式和接口连接等方面的规范。

电气特性

RS-232标准规定了通信线路的电气特性,包括信号电平、波特率和时钟频率等。根据RS-232标准,信号电平分为正负两种,分别表示逻辑1和逻辑0。通常,正电平表示逻辑0,负电平表示逻辑1。波特率是指数据传输的速率,通常以每秒传输的位数(bps)表示。时钟频率则是指传输中使用的时钟信号频率。

信号传输方式

RS-232标准使用异步传输方式,即每个数据字节之间没有固定的时间间隔。数据传输以数据帧为单位,每个数据帧包括起始位、数据位、校验位和停止位。起始位用于标识数据帧的开始,停止位用于标识数据帧的结束。数据位用于传输实际的数据,校验位用于检测数据传输中的错误。

接口连接

RS-232标准规定了连接计算机和外部设备的接口连接方式和引脚定义。一般来说,RS-232标准使用9针或25针的D型插座连接器。其中,9针的连接器常用于较新的设备,而25针的连接器则常用于较旧的设备。接口连接的引脚定义包括数据线、控制线和地线等。
9线连接示意图:
STM32—基于中断/DMA方式的串口通信_第1张图片

应用领域

由于其广泛的应用和成熟的技术,RS-232标准在许多领域中仍然被广泛使用。它常用于计算机与打印机、调制解调器、终端设备和工业自动化设备等外部设备之间的通信。尽管现在有许多新的通信标准和接口出现,但RS-232标准仍然在许多传统应用和设备中发挥着重要作用。
以上就是RS-232标准的介绍,该标准定义了串行通信的电气特性、信号传输方式和接口连接等方面的规范。通过了解RS-232标准,我们可以更好地理解和应用串行通信技术。
STM32—基于中断/DMA方式的串口通信_第2张图片

RS232电平与TTL电平的区别

RS232电平和TTL电平是两种常见的串行通信电平标准,它们在电气特性、电平范围和使用环境等方面存在一些区别。

RS232电平

  • 电气特性:RS232电平使用正负电平表示逻辑1和逻辑0,通常正电平表示逻辑0,负电平表示逻辑1。根据RS232标准,信号电平范围在-15V至+15V之间。
  • 电平范围:RS232电平的电压范围较大,可以在较长距离上进行可靠的数据传输。
  • 使用环境:由于RS232电平的电压范围较大,它主要用于长距离和工业环境中的通信应用。

TTL电平

  • 电气特性:TTL电平使用0V和5V(或3.3V)的电平表示逻辑1和逻辑0。通常0V表示逻辑0,5V(或3.3V)表示逻辑1。
  • 电平范围:TTL电平的电压范围较小,一般在0V至5V(或3.3V)之间。
  • 使用环境:由于TTL电平的电压范围较小,它主要用于短距离和低功耗的通信应用,如嵌入式系统和电子设备之间的通信。

对比

特性 RS232电平 TTL电平
电气特性 正负电平 0V和5V(或3.3V)
电平范围 -15V至+15V 0V至5V(或3.3V)
适用环境 长距离和工业环境 短距离和低功耗环境

RS232和TTL电平在电气特性、电平范围和使用环境方面存在明显的区别。根据具体的应用需求,我们可以选择适合的电平标准来进行串行通信。需要注意的是,在使用不同电平标准时,需要进行电平转换以确保正常的数据传输。

基于HAL库中断方式进行串口通信

STM32CubeMX配置

STM32CubeMX选择单片机(此处以STM32F103C8T6型号单片机为例)
STM32—基于中断/DMA方式的串口通信_第3张图片
设置RCC,使用外部高速时钟
STM32—基于中断/DMA方式的串口通信_第4张图片
设置SYS
STM32—基于中断/DMA方式的串口通信_第5张图片
设置USART(波特率,校验位等参数)
STM32—基于中断/DMA方式的串口通信_第6张图片

设置NVIC,使USART使能
STM32—基于中断/DMA方式的串口通信_第7张图片
创建代码
STM32—基于中断/DMA方式的串口通信_第8张图片
在main函数前面定义全局变量

char c;//指令 0:停止  1:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送

在main函数中设置接收中断
函数:

 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

函数功能
该函数用于通过中断方式接收一定长度的数据。当接收到指定长度的数据或发生错误时,会触发相应的中断回调函数。
参数

  • huart: UART_HandleTypeDef类型的指针,表示UART控制器的句柄。
  • pData: uint8_t类型的指针,表示接收数据的缓冲区指针。
  • Size: uint16_t类型的参数,表示要接收的数据的长度。
    返回值
  • HAL_OK:函数执行成功。
  • HAL_BUSY:UART正忙,不能执行接收操作。
  • HAL_ERROR:函数执行出错。

说明
该函数会启动UART接收,并启用接收完成中断。当接收到指定长度的数据时,会触发UART_RX_CPLT_Callback()回调函数,用户可以在回调函数中对接收到的数据进行处理。
需要注意的是,为了使能接收完成中断,需要提前在中断优先级组配置中设置相应的中断优先级,以便正确处理中断。
示例

uint8_t rxBuffer[10];
UART_HandleTypeDef huart;
// 初始化UART句柄和其他相关配置
// 启动接收10个字节的数据
HAL_UART_Receive_IT(&huart, rxBuffer, 10);
// 接收完成中断回调函数
void UART_RX_CPLT_Callback(UART_HandleTypeDef *huart)
{
    // 在这里处理接收到的数据
}

在while循环中添加一下代码

if(flag==1)
{
//发送信息
  HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF); 
//延时
  HAL_Delay(1000);
};

可能出现以下报错
STM32—基于中断/DMA方式的串口通信_第9张图片

解决方法
添加以下头文件即可

#include 

STM32—基于中断/DMA方式的串口通信_第10张图片

程序

在main函数下面添加以下代码:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为0时,发送提示并改变flag
	if(c=='0')
	{
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	//当输入的指令为1时,发送提示并改变flag
	else if(c=='1')
	{
		flag=1;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF); 
	}
	//当输入不存在指令时,发送提示并改变flag
	else 
	{
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF); 
	}
	//重新设置中断
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);  
}

实验效果

编译
STM32—基于中断/DMA方式的串口通信_第11张图片

基于HAL库实现DMA串口通信

DMA(Direct Memory Access ) 简介

Direct Memory Access(DMA)是一种计算机系统中用于数据传输的技术。它允许数据在外设和内存之间直接进行传输,而无需CPU的干预。DMA技术可以提高数据传输的效率,减轻CPU的负担。
DMA的工作原理
DMA的工作原理如下:

  1. CPU配置DMA控制器,指定数据的源地址、目的地址和传输长度等参数。
  2. DMA控制器根据CPU的配置,直接从源地址读取数据,然后将数据写入目的地址。
  3. 一旦数据传输完成,DMA控制器会发出中断信号通知CPU。

DMA的优势
DMA技术相比于CPU直接处理数据传输具有以下优势:

  • 减轻CPU负担:DMA技术可以在数据传输过程中不需要CPU的干预,从而释放CPU的处理能力,使其可以同时执行其他任务。
  • 高效的数据传输:DMA可以实现高速、连续的数据传输,而不会因为CPU的繁忙而导致传输延迟。
  • 灵活性:DMA可以支持多种数据传输模式,如单次传输、循环传输和块传输等,以满足不同的应用需求。

DMA的应用领域
DMA技术广泛应用于各种领域,包括但不限于以下方面:

  • 音频和视频处理:DMA技术可以实现高速的音频和视频数据传输,用于实时的音频和视频处理。
  • 网络通信:DMA技术可以用于网络数据包的接收和发送,提高网络通信的效率。
  • 存储设备:DMA技术可以用于硬盘、固态硬盘等存储设备的数据读写操作,提高数据传输速度。
  • 图形处理:DMA技术可以用于图形处理器(GPU)和显示器之间的数据传输,提高图形渲染的效率。

DMA的应用示例
下表是一些常见的DMA应用示例:

应用示例 描述
音频播放 使用DMA从内存中读取音频数据,并传输给音频编解码器进行播放。
图像采集 使用DMA从图像传感器中读取图像数据,并传输到内存或显示器进行处理或显示。
存储器备份 使用DMA从一个存储设备读取数据,并将数据直接传输到另一个存储设备进行备份。
网络数据传输 使用DMA从网络接口读取数据包,并将数据包传输到内存进行处理或发送。

以上就是对Direct Memory Access(DMA)的简介。DMA技术可以提高数据传输的效率,减轻CPU的负担,广泛应用于各个领域。

STM32CubeMX配置

选择和上面相同的单片机,创建工程
此处省略
配置RCC,开启外部高速时钟
STM32—基于中断/DMA方式的串口通信_第12张图片
配置USART1,选择异步通信
STM32—基于中断/DMA方式的串口通信_第13张图片
其余参数默认即可
添加两个通信信道
STM32—基于中断/DMA方式的串口通信_第14张图片
然后生成代码即可!

程序

代码解释
HAL_UART_Transmit_DMA()
函数原型**

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

函数说明**
使用DMA传输UART数据,将指定长度的数据从pData缓冲区通过DMA传输到UART外设寄存器。
参数说明

  • huart: UART_HandleTypeDef结构体指针,表示UART外设句柄
  • pData: 数据缓冲区指针,需要传输的数据存储在这个缓冲区
  • Size: 需要传输的数据长度

返回值

  • HAL_OK: 传输成功
  • HAL_ERROR: 传输失败
  • HAL_BUSY: DMA传输正在进行中
  • HAL_TIMEOUT: 传输超时
    示例
uint8_t tx_buffer[] = "Hello World";
HAL_StatusTypeDef state;
state = HAL_UART_Transmit_DMA(&huart2, (uint8_t*)tx_buffer, strlen(tx_buffer));
if(state != HAL_OK) {
  // error handling 
}

首先定义一个字符串缓冲区,然后调用HAL_UART_Transmit_DMA函数通过DMA将缓冲区数据发送到UART外设。

HAL_UART_Receive_DMA():串口DMA模式接收

函数原型

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

函数说明
使用DMA从UART外设接收指定长度的数据,并存储到pData缓冲区。
参数说明

  • huart: UART_HandleTypeDef结构体指针,表示UART外设句柄
  • pData: 接收数据缓冲区指针
  • Size: 需要接收的数据长度

返回值

  • HAL_OK: 接收成功
  • HAL_ERROR: 接收失败
  • HAL_BUSY: DMA传输正在进行中
  • HAL_TIMEOUT: 接收超时
    示例
uint8_t rx_buffer[100];
HAL_StatusTypeDef state;
state = HAL_UART_Receive_DMA(&huart2, rx_buffer, 100);
if(state != HAL_OK) {
  // error handling
}
// 在DMA完成后,rx_buffer中存储了接收到的数据

通过HAL_UART_Receive_DMA函数启动DMA,从UART外设接收100字节数据到rx_buffer缓冲区。

实验任务
STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;
编写代码的思路

  1. 定义发送和接收缓冲区,以及标志变量is_sending。
  2. 配置UART,设置接收中断。
  3. 发送任务:
    • 检查is_sending标志,如果为1则继续发送;否则跳出。
    • 发送字符串"hello windows!"。
    • 判断发送完成标志,如果为1则清除标志并跳回开始发送。
  4. 接收任务:
    • 在接收中断服务函数中,将接收到的数据保存到接收缓冲区。
    • 判断接收到的字符,如果为’stop’则设置is_sending为0;如果为’start’则设置为1。
  5. 主函数中循环调用发送和接收任务。

代码框架

uint8_t tx_buf[] = "hello windows!";
uint8_t rx_buf;
uint8_t is_sending = 1;
void UART_Send(void) {
  if(is_sending == 1) {
    HAL_UART_Transmit(&huart, tx_buf, sizeof(tx_buf), 1000); 
  }
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  if(rx_buf == 'stop') 
    is_sending = 0;
  else if(rx_buf == 'start')
    is_sending = 1;
}
int main(void) {
  while(1) {
    UART_Send();
    UART_Receive(); 
  }
}

通过HAL库完成UART发送和接收,实现PC与STM32的双向通信控制。
最终完成后main函数中的代码:


#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
 

void SystemClock_Config(void);
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口数据存放的数组
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;
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rx_buf,"stop!"))
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rx_buf,"start"))
	{
		flag=1;
	}
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
int main(void)
{
  HAL_Init();
  uint8_t message[] = "hello windows!\n";  //定义数据发送数组
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//设置DMA接收到的数据存放在rx_buf中
  while (1)
  {
      if(flag==1)
	  {
	    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
	    HAL_Delay(600);
	  }
  }
}

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_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  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_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

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 */

实验效果

用Keil的软件仿真逻辑分析仪功能观察串口输出波形

参考文章:
链接: https://blog.csdn.net/weixin_63019977/article/details/133749827?spm=1001.2014.3001.5502
得到的波形
STM32—基于中断/DMA方式的串口通信_第15张图片
由图可知时序状态正确,波特率实际为0.983689Hz

总结

在进行STM32基于中断/DMA方式的串口通信实验时,我得出了以下几点总结和经验:

  1. 选择合适的串口通信协议:根据应用需求选择合适的串口通信协议,如UART、USART、SPI、I2C等。每种协议都有其特点和适用场景,需要根据具体情况进行选择。
  2. 配置串口通信参数:在实验中,需要正确配置串口的波特率、数据位、停止位、校验位等通信参数。确保发送端和接收端的通信参数一致,以确保数据的正确传输。
  3. 中断或DMA配置:根据实验需求,选择中断方式或DMA方式进行数据的接收和发送。中断方式适用于实时性要求较高的场景,而DMA方式适用于大量数据传输的场景。配置中断或DMA通道,设置传输参数,并编写相应的中断服务函数或DMA传输完成的中断回调函数。
  4. 数据处理和拆包:在接收数据时,根据通信协议和数据格式进行正确的数据处理和拆包。根据实际应用需求,可以使用缓冲区或队列来存储接收到的数据,以便进行后续的处理和解析。
  5. 错误处理和容错机制:在实验中,需要考虑错误处理和容错机制。例如,检测和处理数据丢失、溢出、校验错误等情况,以提高数据传输的可靠性和稳定性。
  6. 性能优化和测试:在实验中,可以进行性能优化和测试来评估和改进串口通信的性能。例如,通过调整缓冲区大小、优化中断或DMA配置、使用流控制等方法,提高数据传输的效率和稳定性。

总体而言,基于中断/DMA方式的串口通信实验需要根据具体应用需求选择合适的通信协议和方式,并正确配置参数和处理数据。通过合理的配置和优化,可以提高数据传输的效率和可靠性,满足实际应用需求。

你可能感兴趣的:(stm32,单片机,嵌入式硬件)