STM32F103下的DMA串口通讯-以485硬件收发

本程序开始之前已经开通了DMA的ADC通道

1、建立工程后,依次点击如图1、2、3、4、5

 2、波特率选择9600,也可以选别的,能满足需求的波特率尽可能小,越小越稳定

 3.1、开启DMA,添加Tx发送信息

3.2、 同样的方法,添加Rx接收信息

 4、选择DMA优先级,如图,优先级数字越小,优先级程度越高

 

 5.1、由于使用485硬件发送与接收,因此使用一个IO控制硬件的收发功能,高电平时为发送,低电平是为接收模式,所有只有发送的时候是高电平,其余皆为低电平,我选择的IO是PTA11

5.1.2、依次单机1、2

 5.1.2、依次单机如图所示,由于硬件隔离电路问题选择了开漏模式,如果没有隔离可以选择推挽模式加上拉电阻,速度选择高速模式

5.2、完成后代码生成,并打开代码

 6、在“main.h”中添加代码,位置如图

6.1、找到宏定义

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

 添加代码  宏定义 接收到的数据长度BUFFER_SIZE 小于 100

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
#define BUFFER_SIZE 100
/* USER CODE END EM */

6.2、找到代码

/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

添加全局变量,extern是全局变量定义,就是定义的数组整个工程都可以用

/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
extern unsigned int timen;//系统运行绝对时钟,从系统运行开始计时,每1ms自加一次
extern unsigned short ADC[10];
extern unsigned char RxBuff[BUFFER_SIZE];
extern unsigned char TxBuff[BUFFER_SIZE];
extern unsigned char TxEndFlag;//完成发送标志位
extern unsigned char RxEndFlag;//完成接收标志位
extern unsigned char RxLen;//接收数据长度
/* USER CODE END Private defines */

7、在“main.c”函数中添加代码如下,定义RxBuff是接收到暂存数组,每次接收到数据后都会把数据存放在这个数组中,因此有很强的实时性,TxBuff是发送暂存数组,每次发送都是这个数组的数据

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
unsigned int timen;//系统运行绝对时钟,从系统运行开始计时,每1ms自加一次
unsigned short ADC[10];
unsigned char RxBuff[BUFFER_SIZE];
unsigned char TxBuff[BUFFER_SIZE];
unsigned char TxEndFlag;//完成发送标志位
unsigned char RxEndFlag;//完成接收标志位
unsigned char RxLen;//接收数据长度
/* USER CODE END PV */

8、在“usart.c”文件下添加代码

8.1、找到一下代码

/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

添加 “#include "main.h" ” 如下,为了把定义的变量添加进去

/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */
#include "main.h"
/* USER CODE END 0 */

8.2.1、在“void MX_USART1_UART_Init(void)”函数的开头找到以下代码

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

添加 代码 “unsigned char RxBuff[BUFFER_SIZE]; ”

  /* USER CODE BEGIN USART1_Init 0 */
  unsigned char RxBuff[BUFFER_SIZE];
  /* USER CODE END USART1_Init 0 */

8.2.2、在“void MX_USART1_UART_Init(void)”函数的末尾找到以下代码

  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

添加 代码 “__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); 

 HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);”

  /* USER CODE BEGIN USART1_Init 2 */
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
  
  //DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
	HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);
  /* USER CODE END USART1_Init 2 */

8.3、在“usart.c”文件的最后末尾添加代码,如图

/* USER CODE BEGIN 1 */
void PMJD_UART1_DMA_Send(uint8_t *buf,uint8_t len)
{
	if(TxEndFlag==0)//发送
	{
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
		for(unsigned short i=0;i<280;i++);//280
		if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
		{
			Error_Handler();
		}
	}

}



/*
*********************************************************************************************************
* 函 数 名: PMJD_UART1_DMA_Receive
* 功能说明: 串口接收功能函数
* 形  参: Data,len
* 返 回 值: 无
*********************************************************************************************************
*/
void PMJD_UART1_DMA_Receive(void)//串口接收封装uint8_t *Data,uint8_t len
{

	
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);//BUFFER_SIZE
	
	HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);//重新打开DMA接收
}

