rit*_*888 5 c microcontroller pic dma mplab
将 MPLAB X 1.70 与 dsPIC33FJ128GP802 微控制器一起使用。
我有一个应用程序,它以不同的采样率(一个为 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++;
}
}
Run Code Online (Sandbox Code Playgroud)
不同大小的数组(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
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
这个例子是关闭乒乓球的。我使用相同的 DMA2STA 寄存器,但根据我拥有的数据包类型更改数组。我正在根据要发送的数据确定数据包类型,更改要发送的 DMA 字节 (DMA2CNT),使用 for 循环构建与以前相同的数组,然后强制第一次传输并重新启用通道。
处理大数据包的数据需要更长的时间,我开始认为 DMA 缺少这些数据包并在其位置发送空/奇怪的数据包。在我构建缓冲区并强制第一次传输之前,它似乎正在轮询。也许不是每次投票都需要强制;我不知道...
任何帮助都会很棒。
经过几天的努力,我想我已经明白了。
我遇到的主要问题是 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
}
Run Code Online (Sandbox Code Playgroud)
无论数据包的大小如何,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
Run Code Online (Sandbox Code Playgroud)
这是一次性的、无乒乓、字节传输以及 UART1 TX 的所有正确参数。
希望这对将来的人有所帮助,一般原理可以应用于大多数 PIC 微控制器。