DMA高级应用
目录
- DMA基础回顾
- ADC + DMA
- UART + DMA
- UART超时解析
- 实战示例
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;
__interrupt void dma_ch1_isr(void) { dma_complete = true; Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP7); }
void ADC_DMA_Init(void) { 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);
ADC_setupSOC(ADCA_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM1_SOCA, ADC_CH_ADCIN0, 15);
EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP); EPWM_setTimeBasePeriod(EPWM1_BASE, 10000); 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_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_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;
for(int i = 0; i < ADC_BUFFER_SIZE; i++) { float voltage = (float)adc_buffer[i] * 3.3f / 4095.0f; }
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) {
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) { 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_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_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) { 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_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
char rx_buffer[RX_BUFFER_SIZE]; volatile uint16_t rx_index = 0; volatile bool rx_timeout = false; volatile uint32_t last_rx_time = 0;
__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); }
__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) { 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);
CPUTimer_stopTimer(CPUTIMER0_BASE); CPUTimer_setPeriod(CPUTIMER0_BASE, 100000); 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
| void UART_Idle_Init(void) { 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
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; DMA_configAddresses(DMA_CH1_BASE, (uint16_t *)&AdcaResultRegs.ADCRESULT0, adc_buffer_b); use_buffer_a = false; } else { buffer_b_ready = true; 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_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_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP); EPWM_setTimeBasePeriod(EPWM1_BASE, 2000); EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_ZERO); EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
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; for(int i = 0; i < ADC_BUFFER_SIZE; i++) { } }
if(buffer_b_ready) { buffer_b_ready = false; } } }
|
示例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>
#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: break; case 0x02: 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