void Bsp_break_usartRx(void)
{
	uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET)&(TxEndFlag == 0))//idle标志被置位
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		
		HAL_UART_DMAStop(&huart1); //
		temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   
		
		RxLen =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		RxEndFlag = 1;	// 接受完成标志位置1	
	 }
}


void Bsp_break_usart_DMA_Tx(void)
{
	TxEndFlag++;//为1时,初始进入中断,开始发送,为2,发送结束
	if(TxEndFlag >= 2)
	{
		TxEndFlag = 0;
		PMJD_UART1_DMA_Receive();//接收RS485
	}
}

/* USER CODE END 1 */

9、打开“usart.h”文件,在文件末尾找到,如下

void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

添加代码

void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */
void PMJD_UART1_DMA_Send(uint8_t *buf,uint8_t len);//DMA发送数据
void PMJD_UART1_DMA_Receive(void);//DMA重启数据接收
void Bsp_break_usartRx(void);//用于接收中断
void Bsp_break_usart_DMA_Tx(void);//用于发送中断
/* USER CODE END Prototypes */

10、打开“stm32f1xx_it.c”文件

10.1、添加头文件 “ #include "usart.h" ”如下

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
/* USER CODE END Includes */

10.2、添加代码“  timen++;  ”,“SysTick_Handler”函数是系统时钟,每1ms被调用一次

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
  timen++;
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

10.3、添加代码 “  Bsp_break_usart_DMA_Tx();  ”,如图

void DMA1_Channel4_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel4_IRQn 0 */

  /* USER CODE END DMA1_Channel4_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_tx);
  /* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
  Bsp_break_usart_DMA_Tx();
  /* USER CODE END DMA1_Channel4_IRQn 1 */
}

10.4、添加代码 “  Bsp_break_usartRx();  ”,如图

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  Bsp_break_usartRx();
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

11、回到主函数“main.c”中,测试下,可不可以运行

        在int main(void)函数中,添加代码 “  unsigned int time;  ”如图

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  unsigned int time;//记录时钟,用于延时等
  unsigned char TxBuffTest[BUFFER_SIZE];//用于测试发送数据时的储存器
  /* USER CODE END 1 */

在"  while(1)  "下添加代码如下,每1s发送一次数据

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	for(unsigned char i=0;i<10;i++)
		TxBuff[i]=0x00+i;//数据赋值
	TxBuff[10]=0x0D;//这个和下面的共同表示发送结束
	TxBuff[11]=0x0A;
	if((TxEndFlag == 0)&(timen - time >= 1000))
	{
		time = timen;
		PMJD_UART1_DMA_Send(TxBuff,12);//发送数据
	}
  }
  /* USER CODE END 3 */

12、编译,烧入程序,测试接收数据,接收正常,如图,16进制,波特率 9600

 13、测试下,接收数据再转发

在 “  while(1)  ”的函数中添加代码,如下图

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	for(unsigned char i=0;i<10;i++)
		TxBuff[i]=0x00+i;//数据赋值
	TxBuff[10]=0x0D;//这个和下面的共同表示发送结束
	TxBuff[11]=0x0A;
	if((TxEndFlag == 0)&(timen - time >= 1000))
	{
		time = timen;
		PMJD_UART1_DMA_Send(TxBuff,12);//发送数据
	}
	if(RxEndFlag == 1)  //接收完成标志
	{
        for(unsigned char i=0;i<RxLen;i++)
		{
			TxBuffTest[i]=RxBuff[i];
		}
		PMJD_UART1_DMA_Send(TxBuff, RxLen);//发送接收到的数据
		memset(RxBuff,0,RxLen);//清除接收到的数据
		RxLen = 0;//清除计数
		RxEndFlag = 0;//清除接收结束标志位
	}
  }
  /* USER CODE END 3 */

