2013-12-17 77 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單片機。