GD32F103VE串口与DMA传输,本测试采用的的串口1和DMA0之间的数据传输,然后通过RS485和其它设备进行数据交换,没有采用任何中断参与。
GD32F103VE的DMA0请求映射到串口:
1,USART0_RX映射到DMA0的通道4,USART0_TX映射到DMA0的通道3;
2,USART1_RX映射到DMA0的通道5,USART1_TX映射到DMA0的通道6;
3,USART2_RX映射到DMA0的通道2,USART2_TX映射到DMA0的通道1;
有时需要插图讲解,可能会更好。无图的中文,过了一段时间,自己都不清楚自己在讲啥。见下图:
程序比一堆文字叙述更为重要。说实话,也参考了很多的人测试程序,没有达到我想要的结果。
#include "USART1_DMA0.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "delay.h"/*
DMA0请求映射到串口:
USART0_RX映射到DMA0的通道4,USART0_TX映射到DMA0的通道3;
USART1_RX映射到DMA0的通道5,USART1_TX映射到DMA0的通道6;
USART2_RX映射到DMA0的通道2,USART2_TX映射到DMA0的通道1;
*/void GD32F103_USART1_DMA0_Init(unsigned int bound);
void RS485_USART1_DMA_Send(void);
void RS485_USART1_DMA_Receive(void);void Usart1Send_DMA0_DMA_CH6_config(void);
void Usart1Receive_DMA0_DMA_CH5_config(void);void usart_dma_config(void);#define ARRAYNUM(arr_name) (uint32_t)(sizeof(arr_name)/sizeof(*(arr_name))) //计算数组arr_name[]的长度uint8_t USART1_TX_Buffer[USART1_TX_Buffer_Size] ={ 'A', 'B', 'C', 'D', '\r', '\n' };
#define USART1_RX_Buffer_Size 100
uint8_t USART1_RX_Buffer[USART1_RX_Buffer_Size];
uint8_t USART1_RX_Buffer_StartIndex;//USART1_RX_Buffer[]的装载索引值
uint8_t USART1_RX_Buffer_EndIndex;//USART1_RX_Buffer[]的装载索引值void RS485_Enable_Output_Init(void)
{rcu_periph_clock_enable(RCU_GPIOA); //使能GPIOA时钟,enable GPIO clock gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);//将GPIOA1设置为输出上拉RS485_ENABLE_PIN_Output_High();
}//函数功能:初始化USART1
void GD32F103_USART1_DMA0_Init(unsigned int bound)
{RS485_Enable_Output_Init();rcu_periph_clock_enable(RCU_GPIOA); //使能GPIOA时钟,enable GPIO clock rcu_periph_clock_enable(RCU_USART1); //使能USART时钟,enable USART clockgpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);//将GPIOA2设置为AFIO口(复用IO口),输出上拉gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);//将GPIOA3设置为浮空输入口usart_deinit(USART1); //复位USART1,USART configureusart_baudrate_set(USART1,bound); //设置USART1的波特率usart_word_length_set(USART1,USART_WL_8BIT); //设置USART1数据传输格式为8位usart_stop_bit_set(USART1,USART_STB_1BIT); //设置USART1停止位为1位usart_parity_config(USART1,USART_PM_NONE); //设置USART1无需奇偶校验usart_hardware_flow_rts_config(USART1,USART_RTS_DISABLE); //设置不使能USART1的RTS引脚功能usart_hardware_flow_cts_config(USART1,USART_CTS_DISABLE); //设置不使能USART1的CTS引脚功能usart_receive_config(USART1, USART_RECEIVE_ENABLE); //使能USART1接收usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); //使能USART1发送usart_enable(USART1); //使能USART1Usart1Send_DMA0_DMA_CH6_config();Usart1Receive_DMA0_DMA_CH5_config();
}/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{usart_data_transmit(USART1, (uint8_t) ch);while( RESET == usart_flag_get(USART1, USART_FLAG_TBE) ){//等待串口0发送结束}return ch;
}void Usart1Receive_DMA0_DMA_CH5_config(void)
{dma_parameter_struct dma_init_struct;RS485_ENABLE_PIN_Output_Low();//必须先允许RS485接收,然后再配置"串口到DMA接收",否则第一个数据为0delay_ms(5);rcu_periph_clock_enable(RCU_DMA0);//使能DMA0时钟,enable DMA0///配置串口DMA接收开始
初始化"DMA0通道5寄存器"开始///dma_deinit(DMA0, DMA_CH5);dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;//DMA传送方向:从外设到内存dma_init_struct.periph_addr = USART1_DATA_REGISTER_ADDRESS; //源数据块首地址为:串口接收数据寄存器dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//源数据块地址不递增dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;//源数据块的数据宽度为8位dma_init_struct.memory_addr = (uint32_t)USART1_RX_Buffer;//目的数据首地址为:USART1_RX_Buffer[]dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //目的数据块地址递增dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT; //目的数据块的数据宽度为8位dma_init_struct.number = USART1_RX_Buffer_Size; //需要接收的数据长度dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //超高的优先级dma_init(DMA0, DMA_CH5, &dma_init_struct);//使用dma_init_struct数据结构初始化DMA0通道5
初始化"DMA0通道5寄存器"结束//配置"DMA0通道5工作模式"开始///dma_circulation_disable(DMA0, DMA_CH5); //不使能"DMA0通道5循环工作模式"dma_memory_to_memory_disable(DMA0, DMA_CH5); //不使能"DMA0通道5内存到内存传输模式"
配置"DMA0通道5工作模式"结束///
///配置串口DMA接收开结束dma_channel_enable(DMA0, DMA_CH5);//使能指定的DMA0通道5usart_dma_receive_config(USART1,USART_DENR_ENABLE);//使能DMA串口接收USART1_RX_Buffer_StartIndex=USART1_RX_Buffer_Size-dma_transfer_number_get(DMA0,DMA_CH5);USART1_RX_Buffer_EndIndex=USART1_RX_Buffer_StartIndex;
}void Usart1Send_DMA0_DMA_CH6_config(void)
{dma_parameter_struct dma_init_struct;rcu_periph_clock_enable(RCU_DMA0);//使能DMA0时钟,enable DMA0///配置串口DMA发送开始
初始化"DMA0通道6寄存器"开始///dma_deinit(DMA0,DMA_CH6);//根据DMA0通道6,将DMA0通道6的相关寄存器初始化为初始值dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL; //DMA传送方向:从内存到外设dma_init_struct.memory_addr = (uint32_t)USART1_TX_Buffer;//源数据首地址为:USART1_TX_Buffer[]dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //源数据块地址递增dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT; //源数据块的数据宽度为8位dma_init_struct.number = USART1_TX_Buffer_Size; //源数据块的数据长度dma_init_struct.periph_addr = USART1_DATA_REGISTER_ADDRESS; //目的数据首地址为:串口发送数据寄存器dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//目的数据块地址不递增dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;//目的据块的数据宽度为8位dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //超高的优先级dma_init(DMA0, DMA_CH6, &dma_init_struct);//使用dma_init_struct数据结构初始化DMA0通道6
初始化"DMA0通道6寄存器"结束///配置"DMA0通道6工作模式"开始///dma_circulation_disable(DMA0, DMA_CH6); //不使能"DMA0通道6循环工作模式"dma_memory_to_memory_disable(DMA0, DMA_CH6); //不使能"DMA0通道6内存到内存传输模式"
配置"DMA0通道6工作模式"结束///
///配置串口DMA发送结束配置"DMA0通道5工作模式"开始///dma_circulation_disable(DMA0, DMA_CH5); //不使能"DMA0通道5循环工作模式"dma_memory_to_memory_disable(DMA0, DMA_CH5); //不使能"DMA0通道5内存到内存传输模式"
配置"DMA0通道5工作模式"结束///
///配置串口DMA接收开结束dma_channel_enable(DMA0, DMA_CH5);//使能指定的DMA0通道5
// usart_dma_transmit_config(USART1, USART_DENT_ENABLE);//使能DMA串口发送
}void RS485_USART1_DMA_Send(void)
{RS485_ENABLE_PIN_Output_High();//允许发送delay_ms(5);//等待usart_dma_transmit_config(USART1,USART_DENT_ENABLE);//使能DMA串口发送
// if(SET == dma_flag_get(DMA0,DMA_CH6,DMA_FLAG_FTF))//如果
// {//USART1 TX DMA0 channel3 transfer complete
// dma_flag_clear(DMA0,DMA_CH6,DMA_FLAG_FTF);"DMA0通道6"初始化开始/dma_deinit(DMA0,DMA_CH6);//将DMA0通道6的相关寄存器初始化为初始值dma_transfer_direction_config(DMA0,DMA_CH6,DMA_MEMORY_TO_PERIPHERAL);//DMA传送方向:从内存到外设dma_memory_address_config(DMA0,DMA_CH6,(uint32_t)USART1_TX_Buffer); //源数据首地址为:USART1_TX_Buffer[]dma_memory_increase_enable(DMA0,DMA_CH6); //源数据块地址递增dma_memory_width_config(DMA0,DMA_CH6,DMA_MEMORY_WIDTH_8BIT); //源数据块的数据宽度为8位dma_transfer_number_config(DMA0,DMA_CH6,strlen((char*)USART1_TX_Buffer));//源数据块的数据长度dma_periph_address_config(DMA0,DMA_CH6,USART1_DATA_REGISTER_ADDRESS);//目的数据首地址为:串口发送数据寄存器dma_periph_increase_disable(DMA0,DMA_CH6); //目的数据块地址不递增dma_periph_width_config(DMA0,DMA_CH6,DMA_PERIPHERAL_WIDTH_8BIT); //目的据块的数据宽度为8位dma_priority_config(DMA0,DMA_CH6,DMA_PRIORITY_ULTRA_HIGH); //超高的优先级
"DMA0通道6"初始化结束/dma_channel_enable(DMA0, DMA_CH6);//使能指定的DMA0通道6
// usart_dma_transmit_config(USART1,USART_DENT_ENABLE);//使能DMA串口发送
// }while(RESET == dma_flag_get(DMA0,DMA_CH6,DMA_FLAG_FTF))//等待发送完成{}dma_flag_clear(DMA0,DMA_CH6,DMA_FLAG_FTF);//清除DMA0的通道6标志delay_ms(5);//DMA传完,不代表串口发送完成,所以这里要等待RS485_ENABLE_PIN_Output_Low();//允许RS485接收delay_ms(5);
}void RS485_USART1_DMA_Receive(void)
{uint8_t i;uint8_t len_New;uint8_t len_Old;len_New=dma_transfer_number_get(DMA0,DMA_CH5);//读DMA0通道5剩余空间len_Old=len_New;USART1_RX_Buffer_EndIndex=USART1_RX_Buffer_Size-len_Old;if(USART1_RX_Buffer_StartIndex!=USART1_RX_Buffer_EndIndex)//发现新数据{for(i=0;i<10;i++){delay_ms(2);len_New=dma_transfer_number_get(DMA0,DMA_CH5);//读DMA0通道5剩余空间if(len_New!=len_Old)//接收没有完成{len_Old=len_New;i=0;}else i=11;}USART1_RX_Buffer_EndIndex=USART1_RX_Buffer_Size-len_Old;i=0;for(;USART1_RX_Buffer_StartIndex!=USART1_RX_Buffer_EndIndex;){//将接收到的数据保存到USART1_TX_Buffer[]中;
// if(USART1_RX_Buffer[USART1_RX_Buffer_StartIndex])//若发现字符为0x00,则抛弃
// {//RS485从发送进入接收,第1个字符为0USART1_TX_Buffer[i]=USART1_RX_Buffer[USART1_RX_Buffer_StartIndex];i++;
// }
// else
// {
// USART1_TX_Buffer[i]='F';i++;
// USART1_TX_Buffer[i]='F';i++;
// }USART1_RX_Buffer_StartIndex++;}USART1_TX_Buffer[i]='\r';i++;USART1_TX_Buffer[i]='\n';i++;USART1_TX_Buffer[i]='\0';RS485_USART1_DMA_Send();//将接收到的数据回传Usart1Receive_DMA0_DMA_CH5_config();//重新初始化串口到DMA接收}
}
#ifndef __USART1_DMA0_H
#define __USART1_DMA0_H#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool#define USART0_DATA_REGISTER_ADDRESS ((USART0) + (0x00000004U))
//((uint32_t)0x40013804),串口0发送/接收数据寄存器地址
//USART0的基地址为0x40013800,APB2时钟
//串口发送/接收数据寄存器偏移地址为0x0004#define USART1_DATA_REGISTER_ADDRESS ((USART1) + (0x00000004U)) //串口0发送/接收数据寄存器地址#define RS485_Enable_Output PAout(1)
#define RS485_ENABLE_PIN_Output_High() GPIO_BOP(GPIOA)=GPIO_PIN_1 //定义RS485串口使能脚输出高电平
#define RS485_ENABLE_PIN_Output_Low() GPIO_BC(GPIOA)=GPIO_PIN_1 //定义RS485串口使能脚输出低电平
#define RS485_ENABLE_PIN_Toggle() gpio_bit_write( GPIOA,GPIO_PIN_1,(bit_status)((1-gpio_input_bit_get(GPIOA,GPIO_PIN_1))) )
//GA1取反输出电平#define USART1_TX_Buffer_Size 50
extern uint8_t USART1_TX_Buffer[USART1_TX_Buffer_Size];extern void GD32F103_USART1_DMA0_Init(unsigned int bound);
extern void RS485_USART1_DMA_Send(void);
extern void RS485_USART1_DMA_Receive(void);#endif
main.c程序如下:
#include "gd32f10x.h" //使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t,bool
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()#include "delay.h"
#include "USART1_DMA0.h"const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
const char CPU_Is_Run_REG[]="\r\nCPU is run!\r\n";
int main(void)
{//NVIC_PRIGROUP_PRE4_SUB0:抢占优先级为4bit(取值为0~15),子优先级为0bit(没有响应优先级)//NVIC_PRIGROUP_PRE3_SUB1:抢占优先级为3bit(取值为0~7),子优先级为1bit(取值为0~1)//NVIC_PRIGROUP_PRE2_SUB2:抢占优先级为2bit(取值为0~3),子优先级为2bit(取值为0~3)//NVIC_PRIGROUP_PRE1_SUB3:抢占优先级为1bit(取值为0~1),子优先级为3bit(取值为0~7)//NVIC_PRIGROUP_PRE0_SUB4:抢占优先级为0bit(没有抢占优先级),子优先级为3bit(取值为0~15)nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);//设置系统中断优先级"抢占优先级为4bit,子优先级为0bit"INTX_ENABLE();//开启所有中断delay_init();//初始化延迟函数GD32F103_USART1_DMA0_Init(115200);delay_ms(500);strcpy((char*)USART1_TX_Buffer,CPU_Reset_REG);RS485_USART1_DMA_Send();strcpy((char*)USART1_TX_Buffer,CPU_Is_Run_REG);RS485_USART1_DMA_Send();while(1){RS485_USART1_DMA_Receive();}
}