串口空闲中断配合DMA收发不定长数据
注意下面这段我是引用的 雨飞工作室的,基本没改动,因为觉得将得很好,这里给出链接
https://mp.weixin.qq.com/s/Zp3NjLbj981W8JX-064Few
解释为何用DMA
串口(UART)是一种低速的串行异步通信,适用于低速通信场景,通常使用的波特率小于或等于115200bps。对于小于或者等于115200bps波特率的,而且数据量不大的通信场景,一般没必要使用DMA,或者说使用DMA并未能充分发挥出DMA的作用。对于数量大,或者波特率提高时,必须使用DMA以释放CPU资源,因为高波特率可能带来CPU资源过度浪费的问题。
举个例子:对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU。对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断。以115200bps波特率,1s大约传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源。对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源。因此,在高波特率传输场景下,串口非常有必要使用DMA。
串口接收完数据是要处理的,那么处理的步骤是怎么样呢?
- 暂时关闭串口接收DMA通道,有两个原因:1.防止后面又有数据接收到,产生干扰,因为此时的数据还未处理。2.DMA需要重新配置。
- 清串口空闲中断标志位。
- 从DMA寄存器中获取接收到的数据字节数(可有可无)。
- 重新设置DMA下次要接收的数据字节数。注意,数据传输数量范围为0至65535。这个寄存器只能在通道不工作(DMA_CCRx的EN=0)时写入。通道开启后该寄存器变为只读,指示剩余的待传输字节数目。寄存器内容在每次DMA传输后递减。数据传输结束后,寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。
- 给出信号量,发送接收到新数据标志,供前台程序查询。
- 开启DMA通道,等待下一次的数据接收,注意,对DMA的相关寄存器配置写入,如重置DMA接收数据长度,必须要在关闭DMA的条件进行,否则操作无效。
注意事项
STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有两种方式解决:(1)在重新开启接收DMA通道之前,将Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。(2)建立双缓冲,重新配置DMA_MemoryBaseAddr的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。
本次实现的是双缓冲,这里参考的是b站超子物联网,思路非常清晰
代码区
usart.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #ifndef __USART_H #define __USART_H
#include "stdint.h" #include "stdarg.h" #include "stdio.h" #include "string.h"
#define U0_TX_SIZE 2048 #define U0_RX_SIZE 2048 #define U0_RX_MAX 256 #define NUM 10
extern uint8_t U0_RxBuff[U0_RX_SIZE];
typedef struct { uint8_t *start; uint8_t *end; }UCB_URxBuffptr;
typedef struct{
uint16_t URxCounter; UCB_URxBuffptr URxDataPtr[NUM]; UCB_URxBuffptr *URxDataIn; UCB_URxBuffptr *URxDataOut; UCB_URxBuffptr *URxDataEnd; }UCB_CB; extern UCB_CB U0_CB;
void Usart0_Init(uint32_t baund); void DMA_Init(void); void U0Rx_PtrInit(void); void u0_printf(char *format,...); #endif
|
usart.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include "usart.h" #include "gd32f10x.h" uint8_t U0_RxBuff[U0_RX_SIZE]; uint8_t U0_TxBuff[U0_TX_SIZE]; UCB_CB U0_CB;
void Usart0_Init(uint32_t baund) { rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_USART0);
gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_9); gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_10);
usart_deinit(USART0); usart_baudrate_set(USART0,baund); usart_parity_config(USART0,USART_PM_NONE); usart_word_length_set(USART0,USART_WL_8BIT); usart_stop_bit_set(USART0,USART_STB_1BIT); usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE); usart_transmit_config(USART0,USART_TRANSMIT_ENABLE); usart_receive_config(USART0,USART_RECEIVE_ENABLE); usart_dma_receive_config(USART0,USART_RECEIVE_DMA_ENABLE); nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); nvic_irq_enable(USART0_IRQn,0,0); usart_interrupt_enable(USART0,USART_INT_IDLE);
DMA_Init(); U0Rx_PtrInit(); usart_enable(USART0); }
void DMA_Init(void) { rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0,DMA_CH4); dma_parameter_struct dma_config_t; dma_config_t.direction = DMA_PERIPHERAL_TO_MEMORY; dma_config_t.memory_addr = (uint32_t)U0_RxBuff; dma_config_t.memory_inc = DMA_MEMORY_INCREASE_ENABLE ; dma_config_t.number = U0_RX_MAX + 1; dma_config_t.memory_width = DMA_MEMORY_WIDTH_8BIT; dma_config_t.periph_addr = USART0 + 4 ; dma_config_t.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_config_t.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; dma_config_t.priority = DMA_PRIORITY_HIGH; dma_struct_para_init(&dma_config_t); dma_circulation_disable(DMA0,DMA_CH4);
dma_init(DMA0,DMA_CH4,&dma_config_t); dma_channel_enable(DMA0,DMA_CH4); }
void U0Rx_PtrInit(void) { U0_CB.URxCounter = 0 ; U0_CB.URxDataIn = &U0_CB.URxDataPtr[0]; U0_CB.URxDataOut = &U0_CB.URxDataPtr[0]; U0_CB.URxDataEnd = &U0_CB.URxDataPtr[NUM-1]; U0_CB.URxDataIn->start = U0_RxBuff; }
void u0_printf(char *format,...) { uint16_t i; va_list listdata; va_start(listdata,format); vsprintf((char *)U0_TxBuff,format,listdata); va_end(listdata); for(i = 0; i < strlen((const char *)U0_TxBuff);i++) { while(usart_flag_get(USART0,USART_FLAG_TBE) != 1); usart_data_transmit(USART0,U0_TxBuff[i]); } while (usart_flag_get(USART0,USART_FLAG_TC) != 1); }
|
串口0中断函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| void USART0_IRQHandler(void) { if(usart_flag_get(USART0,USART_INT_FLAG_IDLE) == 1) { usart_flag_get(USART0,USART_FLAG_IDLEF); usart_data_receive(USART0); } U0_CB.URxCounter += (U0_RX_MAX + 1) - dma_transfer_number_get(DMA0,DMA_CH4); U0_CB.URxDataIn->end = &U0_RxBuff[U0_CB.URxCounter - 1]; U0_CB.URxDataIn++; if( U0_CB.URxDataIn == U0_CB.URxDataEnd) { U0_CB.URxDataIn = &U0_CB.URxDataPtr[0]; } if(U0_RX_SIZE - U0_CB.URxCounter >= 256) { U0_CB.URxDataIn->start = U0_RxBuff[U0_CB.URxCounter]; }else { U0_CB.URxDataIn->start = U0_RxBuff; U0_CB.URxCounter = 0; } dma_channel_disable(DMA0,DMA_CH4); dma_transfer_number_config(DMA0,DMA_CH4,U0_RX_MAX + 1); dma_memory_address_config(DMA0,DMA_CH4,(uint32_t)U0_CB.URxDataIn->start); dma_channel_enable(DMA0,DMA_CH4); }
|
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include "gd32f10x.h" #include "usart.h"
int main(void) { Usart0_Init(115200); uint8_t i;
while (1) { if(U0_CB.URxDataOut != U0_CB.URxDataIn) { u0_printf("本次接受了%d字节数据\r\n",U0_CB.URxDataOut->end - U0_CB.URxDataOut->start + 1); for( i = 0 ; i < U0_CB.URxDataOut->end - U0_CB.URxDataOut->start + 1 ;i++ ) { u0_printf("%c",U0_CB.URxDataOut->start[i]); } u0_printf("\r\n\r\n"); U0_CB.URxDataOut++; if( U0_CB.URxDataOut == U0_CB.URxDataEnd) { U0_CB.URxDataOut = &U0_CB.URxDataPtr[0]; } } } }
|