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 clock
gpio_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 configure
usart_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); //使能USART1
Usart1Send_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接收",否则第一个数据为0
delay_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通道5
usart_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个字符为0
USART1_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();
}
}