DMA高级应用

目录

  1. DMA基础回顾
  2. ADC + DMA
  3. UART + DMA
  4. UART超时解析
  5. 实战示例

DMA基础回顾

DMA (Direct Memory Access) 可以在不占用CPU的情况下传输数据。

DMA特性

  • 通道数: 6个独立DMA通道
  • 触发源: ADC, SPI, SCI, ePWM等
  • 传输模式: 单次、突发、连续
  • 地址模式: 固定地址或递增地址

ADC + DMA

使用DMA自动传输ADC结果,无需CPU干预。

配置步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "driverlib.h"
#include "device.h"

#define ADC_BUFFER_SIZE 256

uint16_t adc_buffer[ADC_BUFFER_SIZE];
volatile bool dma_complete = false;

// DMA中断
__interrupt void dma_ch1_isr(void)
{
dma_complete = true;
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

void ADC_DMA_Init(void)
{
// 配置ADC
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_4_0);
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
ADC_enableConverter(ADCA_BASE);
DEVICE_DELAY_US(1000);

// 配置SOC0(ePWM触发)
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0,
ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);

// 配置ePWM1触发ADC(10kHz采样率)
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
EPWM_setTimeBasePeriod(EPWM1_BASE, 10000); // 10kHz
EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);
EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);

// 配置DMA
DMA_initController();
DMA_configAddresses(DMA_CH1_BASE,
(uint16_t *)&AdcaResultRegs.ADCRESULT0,
adc_buffer);
DMA_configBurst(DMA_CH1_BASE, 1, 1, 1);
DMA_configTransfer(DMA_CH1_BASE, ADC_BUFFER_SIZE, 1, 1);
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA1,
DMA_CFG_ONESHOT_ENABLE | DMA_CFG_SIZE_16BIT);

// 使能DMA中断
DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH1_BASE);
Interrupt_register(INT_DMA_CH1, &dma_ch1_isr);
Interrupt_enable(INT_DMA_CH1);

DMA_enableTrigger(DMA_CH1_BASE);
DMA_startChannel(DMA_CH1_BASE);
}

void main(void)
{
Device_init();
Device_initGPIO();

Interrupt_initModule();
Interrupt_initVectorTable();

ADC_DMA_Init();

EINT;
ERTM;

while(1)
{
if(dma_complete)
{
dma_complete = false;

// 处理ADC数据
for(int i = 0; i < ADC_BUFFER_SIZE; i++)
{
float voltage = (float)adc_buffer[i] * 3.3f / 4095.0f;
// 处理数据
}

// 重启DMA
DMA_startChannel(DMA_CH1_BASE);
}
}
}

连续采样模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void ADC_DMA_Continuous_Init(void)
{
// ADC和ePWM配置同上...

// 配置DMA为连续模式(乒乓缓冲)
DMA_configAddresses(DMA_CH1_BASE,
(uint16_t *)&AdcaResultRegs.ADCRESULT0,
adc_buffer);
DMA_configBurst(DMA_CH1_BASE, 1, 1, 1);
DMA_configTransfer(DMA_CH1_BASE, ADC_BUFFER_SIZE, 1, 1);
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA1,
DMA_CFG_ONESHOT_DISABLE | // 连续模式
DMA_CFG_CONTINUOUS_ENABLE |
DMA_CFG_SIZE_16BIT);

DMA_enableTrigger(DMA_CH1_BASE);
DMA_startChannel(DMA_CH1_BASE);
}

UART + DMA

使用DMA进行UART数据传输,提高效率。

UART发送DMA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "driverlib.h"
#include "device.h"

char tx_buffer[256];
volatile bool tx_complete = false;

