STM32F407VET6开发板标准库实现DMA空闲接收和发送
使用STM32F407VET6开发板的标准库实现DMA空闲接收和收发,
此开发板有两个485接口和一个232接口,实现了三个串口的DMA收发数据。
代码下载地址:
https://gitee.com/stm_3/stm32-f407-vet6-dma.git
232接口:USART1,针脚PA9和PA10
第一路485接口:USART2,复用针脚PD5和PD6,485切换方向针脚PB10
第二路485接口:UART4,复用针脚PC10和PC11,485切换方向针脚PE15
main.c
//main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "rs485_1.h"
#include "rs485_2.h"
#include <string.h>
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2LED_Init();delay_init(168); //初始化延时函数uart_init(115200); //初始化串口波特率为115200USART2_DMA_Init(115200);USART4_DMA_Init(115200);while(1){process_uart();Process_USART2();//Rs485 1Process_USART4();//Rs485 2 LED0=!LED0;LED1=!LED1;//如果需要加快速度,可以注释掉下面这句delay_ms(50);}
}
232接口的实现:文件名usart.c
//usart.c
#include "sys.h"
#include "usart.h"
#include <string.h>
//////////////////////////////////////////////////////////////////////////////////
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h" //ucos 使用
#endif//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle;
}; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch;
}
#endif#define BUFFER_SIZE 8uint8_t rxBuffer[BUFFER_SIZE];
uint8_t txBuffer[BUFFER_SIZE];volatile uint8_t dmaTxComplete = 1;
volatile uint16_t usart1RecvLen = 0;void USART1_DMA_Send() {while(!dmaTxComplete); // 等待上次传输完成 dmaTxComplete = 0;DMA_Cmd(DMA2_Stream7, DISABLE);DMA_SetCurrDataCounter(DMA2_Stream7, usart1RecvLen);DMA_Cmd(DMA2_Stream7, ENABLE);
}void DMA_Config(void) {DMA_InitTypeDef DMA_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);// DMA2 Stream2 Channel4 for USART1_RXDMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)rxBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_InitStruct.DMA_Priority = DMA_Priority_High;DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA2_Stream2, &DMA_InitStruct);DMA_Cmd(DMA2_Stream2, ENABLE);USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);// DMA2 Stream7 Channel4 for USART1_TXDMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)txBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_Init(DMA2_Stream7, &DMA_InitStruct);USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);}/*
void NVIC_Config(void) {// 统一使用快捷函数时显式设置优先级(需配合NVIC_SetPriority)NVIC_SetPriority(DMA2_Stream2_IRQn, 0); // 设置优先级NVIC_EnableIRQ(DMA2_Stream2_IRQn);DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);NVIC_SetPriority(DMA2_Stream7_IRQn, 0);NVIC_EnableIRQ(DMA2_Stream7_IRQn);DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
}*/
void NVIC_Config(void) {NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = DMA2_Stream2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);//enable receive interruptDMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);//enable transfer interruptDMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}void DMA2_Stream2_IRQHandler(void) {if(DMA_GetITStatus(DMA2_Stream2, DMA_IT_TCIF2)) { int i = 0;uint16_t recvLen = 0;// 必须按顺序操作:先停止DMA再读取计数器DMA_Cmd(DMA2_Stream2, DISABLE);recvLen = sizeof(rxBuffer) - DMA_GetCurrDataCounter(DMA2_Stream2);DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);// 数据处理for(i = 0; i < recvLen; i++) {txBuffer[i] = rxBuffer[i];}// 重置DMA配置DMA_SetCurrDataCounter(DMA2_Stream2, sizeof(rxBuffer));DMA_Cmd(DMA2_Stream2, ENABLE);usart1RecvLen = recvLen;}
}//发送
void DMA2_Stream7_IRQHandler(void) {if(DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7)) {DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);//由于不需要485的方向切换,可以直接在这里标志传输完成dmaTxComplete = 1;}
}//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;//NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟//串口1对应引脚复用映射GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1//USART1端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10//USART1 初始化设置USART_InitStructure.USART_BaudRate = bound;//波特率设置USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_Cmd(USART1, ENABLE); //使能串口1 /*USART_ClearFlag(USART1, USART_FLAG_TC);#if EN_USART1_RX USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、#endif*/DMA_Config();NVIC_Config();
}void process_uart(void)
{if(usart1RecvLen>0){USART1_DMA_Send();usart1RecvLen = 0;}
}
第一路485接口:USART2,复用针脚PD5和PD6,485切换方向针脚PB10
文件名称:rs485_1.c
//rs485_1.c
#include <stdio.h>
#include <string.h>
#include "sys.h"
#include "rs485_1.h"
#include "delay.h"//485模式控制.0,接收;1,发送.
#define RS485_1_TX_EN PBout(10)#define USART2_RX_BUF_SIZE 256
#define USART2_TX_BUF_SIZE 256uint8_t usart2RxBuffer[USART2_RX_BUF_SIZE];
uint8_t usart2TxBuffer[USART2_TX_BUF_SIZE];
volatile uint16_t usart2RxLength = 0;/* GPIO初始化 */
static void USART2_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct;//启用时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOD, &GPIO_InitStruct);//复用针脚PD5和PD6GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2);GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_USART2);//RS485 RE DE set,485方向切换针脚PB10,低电平接收,高电平发送RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //GPIOB10GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOB,&GPIO_InitStruct); //初始化PB10//默认接收状态RS485_1_TX_EN = 0;
}/* USART2初始化 */
static void USART2_Init(uint32_t baudrate) {USART_InitTypeDef USART_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);USART_InitStruct.USART_BaudRate = baudrate;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(USART2, &USART_InitStruct);/* ** 配置串口接收空闲中断,这样可以接收变长数据** 但接收的最长数据的长度必须小于接收缓存,否则不会触发空闲中断*/USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//使能传输完成中断,用于判断完成后使能485USART_ITConfig(USART2, USART_IT_TC, ENABLE);USART_ClearFlag(USART2, USART_IT_TC);NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);USART_Cmd(USART2, ENABLE);
}/* DMA接收初始化 DMA1_Stream5 通道4, 普通模式*/
static void USART2_DMA_Rx_Init(void) {DMA_InitTypeDef DMA_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);DMA_DeInit(DMA1_Stream5);while(DMA_GetCmdStatus(DMA1_Stream5) != DISABLE);DMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart2RxBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStruct.DMA_BufferSize = sizeof(usart2RxBuffer);DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_InitStruct.DMA_Priority = DMA_Priority_High;DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream5, &DMA_InitStruct); USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);DMA_Cmd(DMA1_Stream5, ENABLE);
}/* DMA发送初始化 DMA1_Stream6, 通道4,普通模式*/
static void USART2_DMA_Tx_Init(void) {DMA_InitTypeDef DMA_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;DMA_DeInit(DMA1_Stream6);while(DMA_GetCmdStatus(DMA1_Stream6) != DISABLE);DMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart2TxBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;DMA_InitStruct.DMA_BufferSize = 0;DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream6, &DMA_InitStruct);// 配置DMA发送完成中断DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE); NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream6_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
}/* 初始化函数 */
void USART2_DMA_Init(uint32_t baudrate) {USART2_GPIO_Init();USART2_Init(baudrate);USART2_DMA_Rx_Init();USART2_DMA_Tx_Init();
}/* 发送数据 */
void USART2_SendData(uint8_t *data, uint16_t length) {while(DMA_GetCmdStatus(DMA1_Stream6) == ENABLE);if(length > sizeof(usart2TxBuffer)){length = sizeof(usart2TxBuffer);}memcpy(usart2TxBuffer, data, length);//485高电平,准备发送RS485_1_TX_EN = 1;DMA_SetCurrDataCounter(DMA1_Stream6, length);DMA_Cmd(DMA1_Stream6, ENABLE);
}/* USART2中断服务函数 */
void USART2_IRQHandler(void) {if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) {uint16_t recvLen = 0;// 清除空闲中断标志USART_ReceiveData(USART2);DMA_Cmd(DMA1_Stream5, DISABLE);recvLen = sizeof(usart2RxBuffer) - DMA_GetCurrDataCounter(DMA1_Stream5);/* 关键改进点 */if(recvLen > 0) {// 重置DMA前必须清除所有状态标志DMA_ClearFlag(DMA1_Stream5, DMA_FLAG_TCIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TEIF5); // 重新配置DMA(必须包含以下步骤)DMA_SetCurrDataCounter(DMA1_Stream5, sizeof(usart2RxBuffer));}DMA_Cmd(DMA1_Stream5, ENABLE);usart2RxLength = recvLen;}if(USART_GetITStatus(USART2, USART_IT_TC) != RESET){USART_ClearITPendingBit(USART2, USART_IT_TC);//物理发送完成后,485必须处于低电平准备接收状态RS485_1_TX_EN = 0;}
}/* DMA发送完成中断 */
void DMA1_Stream6_IRQHandler(void) {if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET) {DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);//这里发送完成仅仅是DMA计数为0,不代码物理发送完成。}
}void Process_USART2(void)
{if(usart2RxLength > 0){//printf("rs4851:%u\n", usart2RxLength);USART2_SendData(usart2RxBuffer, usart2RxLength);usart2RxLength = 0;}}
头文件名称:rs485_1.h
//rs485_1.h
#ifndef __RS485_1_H
#define __RS485_1_H
#include "sys.h"
#include "stm32f4xx.h"void USART2_DMA_Init(uint32_t baudrate);
void USART2_SendData(uint8_t *data, uint16_t length);
void Process_USART2(void);#endif
第二路485接口:UART4,复用针脚PC10和PC11,485切换方向针脚PE15
文件名称:rs485_2.c
/* rs485_2.c
代码特点:完整实现USART4的DMA收发功能,使用PC10(TX)和PC11(RX)引脚接收采用DMA1_Stream2通道4循环模式,发送采用DMA1_Stream4通道4普通模式通过空闲中断检测数据帧接收完成,避免频繁中断发送完成通过DMA中断通知,提高传输效率提供数据长度获取和拷贝接口,避免直接操作缓冲区模块化设计,头文件提供清晰接口,便于复用
*/#include <string.h>
#include <stdio.h>
#include "sys.h"
#include "rs485_2.h"
#include "delay.h"//485模式控制.0,接收;1,发送.
#define RS485_2_TX_EN PEout(15)#define USART4_RX_BUF_SIZE 256
#define USART4_TX_BUF_SIZE 256uint8_t usart4RxBuffer[USART4_RX_BUF_SIZE];
uint8_t usart4TxBuffer[USART4_TX_BUF_SIZE];
volatile uint16_t usart4RxLength = 0;static void USART4_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_UART4);GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_UART4);//RE DE setGPIO_InitStruct.GPIO_Pin = GPIO_Pin_15; //GPIOE15GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOE,&GPIO_InitStruct); //初始化PE15//default, enable receive stateRS485_2_TX_EN = 0;
}static void USART4_Init(uint32_t baudrate) {USART_InitTypeDef USART_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);USART_InitStruct.USART_BaudRate = baudrate;USART_InitStruct.USART_WordLength = USART_WordLength_8b;USART_InitStruct.USART_StopBits = USART_StopBits_1;USART_InitStruct.USART_Parity = USART_Parity_No;USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(UART4, &USART_InitStruct);//使能空闲中断,接收的数据必须小于缓存才会触发USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);//使能传输完成中断,用于判断完成后使能485USART_ITConfig(UART4, USART_IT_TC, ENABLE);USART_ClearFlag(UART4, USART_IT_TC);NVIC_InitStruct.NVIC_IRQChannel = UART4_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);USART_Cmd(UART4, ENABLE);
}static void USART4_DMA_Rx_Init(void) {DMA_InitTypeDef DMA_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);DMA_DeInit(DMA1_Stream2);while(DMA_GetCmdStatus(DMA1_Stream2) != DISABLE);DMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart4RxBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;DMA_InitStruct.DMA_BufferSize = USART4_RX_BUF_SIZE;DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_InitStruct.DMA_Priority = DMA_Priority_High;DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream2, &DMA_InitStruct);DMA_ITConfig(DMA1_Stream2, DMA_IT_TC, ENABLE); //same as NVIC_EnableIRQ(DMA1_Stream2_IRQn); default priorityNVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);USART_DMACmd(UART4, USART_DMAReq_Rx, ENABLE);DMA_Cmd(DMA1_Stream2, ENABLE);
}static void USART4_DMA_Tx_Init(void) {DMA_InitTypeDef DMA_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;DMA_DeInit(DMA1_Stream4);while(DMA_GetCmdStatus(DMA1_Stream4) != DISABLE);DMA_InitStruct.DMA_Channel = DMA_Channel_4;DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&UART4->DR;DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)usart4TxBuffer;DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;DMA_InitStruct.DMA_BufferSize = 0;DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA1_Stream4, &DMA_InitStruct);DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);//same as NVIC_EnableIRQ(DMA1_Stream4_IRQn);NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream4_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);USART_DMACmd(UART4, USART_DMAReq_Tx, ENABLE);}void USART4_DMA_Init(uint32_t baudrate) {USART4_GPIO_Init();USART4_Init(baudrate);USART4_DMA_Rx_Init();USART4_DMA_Tx_Init();
}uint16_t USART4_SendData(uint8_t *data, uint16_t length) {while(DMA_GetCmdStatus(DMA1_Stream4) == ENABLE);if(length > USART4_TX_BUF_SIZE) length = USART4_TX_BUF_SIZE;memcpy(usart4TxBuffer, data, length);RS485_2_TX_EN = 1;DMA_SetCurrDataCounter(DMA1_Stream4, length);DMA_Cmd(DMA1_Stream4, ENABLE);return length;
}void UART4_IRQHandler(void) {if(USART_GetITStatus(UART4, USART_IT_IDLE) != RESET) {uint16_t recvLen = 0;// 清除空闲中断标志USART_ReceiveData(UART4);;DMA_Cmd(DMA1_Stream2, DISABLE);recvLen = sizeof(usart4RxBuffer) - DMA_GetCurrDataCounter(DMA1_Stream2);/* 关键改进点 */if(recvLen > 0) {// 重置DMA前必须清除所有状态标志DMA_ClearFlag(DMA1_Stream2, DMA_FLAG_TCIF2 | DMA_FLAG_HTIF2 | DMA_FLAG_TEIF2); // 重新配置DMA(必须包含以下步骤)DMA_SetCurrDataCounter(DMA1_Stream2, sizeof(usart4RxBuffer));}DMA_Cmd(DMA1_Stream2, ENABLE);usart4RxLength = recvLen;}if(USART_GetITStatus(UART4, USART_IT_TC) != RESET){USART_ClearITPendingBit(UART4, USART_IT_TC);//物理发送完成后,485必须处于低电平准备接收状态RS485_2_TX_EN = 0;}/* 扩展错误处理(可选)*/if(DMA_GetITStatus(DMA1_Stream2, DMA_IT_TEIF2)) {DMA_ClearITPendingBit(DMA1_Stream2, DMA_IT_TEIF2);//Error_Handler();}
}// transfer data
void DMA1_Stream4_IRQHandler(void) {if(DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4) != RESET) {DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);}
}// receive data
void DMA1_Stream2_IRQHandler(void)
{ if(DMA_GetITStatus(DMA1_Stream2, DMA_IT_TCIF2) != RESET) {DMA_ClearITPendingBit(DMA1_Stream2, DMA_IT_TCIF2);}if(DMA_GetITStatus(DMA1_Stream2, DMA_FLAG_HTIF2) != RESET) {DMA_ClearITPendingBit(DMA1_Stream2, DMA_FLAG_HTIF2);}if(DMA_GetITStatus(DMA1_Stream2, DMA_FLAG_TEIF2) != RESET) {DMA_ClearITPendingBit(DMA1_Stream2, DMA_FLAG_TEIF2);}if(DMA_GetITStatus(DMA1_Stream2, DMA_FLAG_DMEIF2) != RESET) {DMA_ClearITPendingBit(DMA1_Stream2, DMA_FLAG_DMEIF2); }
}void Process_USART4(void)
{if(usart4RxLength > 0){//printf("rs4852:%u\n", usart4RxLength);USART4_SendData(usart4RxBuffer, usart4RxLength);usart4RxLength = 0;}}
头文件 rs485_2.h
// rs485_2.h
#ifndef __RS485_2_H
#define __RS485_2_H
#include "sys.h"
#include "stm32f4xx.h"void USART4_DMA_Init(uint32_t baudrate);
uint16_t USART4_SendData(uint8_t *data, uint16_t length);
void Process_USART4(void);#endif