HAL库中断方式/DMA方式进行串口通信

一、何为串口通信协议

串口通信指两个或两个以上的设备使用串口按位(bit)发送和接收字节。可以在使用一根线发送数据的同时用另一根线接收数据。 串口通信协议就是串口通讯时共同遵循的协议。 协议的内容是每一个bit 所代表的意义。 常用的串口通信协议 有以下几种
1 RS-232(ANSI/EIA-232标准) 只支持 点对点, 最大距离 50英尺。最大速度为128000bit/s, 距离越远 速度越慢。 支持全双工(发送同时也可接收)。
2 RS-422(EIA RS-422-AStandard),支持点对多一条平衡总线上连接最多10个接收器 将传输速率提高到10Mbps,传输距离延长到4000英尺(约1219米),所以在100kbps速率以内,传输距离最大。支持全双工(发送同时也可接收)。
3 RS-485(EIA-485标准)是RS-422的改进, 支持多对多(2线连接),从10个增加到32个,可以用超过4000英尺的线进行串行通行。速率最大10Mbps。支持全双工(发送同时也可接收)。2线连接时 是半双工状态。

二、RS-232

RS-232标准接口(又称EIA RS-232)是常用的串行通信接口标准之一,它是由美国电子工业协会(EIA)联合贝尔系统公司、调制解调厂家及计算机终端生产厂家于1970年共同制定,其全名是“数据终端设备( DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。

三、TTL电平与RS232电平的区别

什么是TTL电平、RS232电平?它们有什么区别呢?

(一)、TTL电平标准

输出 L: <0.8V ; H:>2.4V。
输入 L: <1.2V ; H:>2.0V
TTL器件输出低电平要小于0.8V,高电平要大于2.4V。输入,低于1.2V就认为是0,高于2.0就认为是1。于是TTL电平的输入低电平的噪声容限就只有(0.8-0)/2=0.4V,高电平的噪声容限为(5-2.4)/2=1.3V。

(二)、RS232标准

在TXD和RXD数据线上:
  (1)逻辑1为-3~-15V的电压
  (2)逻辑0为3~15V的电压
在RTS、CTS、DSR、DTR和DCD等控制线上:
  (1)信号有效(ON状态)为3~15V的电压
  (2)信号无效(OFF状态)为-3~-15V的电压
这是由通信协议RS-232规定的。
RS-232:标准串口,最常用的一种串行通讯接口。有三种类型(A,B和C),它们分别采用不同的电压来表示on和off。最被广泛使用的是RS-232C,它将mark(on)比特的电压定义为-3V到-12V之间,而将space(off)的电压定义到+3V到+12V之间。传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。

四、了解“USB/TTL转232”模块(以CH340芯片模块为例)的工作原理。

CH340 是一个USB 总线的转接芯片,实现USB 转串口、USB 转IrDA 红外或者USB 转打印口。为了增加串口通讯的远距离传输及抗干扰能力,RS-232标准使用-15V 表示逻辑 1, +15V 表示逻辑 0。常常会使用 MH340芯片对 USB/TTL与RS-232电平的信号进行转换。

CH340工作原理图

在这里插入图片描述

五、中断实例演示

1、环境配置

在这里插入图片描述

时钟RCC设置,将HSE选为外部晶振模式

在这里插入图片描述

SYS设置,选择Serial Wire模式

在这里插入图片描述

设置USART

在这里插入图片描述

设置NVIC

在这里插入图片描述

时钟树设置,之后设置工程路径、工程名后打开工程

在这里插入图片描述

二、配置代码

1、在usart.c和main.c中添加头文件

#include"stdio.h"
#include"string.h"

2、重定义printf函数和勾选项

在这里插入图片描述

mian函数外定义

int fputc(int ch, FILE *f)
 
{
 
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
 
  return ch;
 
}




3、设置全局变量

uint8_t aRxBuffer;		
uint8_t Uart1_RxBuff[256]; 
uint8_t start[20] = "start";
uint8_t stop[20] = "stop";
uint8_t Uart1_Rx_Cnt = 0;	
uint8_t cAlmStr[] = "溢出了\r\n";
unsigned int flag = 1;



4、设置中断函数

1.在main函数中加入

HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);



2、在while循环中加入

while (1)
{
	if(flag == 1){		
	  		printf("Hello windows!\r\n");
		}
	HAL_Delay(1000);
}



3、重写回调函数(写在main函数外)

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	if (strcmp(Uart1_RxBuff, start) == 0){
			flag = 1;
			printf("start....\r\n");
		
	}
	if (strcmp(Uart1_RxBuff, stop) == 0){
			flag = 0;
			printf("stop....\r\n");
		
	}
	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
		HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);	
	}
	else
	{
		Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
	
		if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			Uart1_Rx_Cnt = 0;
			memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //6清空数组
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断

}

2、烧录运行效果

1698909338360

五、DMA方式

一、DMA

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

二、环境配置

在这里插入图片描述

RCC配置,开启外部高速时钟

在这里插入图片描述

USART配置

在这里插入图片描述

添加两个通信信道

在这里插入图片描述

创建工程名,再生成工程打开

在这里插入图片描述

三、代码配置

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



编译

在这里插入图片描述

烧录效果

1698910139531

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

在这里插入图片描述

在这里插入图片描述

七、总结

在本次实验中我学习了STM32基于中断/DMA方式的串口通信,明白了如何用HAL库采用中断方式和DMA方式编程。在这基础上对中断方式和DMA方式加深了了解,明白了它们实现的串口通信的原理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值