文章目录
简介: 使用485硬件发送与接收,与普通串口收发稍有不同,需要使用一个IO控制硬件的收发功能,高电平时为发送,低电平是为接收模式。
出现问题最多的,就是485方向转换的时机。
前期准备
开发环境: CubIDE ------ HAL库版代码
MCU: STM32F103VCT6
软件: 串口调试助手
其他: 485硬件—75176B芯片 485转UAB线
实现步骤
1、STM32CubeIDE配置部分(CubeMX等同)
先新建项目,选择自己使用的芯片进行下一步
(1)配置时钟
配置时钟的同时会自动配置IO口引脚
配置RTC使能
将HCLK设置为最大频率72MHz
(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);
}
产生的具体原因因篇幅有限在下一篇文章中进行分析。