__interrupt void dma_ch2_isr(void)
{
tx_complete = true;
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

void UART_TX_DMA_Init(void)
{
// 配置UART
GPIO_setPinConfig(GPIO_28_SCIA_TX);
SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200,
(SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE));
SCI_enableFIFO(SCIA_BASE);
SCI_enableModule(SCIA_BASE);
SCI_resetChannels(SCIA_BASE);

// 配置DMA
DMA_initController();
DMA_configAddresses(DMA_CH2_BASE, tx_buffer,
(uint16_t *)&SciaRegs.SCITXBUF);
DMA_configBurst(DMA_CH2_BASE, 1, 1, 1);
DMA_configMode(DMA_CH2_BASE, DMA_TRIGGER_SCIATX,
DMA_CFG_ONESHOT_ENABLE | DMA_CFG_SIZE_16BIT);

DMA_setInterruptMode(DMA_CH2_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH2_BASE);
Interrupt_register(INT_DMA_CH2, &dma_ch2_isr);
Interrupt_enable(INT_DMA_CH2);
}

void UART_SendString_DMA(const char *str, uint16_t len)
{
// 等待上次传输完成
while(!tx_complete && DMA_getTransferStatusFlag(DMA_CH2_BASE));

tx_complete = false;

// 复制数据到缓冲区
for(uint16_t i = 0; i < len; i++)
{
tx_buffer[i] = str[i];
}

// 配置传输长度
DMA_configTransfer(DMA_CH2_BASE, len, 1, 1);

// 启动DMA
DMA_enableTrigger(DMA_CH2_BASE);
DMA_startChannel(DMA_CH2_BASE);
}

UART接收DMA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#define RX_BUFFER_SIZE 128

char rx_buffer[RX_BUFFER_SIZE];
volatile bool rx_complete = false;

__interrupt void dma_ch3_isr(void)
{
rx_complete = true;
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

void UART_RX_DMA_Init(void)
{
// 配置UART
GPIO_setPinConfig(GPIO_29_SCIA_RX);
GPIO_setPadConfig(29, GPIO_PIN_TYPE_PULLUP);

SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200,
(SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE));
SCI_enableFIFO(SCIA_BASE);
SCI_enableModule(SCIA_BASE);
SCI_resetChannels(SCIA_BASE);

// 配置DMA
DMA_initController();
DMA_configAddresses(DMA_CH3_BASE,
(uint16_t *)&SciaRegs.SCIRXBUF,
(uint16_t *)rx_buffer);
DMA_configBurst(DMA_CH3_BASE, 1, 1, 1);
DMA_configTransfer(DMA_CH3_BASE, RX_BUFFER_SIZE, 1, 1);
DMA_configMode(DMA_CH3_BASE, DMA_TRIGGER_SCIARX,
DMA_CFG_ONESHOT_ENABLE | DMA_CFG_SIZE_16BIT);

DMA_setInterruptMode(DMA_CH3_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH3_BASE);
Interrupt_register(INT_DMA_CH3, &dma_ch3_isr);
Interrupt_enable(INT_DMA_CH3);

DMA_enableTrigger(DMA_CH3_BASE);
DMA_startChannel(DMA_CH3_BASE);
}

UART超时解析

使用定时器检测UART接收超时,实现数据包解析。

方法1: 定时器超时检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "driverlib.h"
#include "device.h"

#define RX_BUFFER_SIZE 256
#define TIMEOUT_MS 10 // 10ms超时

char rx_buffer[RX_BUFFER_SIZE];
volatile uint16_t rx_index = 0;
volatile bool rx_timeout = false;
volatile uint32_t last_rx_time = 0;

// UART接收中断
__interrupt void sciaRxISR(void)
{
while(SCI_getRxFIFOStatus(SCIA_BASE) != SCI_FIFO_RX0)
{
char c = SCI_readCharNonBlocking(SCIA_BASE);

if(rx_index < RX_BUFFER_SIZE)
{
rx_buffer[rx_index++] = c;
last_rx_time = 0; // 重置超时计数
}
}

SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}

// 定时器中断(1ms)
__interrupt void cpuTimer0ISR(void)
{
if(rx_index > 0)
{
last_rx_time++;

if(last_rx_time >= TIMEOUT_MS)
{
rx_timeout = true;
last_rx_time = 0;
}
}

Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}

void UART_Timeout_Init(void)
{
// 配置UART
GPIO_setPinConfig(GPIO_28_SCIA_TX);
GPIO_setPinConfig(GPIO_29_SCIA_RX);

SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200,
(SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE));
SCI_enableFIFO(SCIA_BASE);
SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX0, SCI_FIFO_RX1);
SCI_enableInterrupt(SCIA_BASE, SCI_INT_RXFF);
SCI_enableModule(SCIA_BASE);
SCI_resetChannels(SCIA_BASE);

