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


简介: 使用485硬件发送与接收,与普通串口收发稍有不同,需要使用一个IO控制硬件的收发功能,高电平时为发送,低电平是为接收模式。
出现问题最多的,就是485方向转换的时机。

前期准备

开发环境: CubIDE ------ HAL库版代码
MCU: STM32F103VCT6
软件: 串口调试助手
其他: 485硬件—75176B芯片 485转UAB线

实现步骤

1、STM32CubeIDE配置部分(CubeMX等同)

先新建项目,选择自己使用的芯片进行下一步

(1)配置时钟

配置时钟的同时会自动配置IO口引脚
配置时钟
配置RTC使能
使能RTC
将HCLK设置为最大频率72MHz
配置HCLK

(2)配置USART和DMA

配置USART
在这里插入图片描述
DMA Settings ——> Add
选择USART3_RX,接着同样步骤再把USART3_TX,添加。选择Normal发送模式。
在这里插入图片描述

  • DMA Request(DMA请求): USART3_RX、USART3_TX
  • Channel(通道): 通道3、通道2
  • Dirction (DMA传输方向): Peripheral To Memory(外设到内存)、Memory To Peripheral(内存到外设 )
  • Priority(优先级): 低、低
  • Mode(模式): 正常、正常

USART3使能中断
在这里插入图片描述
设置中断优先级
在这里插入图片描述
配置485控制端口
在这里插入图片描述

(3)工程生成

在这里插入图片描述
在这里插入图片描述

2、STM32CubeIDE编写部分(Keil等同)

相应位置插入程序

  • main.h
/* USER CODE BEGIN EM */
#define BUFFER_SIZE 255
/* USER CODE END EM */
/* 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 */
  • usart.h

485相关

/* USER CODE BEGIN Private defines */
#define RS485_DIR_Pin GPIO_PIN_12
#define RS485_DIR_GPIO_Port GPIOB

#define RS485DIR_TX  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);//定义我们的控制引脚为发
#define RS485DIR_RX  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);//定义我们的控制引脚为接收
/* USER CODE END Private defines */

函数声明

void RS485_UART3_DMA_Send(uint8_t *buf,uint8_t len);//DMA发送数据
void RS485_UART_DMA_Receive(void);//DMA重启数据接收
void Bsp_break_usartRx(void);//用于接收中断
void Bsp_break_usart_DMA_Tx(void);//用于发送中断
  • usart.c

引入main.h

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

“void MX_USART1_UART_Init(void)”函数的末尾添加以下代码

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

在“usart.c”文件的最后末尾添加代码

void RS485_UART3_DMA_Send(uint8_t *buf,uint8_t len)
{
	if(TxEndFlag==0)//发送
	{
		RS485DIR_TX
		for(unsigned short i=0;i<10;i++);//280
		if(HAL_UART_Transmit_DMA(&huart3, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
//		if(HAL_UART_Transmit(&huart3, buf,len,0xFFFF)!= HAL_OK)
		{
			Error_Handler();
		}
	}
}
void RS485_UART_DMA_Receive(void)//串口接收封装uint8_t *Data,uint8_t len
{
    RS485DIR_RX // 切换为 RS485 接收模式
	HAL_UART_Receive_DMA(&huart3,RxBuff,BUFFER_SIZE);//重新打开DMA接收
//	HAL_UART_Receive(&huart3,RxBuff,BUFFER_SIZE,0xFFFF);
}

void Bsp_break_usartRx(void)
{
	uint32_t tmp_flag = 0;
	uint32_t temp;
	tmp_flag =__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET)&(TxEndFlag == 0))//idle标志被置位
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart3);//清除标志位

		HAL_UART_DMAStop(&huart3);
		temp  =  __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);// 获取DMA中未传输的数据个数

		RxLen =  BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		RxEndFlag = 1;	// 接受完成标志位置1
	 }
}

void Bsp_break_usart_DMA_Tx(void)
{
	TxEndFlag++;//为1时,初始进入中断,开始发送,为2,发送结束
	if(TxEndFlag >= 2)
	{
		TxEndFlag = 0;
		RS485_UART_DMA_Receive();//开启下一次接收RS485
	}
}
  • stm32f1xx_it.c
    添加头文件 “ #include “usart.h” ”
/* USER CODE BEGIN Includes */
#include "usart.h"
#include <string.h>
/* USER CODE END Includes */

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

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

添加代码 “ Bsp_break_usart_DMA_Tx(); ”

void DMA1_Channel2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel2_IRQn 0 */

  /* USER CODE END DMA1_Channel2_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart3_tx);
  /* USER CODE BEGIN DMA1_Channel2_IRQn 1 */
  Bsp_break_usart_DMA_Tx();
  /* USER CODE END DMA1_Channel2_IRQn 1 */
}

添加代码 “ Bsp_break_usartRx(); ”

void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
  Bsp_break_usartRx();
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}
  • main.c

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

/* USER CODE BEGIN 1 */
  unsigned int time;//记录时钟,用于延时
  unsigned char TxBuffTest[BUFFER_SIZE];
  /* USER CODE END 1 */

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

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 >= 100))
	{
	 time = timen;
	 RS485_UART3_DMA_Send(TxBuff,12);//发送数据
	}

	if(RxEndFlag == 1)  //接收完成标志
	{
		  for(unsigned char i=0;i<RxLen;i++)
		{
			TxBuffTest[i]=RxBuff[i];
		}
		RS485_UART3_DMA_Send(TxBuffTest, RxLen);//发送接收到的数据
		memset(RxBuff,0,RxLen);//清除接收到的数据
		RxLen = 0;//清除计数
		RxEndFlag = 0;//清除接收结束标志位
	}
  }

3、效果展示

间隔为0.5s 正在尝试能否在0.1s内稳定运行。

  • 测试发送数据
    在这里插入图片描述

  • 测试接收数据再转发
    在这里插入图片描述

小结

发送接收数据出现最后两个字节被吞掉情况
将主函数中,添加如下函数,在串口中断中调用485换向。

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart == &huart3)
    {
    	RS485DIR_RX // 切换为 RS485 接收模式
    }
}

将usart.c 下RS485_UART_DMA_Receive(void) 中换向关闭

void RS485_UART_DMA_Receive(void)//串口接收封装uint8_t *Data,uint8_t len
{
//	RS485DIR_RX // 切换为 RS485 接收模式
	HAL_UART_Receive_DMA(&huart3,RxBuff,BUFFER_SIZE);//重新打开DMA接收
//	HAL_UART_Receive(&huart3,RxBuff,BUFFER_SIZE,0xFFFF);
}

产生的具体原因因篇幅有限在下一篇文章中进行分析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

S安东尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值