2013-12-17 45 views
4

使用带有dsPIC33FJ128GP802微控制器的MPLAB X 1.70。适用于不同大小阵列的UART DMA

我有一个应用程序从两个传感器以不同的采样率(一个在50Hz,另一个在1000Hz)采集数据,两个传感器数据包的大小也不同(一个是5个字节,另一个是21个字节)。到目前为止我用手动UART transmision所见如下:

void UART_send(char *txbuf, char size) { 
    // Loop variable. 
    char i; 

    // Loop through the size of the buffer until all data is sent. The while 
    // loop inside checks for the buffer to be clear. 
    for (i = 0; i < size; i++) { 
     while (U1STAbits.UTXBF); 
     U1TXREG = *txbuf++; 
    } 
} 

的不同尺寸的阵列(5或21个字节)被发送到该功能,与它们的尺寸,以及一个简单的for循环通过每个字节循环并通过UART tx寄存器U1TXREG输出。

现在,我想要实现DMA来减轻传输大量数据时对系统的一些压力。我用DMA来接收UART和ADC,但是在传输时遇到问题。我已经尝试了乒乓模式的开启和关闭,以及单次模式和连续模式,但是无论何时发送21字节的数据包,都会使用奇怪的值和零值填充混淆。

我正在初始化DMA,如下所示。

void UART_TX_DMA_init() { 

     DMA2CONbits.SIZE = 0;         // 0: word; 1: byte 
     DMA2CONbits.DIR = 1;         // 0: uart to device; 1: device to uart 
     DMA2CONbits.AMODE = 0b00; 
     DMA2CONbits.MODE = 1;         // 0: contin, no ping pong; 1: oneshot, no ping pong; 2: contin, ping pong; 3: oneshot, ping pong. 

     DMA2PAD = (volatile unsigned int) &U1TXREG; 

     DMA2REQ = 12;     // Select UART1 Transmitter 

     IFS1bits.DMA2IF = 0;   // Clear DMA Interrupt Flag 
     IEC1bits.DMA2IE = 1;   // Enable DMA interrupt 
} 

DMA中断我只是清除标志。建立DMA阵列我有以下功能:

char TXBufferADC[5] __attribute__((space(dma))); 
char TXBufferIMU[21] __attribute__((space(dma))); 

void UART_send(char *txbuf, char size) { 

    // Loop variable. 
    int i; 

    DMA2CNT = size - 1; // x DMA requests 

    if (size == ADCPACKETSIZE) { 
     DMA2STA = __builtin_dmaoffset(TXBufferADC); 
     for (i = 0; i < size; i++) { 
      TXBufferADC[i] = *txbuf++; 
     } 
    } else if (size == IMUPACKETSIZE) { 
     DMA2STA = __builtin_dmaoffset(TXBufferIMU); 
     for (i = 0; i < size; i++) { 
      TXBufferIMU[i] = *txbuf++; 
     } 
    } else { 
     NOTIFICATIONLED ^= 1; 
    } 

    DMA2CONbits.CHEN = 1; // Re-enable DMA2 Channel 
    DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer 
} 

这个例子是关闭乒乓。我正在使用相同的DMA2STA寄存器,但根据我具有的数据包类型更改阵列。我从要发送的数据中确定数据包类型,更改要发送的DMA字节(DMA2CNT),使用for循环构建与之前相同的数组,然后强制第一次传输并重新启用通道。

处理大数据包的数据需要更长的时间,我开始认为DMA丢失了这些数据包,并在它的位置发送空/奇怪数据包。在构建缓冲区并强制第一次传输之前,似乎是轮询。也许这个力量对于每一次民意测验都是不必要的我不知道...

任何帮助将是伟大的。

+0

您是否需要在'UART_send()'中禁用中断以防止在那里发生中断? – chux

+0

好建议,明天再试,谢谢。那不会否定DMA的观点吗?如果我故意暂停DMA中断,那么它不会像手动UART方法一样快? – ritchie888

+0

也许我应该说“在UART_send()中,您是否需要禁用中断以防止在那里处理中断?”。它可以在例程中产生中断,但可能需要将例程设为原子级,并在中间被阻止中断。我只是建议在'UART_send()'中暂停中断的处理。 – chux

回答

1

经过几天的工作,我觉得我已经明白了。

我遇到的主要问题是DMA中断的轮询速度比之前的传输速度要快,因此在下一个软件包覆盖之前我只能获得软件包的分段。这与简单地等待UART传输结束时解决:

while(!U1STAbits.TRMT);

我设法避免了通过简单地使原始数据阵列成为DMA识别的数据阵列来重新创建具有包数据的新DMA的冗余。

到底这个过程是相当小,叫每一个包创建时间的功能是:

void sendData() { 
    // Check that last transmission has completed. 
    while (!U1STAbits.TRMT); 

    DMA2CNT = bufferSize - 1; 

    DMA2STA = __builtin_dmaoffset(data); 

    DMA2CONbits.CHEN = 1; // Re-enable DMA0 Channel 
    DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer 
} 

无论什么样的包的大小,DMA改变它发送使用DMA2CNT量注册,那么它只是重新启用DMA并强制第一位。

设置的DMA是:

DMA2CONbits.SIZE = 1; 
DMA2CONbits.DIR = 1; 
DMA2CONbits.AMODE = 0b00; 
DMA2CONbits.MODE = 1; 

DMA2PAD = (volatile unsigned int) &U1TXREG; 

DMA2REQ = 12;        // Select UART1 Transmitter 

IFS1bits.DMA2IF = 0;      // Clear DMA Interrupt Flag 
IEC1bits.DMA2IE = 1;      // Enable DMA interrupt 

这是一次性的,没有乒乓球,字节传输,和所有UART1 TX正确的参数。

希望这可以帮助未来的人,总体原则可以应用于大多数PIC单片机。