使用DMA收发串口数据能提高MCU的处理效率,本来打算使用DMA接收+IDLE中断的,但初步实验了下IDLE中断在实际使用中的问题:
1.idle中断空闲只要有1个字节空闲时间就中断,貌似很多M内核芯片都是这样设置的。
2.实验GD32F427R发现idle中断只要空闲1个字符就会进入,正常是IDLE中断发生后清除只有接收到一个字符后芯片再自动重新开启idle中断,找了GD32F4的手册也没有发现相关的描述,虽然能通过接收字符判断但中断太频繁了。
GD32作为国内M核大厂,希望在软件和手册上还是需要加强。最后不断查找发现有一个接收超时中断,但是接收超时中断只能usart使用。
GD32F4为usart_periph: USARTx(x=0,1,2,5)。并且超时的字节数可以配置,这样使用起来还是挺爽的,基本处理都在中断中,实际使用可以使用接收完成标志在程序中轮训处理收到的数据并进行DMA发送。我使用的是USART2,关键代码如下:
1.串口初始化:
rcu\_periph\_clock\_enable(GD32\_COM2\_CLK);
/\* enable USART clock \*/
rcu\_periph\_clock\_enable(GD32\_COM2\_GPIO\_CLK);
/\* connect port to USARTx\_Tx \*/
gpio\_af\_set(GD32\_COM2\_GPIO\_PORT, GD32\_COM2\_AF, GD32\_COM2\_TX\_PIN);
/\* connect port to USARTx\_Rx \*/
gpio\_af\_set(GD32\_COM2\_GPIO\_PORT, GD32\_COM2\_AF, GD32\_COM2\_RX\_PIN);
/\* configure USART Tx as alternate function push-pull \*/
gpio\_mode\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_MODE\_AF, GPIO\_PUPD\_PULLUP,GD32\_COM2\_TX\_PIN);
gpio\_output\_options\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_OTYPE\_PP, GPIO\_OSPEED\_50MHZ,GD32\_COM2\_TX\_PIN);
/\* configure USART Rx as alternate function push-pull \*/
gpio\_mode\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_MODE\_AF, GPIO\_PUPD\_PULLUP,GD32\_COM2\_RX\_PIN);
gpio\_output\_options\_set(GD32\_COM2\_GPIO\_PORT, GPIO\_OTYPE\_PP, GPIO\_OSPEED\_50MHZ,GD32\_COM2\_RX\_PIN);
/\* USART configure \*/
usart\_deinit(com);
usart\_oversample\_config(com, USART\_OVSMOD\_8);
usart\_baudrate\_set(com,baudrate);
usart\_parity\_config(com, USART\_PM\_NONE);
usart\_word\_length\_set(com, USART\_WL\_8BIT);
usart\_stop\_bit\_set(com, USART\_STB\_1BIT);
usart\_receive\_config(com, USART\_RECEIVE\_ENABLE);
usart\_transmit\_config(com, USART\_TRANSMIT\_ENABLE);
usart\_receiver\_timeout\_threshold\_config(com,10);
usart\_interrupt\_enable(com,USART\_INT\_RT);
usart\_receiver\_timeout\_enable(com);
usart\_enable(com);
usart\_dma\_transmit\_config(com, USART\_DENT\_ENABLE);
/\* USART interrupt configuration \*/
nvic\_irq\_enable(USART2\_IRQn, 1, 1);
/\*uart dma rx set\*/
dma\_single\_data\_parameter\_struct dma\_init\_struct;
/\* enable DMA0 \*/
rcu\_periph\_clock\_enable(RCU\_DMA0);
/\* deinitialize DMA channel \*/
dma\_deinit(DMA0, DMA\_CH1);
dma\_init\_struct.direction = DMA\_PERIPH\_TO\_MEMORY;
dma\_init\_struct.memory0\_addr = (uint32\_t)rxbuffer;
dma\_init\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;
dma\_init\_struct.periph\_memory\_width = DMA\_PERIPH\_WIDTH\_8BIT;
dma\_init\_struct.number = 300;
dma\_init\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);
dma\_init\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;
dma\_init\_struct.priority = DMA\_PRIORITY\_MEDIUM;
dma\_single\_data\_mode\_init(DMA0, DMA\_CH1, &dma\_init\_struct);
/\* configure DMA mode \*/
dma\_circulation\_enable(DMA0, DMA\_CH1);
dma\_channel\_subperipheral\_select(DMA0, DMA\_CH1, DMA\_SUBPERI4);
dma\_transfer\_number\_config(DMA0, DMA\_CH1, 300);
dma\_channel\_enable(DMA0, DMA\_CH1);
usart\_dma\_receive\_config(USART2, USART\_DENR\_ENABLE);
/\* enable DMA1 channel2 transfer complete interrupt \*/
dma\_interrupt\_enable(DMA0, DMA\_CH1, DMA\_CHXCTL\_FTFIE);
nvic\_irq\_enable(DMA0\_Channel1\_IRQn, 0, 1);
2.DMA发送
void USART\_SendString\_DMA(uint8\_t \* str, uint32\_t len)
{
dma\_single\_data\_parameter\_struct dma\_struct = {0};
/\* enable DMA1 \*/
rcu\_periph\_clock\_enable(RCU\_DMA0);
/\* deinitialize DMA channel7(USART2 TX) \*/
dma\_deinit(DMA0, DMA\_CH3);
dma\_struct.direction = DMA\_MEMORY\_TO\_PERIPH;
dma\_struct.memory0\_addr = (uint32\_t)str;
dma\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;
dma\_struct.periph\_memory\_width = DMA\_MEMORY\_WIDTH\_8BIT;
dma\_struct.number = len;
dma\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);
dma\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;
dma\_struct.priority = DMA\_PRIORITY\_HIGH;
dma\_single\_data\_mode\_init(DMA0, DMA\_CH3, &dma\_struct);
dma\_circulation\_disable(DMA0, DMA\_CH3);
dma\_flag\_clear(DMA0, DMA\_CH3, DMA\_FLAG\_FTF);
dma\_channel\_subperipheral\_select(DMA0, DMA\_CH3, DMA\_SUBPERI4);
/\* enable DMA1 channel2 transfer complete interrupt \*/
dma\_interrupt\_enable(DMA0, DMA\_CH3, DMA\_CHXCTL\_FTFIE);
uart\_state = 0;
dma\_channel\_enable(DMA0, DMA\_CH3);
}
3.DMA发送完成中断
void DMA0\_Channel3\_IRQHandler(void)
{
if(dma\_interrupt\_flag\_get(DMA0, DMA\_CH3, DMA\_INT\_FLAG\_FTF)) {
dma\_interrupt\_flag\_clear(DMA0, DMA\_CH3, DMA\_INT\_FLAG\_FTF);
uart\_state = 1;
dma\_channel\_disable(DMA0, DMA\_CH3);
/\*打开DMA接收传输\*/
Receive\_DataStart(GD32\_COM4);
}
}
4.USART接收超时中断
void USART2\_IRQHandler(void)
{
if(usart\_interrupt\_flag\_get(GD32\_COM4,USART\_INT\_FLAG\_RT))
{
usart\_interrupt\_flag\_clear(GD32\_COM4,USART\_INT\_FLAG\_RT);
EventStopB(0);
Receive\_DataPack(GD32\_COM4);
EventStartB(0);
}
}
5.DMA接收完成进行DMA发送
void Receive\_DataPack(uint32\_t com){
/\* 关闭DMA,防止干扰 \*/
dma\_channel\_disable(DMA0, DMA\_CH1);
/\* 清DMA标志位 \*/
dma\_flag\_clear(DMA0, DMA\_CH1, DMA\_INTF\_FTFIF);
/\* 获取接收到的数据长度,单位:字节 \*/
int len = 300 - dma\_transfer\_number\_get(DMA0, DMA\_CH1);
if(len > 0)
{
memcpy(txbuffer,rxbuffer,len);
USART\_SendString\_DMA(txbuffer,len);
}
Receive\_DataStart(GD32\_COM4);
}
6.重新开启接收DMA
void Receive\_DataStart(uint32\_t com){
dma\_single\_data\_parameter\_struct dma\_init\_struct;
/\* deinitialize DMA channel \*/
dma\_deinit(DMA0, DMA\_CH1);
dma\_init\_struct.direction = DMA\_PERIPH\_TO\_MEMORY;
dma\_init\_struct.memory0\_addr = (uint32\_t)rxbuffer;
dma\_init\_struct.memory\_inc = DMA\_MEMORY\_INCREASE\_ENABLE;
dma\_init\_struct.periph\_memory\_width = DMA\_PERIPH\_WIDTH\_8BIT;
dma\_init\_struct.number = 300;
dma\_init\_struct.periph\_addr = (uint32\_t)&USART\_DATA(GD32\_COM2);
dma\_init\_struct.periph\_inc = DMA\_PERIPH\_INCREASE\_DISABLE;
dma\_init\_struct.priority = DMA\_PRIORITY\_MEDIUM;
dma\_single\_data\_mode\_init(DMA0, DMA\_CH1, &dma\_init\_struct);
/\* configure DMA mode \*/
dma\_circulation\_enable(DMA0, DMA\_CH1);
dma\_channel\_subperipheral\_select(DMA0, DMA\_CH1, DMA\_SUBPERI4);
/\* 重新赋值计数值 \*/
dma\_transfer\_number\_config(DMA0, DMA\_CH1, 300);
/\* 重新打开DMA \*/
dma\_channel\_enable(DMA0, DMA\_CH1);
usart\_dma\_receive\_config(USART2, USART\_DENR\_ENABLE);
/\* enable DMA1 channel2 transfer complete interrupt \*/
dma\_interrupt\_enable(DMA0, DMA\_CH1, DMA\_CHXCTL\_FTFIE);
nvic\_irq\_enable(DMA0\_Channel1\_IRQn, 0, 1);
}
原作者:兆易创新GD32 MCU 汪阳