GD32F470之串口空闲中断+DMA篇

您所在的位置:网站首页 gd32F450串口接收脚如何配置 GD32F470之串口空闲中断+DMA篇

GD32F470之串口空闲中断+DMA篇

2024-07-14 13:15| 来源: 网络整理| 查看: 265

先申请,本栏目用的都是GD32F470芯片240M,软件用的是keil,编写用的是C++(其实和C没有区别).

C++格式下怎么使用printf

因为keil下使用C++,要把Micro LIB去掉 ,所以printf要重新重定向 在这里插入图片描述 看下面的代码,是很久之前 抄某一个大神的,

#pragma import(__use_no_semihosting) namespace std{ struct __FILE { int handle; }; FILE __stdout; FILE __stdin; FILE __stderr; int fputc(int ch, std::FILE *f) { while(RESET == usart_flag_get(USART2, USART_FLAG_TBE)); usart_data_transmit(USART2, (uint8_t)ch); return ch; } int ferror(FILE *stream) { /* Your implementation of ferror(). */ return 0; } int fflush(FILE *f) { /* Your implementation of fflush(). */ return 0; } extern "C" void _sys_exit(int) { /* declared in */ // abort(); while(1); } extern "C" void _ttywrch(int ch) { while(RESET == usart_flag_get(USART2, USART_FLAG_TBE)); usart_data_transmit(USART2, (uint8_t)ch); //return ch; return ; } }

然后下面是usart的内容 直接上代码

new一个对象

Usart_class *Usart1_Obj = new Usart_class(USART2,115200); Global_Usart1_Obj=Usart1_Obj; IO配置

:我用的是串口2,然后配置成推挽,备用功能(复用功能),上拉。 具体的IO口配置看我的GPIO篇