// 配置定时器(1ms)
CPUTimer_stopTimer(CPUTIMER0_BASE);
CPUTimer_setPeriod(CPUTIMER0_BASE, 100000); // 1ms
CPUTimer_setPreScaler(CPUTIMER0_BASE, 0);
CPUTimer_reloadTimerCounter(CPUTIMER0_BASE);
CPUTimer_setEmulationMode(CPUTIMER0_BASE, CPUTIMER_EMULATIONMODE_RUNFREE);
CPUTimer_enableInterrupt(CPUTIMER0_BASE);

// 注册中断
Interrupt_register(INT_SCIA_RX, &sciaRxISR);
Interrupt_enable(INT_SCIA_RX);
Interrupt_register(INT_TIMER0, &cpuTimer0ISR);
Interrupt_enable(INT_TIMER0);

CPUTimer_startTimer(CPUTIMER0_BASE);
}

void ProcessPacket(char *data, uint16_t len)
{
// 处理接收到的数据包
// 例如:解析协议、校验等
}

void main(void)
{
Device_init();
Device_initGPIO();

Interrupt_initModule();
Interrupt_initVectorTable();

UART_Timeout_Init();

EINT;
ERTM;

while(1)
{
if(rx_timeout)
{
rx_timeout = false;

// 处理接收到的数据
ProcessPacket(rx_buffer, rx_index);

// 清空缓冲区
rx_index = 0;
}
}
}

方法2: 空闲中断检测

1
2
3
4
5
6
7
8
// 使用UART空闲检测(如果硬件支持)
void UART_Idle_Init(void)
{
// 配置UART接收空闲中断
SCI_enableInterrupt(SCIA_BASE, SCI_INT_RXFF | SCI_INT_RXERR);

// 在接收中断中检测空闲状态
}

实战示例

示例1: 高速数据采集系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "driverlib.h"
#include "device.h"

#define ADC_BUFFER_SIZE 512
#define SAMPLE_RATE 50000 // 50kHz

uint16_t adc_buffer_a[ADC_BUFFER_SIZE];
uint16_t adc_buffer_b[ADC_BUFFER_SIZE];
volatile bool buffer_a_ready = false;
volatile bool buffer_b_ready = false;
volatile bool use_buffer_a = true;

__interrupt void dma_ch1_isr(void)
{
if(use_buffer_a)
{
buffer_a_ready = true;
// 切换到buffer B
DMA_configAddresses(DMA_CH1_BASE,
(uint16_t *)&AdcaResultRegs.ADCRESULT0,
adc_buffer_b);
use_buffer_a = false;
}
else
{
buffer_b_ready = true;
// 切换到buffer A
DMA_configAddresses(DMA_CH1_BASE,
(uint16_t *)&AdcaResultRegs.ADCRESULT0,
adc_buffer_a);
use_buffer_a = true;
}

DMA_startChannel(DMA_CH1_BASE);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7);
}

void HighSpeed_DAQ_Init(void)
{
// 配置ADC
ADC_setPrescaler(ADCA_BASE, ADC_CLK_DIV_2_0);
ADC_setMode(ADCA_BASE, ADC_RESOLUTION_12BIT, ADC_MODE_SINGLE_ENDED);
ADC_enableConverter(ADCA_BASE);
DEVICE_DELAY_US(1000);

ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0,
ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 10);

// 配置ePWM(50kHz触发)
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
EPWM_setTimeBasePeriod(EPWM1_BASE, 2000); // 50kHz
EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO);
EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);

// 配置DMA(乒乓缓冲)
DMA_initController();
DMA_configAddresses(DMA_CH1_BASE,
(uint16_t *)&AdcaResultRegs.ADCRESULT0,
adc_buffer_a);
DMA_configBurst(DMA_CH1_BASE, 1, 1, 1);
DMA_configTransfer(DMA_CH1_BASE, ADC_BUFFER_SIZE, 1, 1);
DMA_configMode(DMA_CH1_BASE, DMA_TRIGGER_ADCA1,
DMA_CFG_ONESHOT_ENABLE | DMA_CFG_SIZE_16BIT);