由于使用了“memset”函数,需要调用标准函数库

在main.h文件中添加标准库的头文件 “  #include "string.h"  ”

/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */

14、编译、烧入程序,测试收发性能,正常

 注:用DMA发送数据,每个消息的数据地址不要用一样的,因为DMA发送的时候,CPU也在工作,DMA数据地址的数据要避免被CPU修改

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: stm32f103作为一款常见的嵌入式控制器,具备丰富的外设使其在工业自动化、通讯和控制等领域有着广泛的应用。其中,rs485串口通讯作为一种支持多节点通讯的标准,也是工业现场通讯应用中的常见方式。而如何通过stm32f103实现rs485通讯,则需要借助于其dma功能来完成。 stm32f103内置的dma控制器,能够支持硬件直接存储器访问。由于rs485串口通讯需要在传输过程中不断地交替进行发送和接收操作,而dma控制器能够通过设置传输方向和源地址等参数,在不同的传输状态下自动完成数据的传输。因此,在实现stm32f103与rs485串口通讯时,可以使用dma控制器来提高传输效率和稳定性。 需要注意的是,在实现stm32f103与rs485通讯时,除了使用dma控制器完成数据传输,还需要考虑缓存数据的存储、校验和处理、适当的数据传输速率等问题。因此,需要在程序设计过程中进行合理的策略选择和参数配置,以提高通讯效率和可靠性。 总之,通过充分发挥stm32f103的dma功能,我们可以实现高效、稳定的rs485串口通讯,提高工业现场的自动化控制和通讯效率。 ### 回答2: STM32F103是一款ARM Cortex-M3内核的单片机,支持RS485通讯协议。为了提高通讯效率和稳定性,可以使用DMA进行数据收发DMA是直接内存访问技术,可以将传输数据的过程交给DMA控制器,从而减轻CPU的负担,提高系统的性能。在STM32F103中,可以使用USART1、USART2和USART3作为RS485的通讯接口,DMA控制器可以与这些接口进行配合,实现数据的高效传输。 在使用DMA进行RS485通讯时,需要配置相关的寄存器,包括DMA控制器的寄存器、USART接口的寄存器等。为了保证数据的安全性和稳定性,需要对数据流控制进行合理的配置,并加入错误校验机制。 总之,在STM32F103上使用RS485通讯协议,可以通过DMA技术实现快速、稳定和可靠的数据收发。需要注意选择合适的USART接口,并对DMA控制器和相关寄存器进行正确的配置和编程。 ### 回答3: STM32F103是一款由STMicroelectronics公司推出的32位微控制器,它集成了丰富的外设和功能模块,可广泛应用于各种控制、通信和数据处理领域。其中,RS485总线接口是STM32F103中常用的通信接口之一,它可以实现高速、长距离的数据传输,并且具有抗干扰能力强、抗噪声能力强等优点。 在使用STM32F103进行RS485通信时,可以利用DMA(Direct Memory Access)引擎提高数据传输效率和代码运行效率。DMA是一种硬件机制,可以直接访问系统内存和外设寄存器,无需CPU的干预,从而提高数据传输速率和系统响应速度。通过使用DMA,可以实现RS485的收发缓冲区自动映射,减少CPU的负担和数据传输的延迟,提高系统的稳定性和可靠性。 具体实现上,可以使用STM32F103的内置DMA控制器,将RS485的数据收发缓冲区映射为内存块,通过设置DMA传输方向、数据长度、传输模式和中断处理方式等参数,实现RS485数据的DMA收发。在RS485数据收发过程中,可以通过设置DMA传输完成中断和RS485数据传输完成中断来进行数据处理和错误处理等操作,从而实现更加稳定、高效和可靠的RS485通信。 总之,STM32F103在RS485通信中的DMA收发应用,可以提高数据传输效率和代码运行效率,降低CPU负担及数据传输延迟,从而提高系统稳定性和可靠性,同时也为实现更加高效、可靠的通信模式提供了重要技术支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦之程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值