void Usart_class::usart_io_init(void) { if(__usart_periph == USART2) { rcu_periph_clock_enable(RCU_GPIOD); // connect port to USARTx_Tx,USARTx_Rx gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_8|GPIO_PIN_9); // configure USART Tx as alternate function push-pull */ gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_8|GPIO_PIN_9); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9); } } Usart配置:

在这里,开启空闲中断和使能时钟,配置dma和使能发送接收,最后开启串口

要注意的是,usart_deinit函数要放在usart_interrupt_enable函数前面,不然不工作。

void Usart_class::usart_init() { if(__usart_periph == USART2) { rcu_periph_clock_enable(RCU_USART2); nvic_irq_enable(USART2_IRQn, 1, 1); } else return; usart_io_init(); usart_deinit(__usart_periph); //取消初始化要放在中断usart_interrupt_enable前面 usart_interrupt_enable(__usart_periph, USART_INT_IDLE);//空闲中断 usart_baudrate_set(__usart_periph,__baudval); usart_receive_config(__usart_periph, USART_RECEIVE_ENABLE); usart_transmit_config(__usart_periph, USART_TRANSMIT_ENABLE); usart_dma_recv_config(); usart_enable(__usart_periph); }

DMA配置:

发送DMA配置:

注意DMA的通道即可,发送是通道3,最前面的通道对应函数 dma_channel_subperipheral_select(DMA0, DMA_CH3, DMA_SUBPERI4); 的最后一个参数DMA_SUBPERI4。 在这里插入图片描述

void Usart_class::usart_dma_Send_config(void) { if(__usart_periph == USART2) { dma_single_data_parameter_struct dma_init_struct_send; // enable DMA0 rcu_periph_clock_enable(RCU_DMA0); dma_deinit(DMA0, DMA_CH3); dma_init_struct_send.direction = DMA_MEMORY_TO_PERIPH; dma_init_struct_send.memory0_addr = (uint32_t)usart_sendbuf;//类成员变量 dma_init_struct_send.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct_send.periph_memory_width = DMA_MEMORY_WIDTH_8BIT; dma_init_struct_send.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; dma_init_struct_send.number = Usart_SendLen;//类成员变量 dma_init_struct_send.periph_addr = USART2_DATA_ADDRESS; dma_init_struct_send.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_struct_send.priority = DMA_PRIORITY_ULTRA_HIGH; dma_single_data_mode_init(DMA0, DMA_CH3, &dma_init_struct_send); /* configure DMA mode */ dma_circulation_disable(DMA0, DMA_CH3); dma_channel_subperipheral_select(DMA0, DMA_CH3, DMA_SUBPERI4); /* enable DMA channel_3 */ dma_channel_enable(DMA0, DMA_CH3); usart_dma_transmit_config(__usart_periph, USART_DENT_ENABLE); /* wait DMA Channel transfer complete */ // while(RESET == dma_flag_get(DMA0, DMA_CH3, DMA_INTF_FTFIF)); } }

这里的内存地址是 uint8_t usart_sendbuf[USART_MAX_LEN]; USART_MAX_LEN = 256

硬件地址是 在这里插入图片描述#define USART2_DATA_ADDRESS ((uint32_t)0x40004800 + 0x04)

发送的字节Usart_SendLen是不定的,需要你每次发送数据时对Usart_SendLen赋值,然后都要执行下面这一整段代码来配置dma,才能发送出去 usart_dma_transmit_config(__usart_periph, USART_DENT_ENABLE); 这句库函数代码就是使能发送 在这里插入图片描述

在这里插入图片描述

void usart_dma_transmit_config(uint32_t usart_periph, uint32_t dmacmd) { uint32_t ctl = 0U; ctl = USART_CTL2(usart_periph); ctl &= ~USART_CTL2_DENT; ctl |= dmacmd; /* configure DMA transmission */ USART_CTL2(usart_periph) = ctl; } 接收DMA

注意要我代码的注释: dma_init_struct_recv.number = USART_MAX_LEN; //使用宏,不像发送那样有几个才配置几个,是程序一开始就配置了,然后在接收中断那里接收完后再配置

这是能不断用DMA接收的原因。

//配合串口的空闲中断 void Usart_class::usart_dma_recv_config(void) { dma_single_data_parameter_struct dma_init_struct_recv; if(__usart_periph == USART2) { // enable DMA0 rcu_periph_clock_enable(RCU_DMA0); dma_deinit(DMA0, DMA_CH1); dma_init_struct_recv.direction = DMA_PERIPH_TO_MEMORY; dma_init_struct_recv.memory0_addr = (uint32_t)usart_recebuf; dma_init_struct_recv.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct_recv.periph_memory_width = DMA_MEMORY_WIDTH_8BIT; dma_init_struct_recv.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; dma_init_struct_recv.number = USART_MAX_LEN; //使用宏,不像发送那样有几个才配置几个,是程序一开始就配置了,然后在接收中断那里接收完后再配置 dma_init_struct_recv.periph_addr = USART2_DATA_ADDRESS; dma_init_struct_recv.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_struct_recv.priority = DMA_PRIORITY_ULTRA_HIGH; dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct_recv); /* configure DMA mode */ dma_circulation_disable(DMA0, DMA_CH1); dma_channel_subperipheral_select(DMA0, DMA_CH1, DMA_SUBPERI4); /* enable DMA channel2 */ dma_channel_enable(DMA0, DMA_CH1); usart_dma_receive_config(__usart_periph, USART_DENR_ENABLE); // while(RESET == dma_flag_get(DMA0, DMA_CH1, DMA_INTF_FTFIF)); } } 中断函数:

这里dma_transfer_number_get的意思是还剩下多少个没接收到,例如DMA配置256个字节,那么接收了10个,那么dma_transfer_number_get就返回256-10=246. 然后我再用256 - 246 =10,那么10就是我接收到的数据的个数。

然后把数据复制出来,最后重新配配置接收DMA。

这样,只有当空闲的时候(两个位的时间 间隙),才进中断.

dma_USART_RX_NUM = dma_transfer_number_get(DMA0, DMA_CH1); USART_RX_NUM = USART_MAX_LEN - dma_transfer_number_get(DMA0, DMA_CH1); //空闲中断 extern "C" void USART2_IRQHandler(void) { uint8_t rxbuffer_real[USART_MAX_LEN]; dma_single_data_parameter_struct dma_init_struct; static uint16_t USART_RX_NUM=0,dma_USART_RX_NUM=0;; if((RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE)) && (RESET != usart_flag_get(USART2, USART_FLAG_IDLE))) { dma_flag_clear(DMA0, DMA_CH1, DMA_INTF_FTFIF); usart_data_receive(USART2); /* 清除接收完成标志位 */ dma_channel_disable(DMA0, DMA_CH1); /* 关闭DMA传输 */ dma_USART_RX_NUM = dma_transfer_number_get(DMA0, DMA_CH1); USART_RX_NUM = USART_MAX_LEN - dma_transfer_number_get(DMA0, DMA_CH1); mymemcpy(rxbuffer_real,Global_Usart1_Obj->usart_recebuf,USART_RX_NUM); /* 转存数据到待处理数据缓冲区*/ printf("%s\r\n",rxbuffer_real); rxbuffer_real[USART_RX_NUM] = '\0'; /* 添加字符串结束符 */ /* 重新设置DMA传输 */ dma_deinit(DMA0, DMA_CH1); dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; dma_init_strucmory0_addr = (uint32_t)Global_Usart1_Obj->usart_recebuf; dma_init_strucmory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.periph_memory_width = DMA_MEMORY_WIDTH_8BIT; dma_init_struct.number = USART_MAX_LEN; dma_init_struct.periph_addr = USART2_DATA_ADDRESS; dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; dma_single_data_mode_init(DMA0, DMA_CH1, &dma_init_struct); /* configure DMA mode */ dma_circulation_disable(DMA0, DMA_CH1); dma_channel_subperipheral_select(DMA0, DMA_CH1, DMA_SUBPERI4); /* enable DMA channel2 */ dma_channel_enable(DMA0, DMA_CH1); usart_dma_receive_config(USART2, USART_DENR_ENABLE); } } 注意:这里有一个小BUG:

就是当一次性接收的数据大于你的DMA配置的个数。 例如配置了接收10个,可是别人发了10个 以上的数据,那么 情况1:如果一直发10个以上的话,那么数据会错乱,最后一个数据会覆盖第一个数据 情况2:如果之后是10个以内,那么第一次会错乱,然后之后就会正常。

这个原因可能是DMA溢出错误了,我没有去处理的原因 。 有一种办法是,把 DMA的配置个数改得很大,那么就不容易出现, 还有一种就是新加个DMA溢出中断.(还没实现)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3