DMA_setInterruptMode(DMA_CH1_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH1_BASE);
Interrupt_register(INT_DMA_CH1, &dma_ch1_isr);
Interrupt_enable(INT_DMA_CH1);

DMA_enableTrigger(DMA_CH1_BASE);
DMA_startChannel(DMA_CH1_BASE);
}

void main(void)
{
Device_init();
Device_initGPIO();

Interrupt_initModule();
Interrupt_initVectorTable();

HighSpeed_DAQ_Init();

EINT;
ERTM;

while(1)
{
if(buffer_a_ready)
{
buffer_a_ready = false;
// 处理buffer A数据
for(int i = 0; i < ADC_BUFFER_SIZE; i++)
{
// FFT、滤波等处理
}
}

if(buffer_b_ready)
{
buffer_b_ready = false;
// 处理buffer B数据
}
}
}

示例2: UART协议解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include "driverlib.h"
#include "device.h"
#include <string.h>

// 协议格式: 0xAA 0x55 [LEN] [DATA...] [CHECKSUM]
#define PACKET_HEADER1 0xAA
#define PACKET_HEADER2 0x55
#define MAX_PACKET_SIZE 64

typedef struct {
uint8_t header1;
uint8_t header2;
uint8_t length;
uint8_t data[MAX_PACKET_SIZE];
uint8_t checksum;
} Packet_t;

char rx_buffer[MAX_PACKET_SIZE + 10];
volatile uint16_t rx_index = 0;
volatile bool packet_ready = false;

uint8_t CalculateChecksum(uint8_t *data, uint16_t len)
{
uint8_t sum = 0;
for(uint16_t i = 0; i < len; i++)
{
sum += data[i];
}
return sum;
}

bool ParsePacket(char *buffer, uint16_t len, Packet_t *packet)
{
if(len < 4) return false;

// 查找包头
for(uint16_t i = 0; i < len - 1; i++)
{
if(buffer[i] == PACKET_HEADER1 && buffer[i+1] == PACKET_HEADER2)
{
packet->header1 = buffer[i];
packet->header2 = buffer[i+1];
packet->length = buffer[i+2];

if(i + 3 + packet->length + 1 > len)
return false;

memcpy(packet->data, &buffer[i+3], packet->length);
packet->checksum = buffer[i+3+packet->length];

// 校验
uint8_t calc_sum = CalculateChecksum(&buffer[i+2], packet->length + 1);
if(calc_sum == packet->checksum)
{
return true;
}
}
}

return false;
}

void main(void)
{
Device_init();
Device_initGPIO();

Interrupt_initModule();
Interrupt_initVectorTable();

UART_Timeout_Init();

EINT;
ERTM;

Packet_t packet;

while(1)
{
if(packet_ready)
{
packet_ready = false;

if(ParsePacket(rx_buffer, rx_index, &packet))
{
// 处理有效数据包
switch(packet.data[0])
{
case 0x01: // 命令1
break;
case 0x02: // 命令2
break;
}
}

rx_index = 0;
}
}
}

性能对比

CPU占用率对比

方式 CPU占用率 数据吞吐量
轮询 80-90%
中断 30-50%
DMA 5-10%

适用场景

  • 轮询: 简单应用,低速数据
  • 中断: 中等速度,需要实时响应
  • DMA: 高速数据传输,释放CPU

常见问题

1. DMA传输失败?

  • 检查源地址和目标地址是否正确
  • 检查传输长度配置
  • 检查触发源配置

2. UART超时不准确?

  • 调整超时时间
  • 检查定时器配置
  • 考虑波特率和数据长度

3. 乒乓缓冲如何实现?

  • 使用两个缓冲区
  • DMA中断中切换地址
  • 主循环处理完成的缓冲区

下一步

继续学习:


参考资料

  • TMS320F28004x Technical Reference Manual - DMA Module
  • C2000Ware DMA Examples
  • Application Note: High-Speed Data Acquisition with DMA