c - 用于不同大小阵列的 UART DMA

标签 c microcontroller pic dma mplab

将 MPLAB X 1.70 与 dsPIC33FJ128GP802 微 Controller 结合使用。

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

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
}

此示例关闭了 ping pong。我使用相同的 DMA2STA 寄存器,但根据我拥有的数据包类型更改数组。我根据要发送的数据确定数据包类型,更改要发送的 DMA 字节 (DMA2CNT),使用 for 循环构建与之前相同的数组,然后强制进行第一次传输并重新启用 channel 。

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

任何帮助都会很棒。

最佳答案

经过几天的努力,我想我已经搞定了。

我遇到的主要问题是 DMA 中断的轮询速度比之前的传输快,因此我只能在下一个包覆盖前一个包之前获取包的片段。只需等待 UART 传输结束即可解决此问题:

同时 (!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 微 Controller 。

关于c - 用于不同大小阵列的 UART DMA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20643260/

相关文章:

c - MPLab PORTFbits 没有名为 RF0 的成员

c - int x[10] 分配了多少内存?

c - ARM Cortex-M3 上的 ELF 重定位

c - C 中的函数、队列、全局变量

c - 如何使用串口控制Arduino板或独立AVR?

c - PIC16f877a 开关读取不正确

c - 与传感器 CCS 编译器的 I2C 通信

java - 如何使用 swig 在 C 和 Java 之间传递包含数组的结构体?

对 libcap (pcap) 和无线感到困惑

c - 通过指针访问多维数组