回复:DMA Ping-Pong应用 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Overview Ping-Pong是一种特殊的已链接传输情况,由于与更复杂版本的已链接传输相比,它的使用通常更为频繁。 Ping-Pong传输通常至少使用两个缓冲区。不论何时,DMA操作都会载入或取消载入一个缓冲区。另一个缓冲区的操作与由软件控制的操作相反,这样在DMA控制器当前使用的缓冲区为满或空时,可将该缓冲区准备好以供使用。图1显示了从外设至存储器中两个缓冲区的Ping-Pong传输的描述符示例。 图1 实施细节 为了将ADC转换结果连续传输到RAM,我将使用四个DMA描述符以Ping-Pong模式工作。 实现这一目标,如图2所示。 图 2 Ping-Pong 模式下的数据流 硬件介绍 LPCXpressor54114 Board(OM13089) 图3 LPCXpressor54114板 示例代码:LPCOpen Library 示例代码 该代码基于periph_adc示例程序,使用SCTimer输出作为ADC的硬件触发信号,同时,ADC转换后的值会自动传输到指定的RAM区域。 #include "board.h"
#define SCT_PWM LPC_SCT
#define NUM_BUFFERS 4
#define DMA_TRANSFER_SIZE 8
#define ADC_INPUT_CHANNEL 1
#define SCT_PWM_RATE 10000 /* PWM frequency 10 KHz */
#define SCT_PWM_PIN_OUT 7 /* COUT7 Generate square wave */
#define SCT_PWM_OUT 1 /* Index of OUT PWM */
uint16_t adcOut;
ALIGN(512) DMA_CHDESC_T ADC_TransferDescriptors[NUM_BUFFERS];
uint16_t CapturedData[32];
uint16_t DMA_Sum=0;
/**
*
* ADC IRQ not Used right now... Only for testing
*/
void ADC_SEQA_IRQHandler(void)
{
/* If SeqA flags is set i.e. data in global register is valid then read it */
Chip_GPIO_SetPinState(LPC_GPIO, 0, 6, true);
//DEBUGOUT("ADC Output = %d\r\n", adcOut);
Chip_GPIO_SetPinState(LPC_GPIO, 0, 6, false);
Chip_ADC_ClearFlags(LPC_ADC,0xFFFFFFFF);
}
void DMA_IRQHandler(void)
{
static uint16_t DMA_Sum=0;
DMA_Sum++;
if(DMA_Sum ==8)
{
DMA_Sum=4;
}
Chip_GPIO_SetPinState(LPC_GPIO, 0, 7,true);
/* Rrror interrupt on channel 0? */
if ((Chip_DMA_GetIntStatus(LPC_DMA) & DMA_INTSTAT_ACTIVEERRINT) != 0)
{
/* This shouldn't happen for this simple DMA example, so set the LED
to indicate an error occurred. This is the correct method to clear
an abort. */
Chip_DMA_DisableChannel(LPC_DMA, DMA_CH0);
while ((Chip_DMA_GetBusyChannels(LPC_DMA) & (1 << DMA_CH0)) != 0) {}
Chip_DMA_AbortChannel(LPC_DMA, DMA_CH0);
Chip_DMA_ClearErrorIntChannel(LPC_DMA, DMA_CH0);
Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
Board_LED_Set(0, true);
}
Chip_GPIO_SetPinState(LPC_GPIO, 0,7,false);
/* Clear DMA interrupt for the channel */
LPC_DMA->DMACOMMON[0].INTA = 1;
}
/***
* ____ __ __ _
* | _ \| \/ | / \
* | | | | |\/| | / _ \
* | |_| | | | |/ ___ \
* |____/|_| |_/_/ \_\
* / ___| ___| |_ _ _ _ __
* \___ \ / _ \ __| | | | '_ \
* ___) | __/ |_| |_| | |_) |
* |____/ \___|\__|\__,_| .__/
* |_|
*/
void DMA_Steup(void)
{
DMA_CHDESC_T Initial_DMA_Descriptor;
ADC_TransferDescriptors[0].source = (uint32_t)&LPC_ADC->SEQ_GDAT[0];
ADC_TransferDescriptors[1].source = (uint32_t)&LPC_ADC->SEQ_GDAT[0];
ADC_TransferDescriptors[2].source = (uint32_t)&LPC_ADC->SEQ_GDAT[0];
ADC_TransferDescriptors[3].source = (uint32_t)&LPC_ADC->SEQ_GDAT[0];
ADC_TransferDescriptors[0].dest = (uint32_t)&CapturedData[(0+1)*DMA_TRANSFER_SIZE-1];
ADC_TransferDescriptors[1].dest = (uint32_t)&CapturedData[(1+1)*DMA_TRANSFER_SIZE-1];
ADC_TransferDescriptors[2].dest = (uint32_t)&CapturedData[(2+1)*DMA_TRANSFER_SIZE-1];
ADC_TransferDescriptors[3].dest = (uint32_t)&CapturedData[(3+1)*DMA_TRANSFER_SIZE-1];
//The initial DMA desciptor is the same as the 1st transfer descriptor. It
//Will link into the 2nd of the main descriptors.
ADC_TransferDescriptors[0].next = (uint32_t)&ADC_TransferDescriptors[1];
ADC_TransferDescriptors[1].next = (uint32_t)&ADC_TransferDescriptors[2];
ADC_TransferDescriptors[2].next = (uint32_t)&ADC_TransferDescriptors[3];
//Link back to the 1st descriptor
ADC_TransferDescriptors[3].next = (uint32_t)&ADC_TransferDescriptors[0];
//For a test, stop the transfers here. The sine wave will look fine.
//ADC_TransferDescriptors[3].next = 0;
ADC_TransferDescriptors[0].xfercfg = (DMA_XFERCFG_CFGVALID |
DMA_XFERCFG_RELOAD |
DMA_XFERCFG_SETINTA |
DMA_XFERCFG_WIDTH_16 |
DMA_XFERCFG_SRCINC_0 |
DMA_XFERCFG_DSTINC_1 |
DMA_XFERCFG_XFERCOUNT(DMA_TRANSFER_SIZE));
ADC_TransferDescriptors[1].xfercfg = ADC_TransferDescriptors[0].xfercfg;
ADC_TransferDescriptors[2].xfercfg = ADC_TransferDescriptors[0].xfercfg;
ADC_TransferDescriptors[3].xfercfg = (DMA_XFERCFG_CFGVALID |
DMA_XFERCFG_RELOAD |
DMA_XFERCFG_SETINTA |
DMA_XFERCFG_WIDTH_16 |
DMA_XFERCFG_SRCINC_0 |
DMA_XFERCFG_DSTINC_1 |
DMA_XFERCFG_XFERCOUNT(DMA_TRANSFER_SIZE));
Initial_DMA_Descriptor.source = ADC_TransferDescriptors[0].source;
Initial_DMA_Descriptor.dest = ADC_TransferDescriptors[0].dest;
Initial_DMA_Descriptor.next = (uint32_t)&ADC_TransferDescriptors[1];
Initial_DMA_Descriptor.xfercfg = ADC_TransferDescriptors[0].xfercfg;
/* DMA initialization - enable DMA clocking and reset DMA if needed */
Chip_DMA_Init(LPC_DMA);
/* Enable DMA controller and use driver provided DMA table for current descriptors */
Chip_DMA_Enable(LPC_DMA);
Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));
/* Setup channel 0 for the following configuration:
- High channel priority
- Interrupt A fires on descriptor completion */
Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
Chip_DMA_EnableIntChannel(LPC_DMA, DMA_CH0);
Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_CH0, //(DMA_CFG_PERIPHREQEN |
(DMA_CFG_HWTRIGEN |
DMA_CFG_TRIGBURST_BURST |
DMA_CFG_TRIGTYPE_EDGE |
DMA_CFG_TRIGPOL_LOW | //DMA_CFG_TRIGPOL_HIGH
DMA_CFG_BURSTPOWER_1 |
DMA_CFG_CHPRIORITY(0)
)
);
//make sure ADC Sequence A interrupts is selected for for a DMA trigger
LPC_INMUX->DMA_ITRIG_INMUX[0] = 0;
/* Enable DMA interrupt */
NVIC_EnableIRQ(DMA_IRQn);
// The 1st descriptor is set up through the registers.
/* Setup transfer descriptor and validate it */
Chip_DMA_SetupTranChannel(LPC_DMA, DMA_CH0, &Initial_DMA_Descriptor);
//Use the transfer configuration for our 4 main descriptors
Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_CH0, ADC_TransferDescriptors[0].xfercfg);
Chip_DMA_SetValidChannel(LPC_DMA, DMA_CH0);
}
void SCT_PWM_Generate(void)
{
/* Initialize the SCT as PWM and set frequency */
Chip_SCTPWM_Init(SCT_PWM);
Chip_SCTPWM_SetRate(SCT_PWM, SCT_PWM_RATE);
/* Setup Board specific output pin */
Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 14, IOCON_FUNC3 | IOCON_MODE_INACT | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF);
/* Use SCT0_OUT7 pin */
Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_OUT, SCT_PWM_PIN_OUT);
/* Start with 50% duty cycle */
Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_OUT, Chip_SCTPWM_PercentageToTicks(SCT_PWM, 10));
Chip_SCTPWM_Start(SCT_PWM);
}
/***
* _ ____ ____
* / \ | _ \ / ___|
* / _ \ | | | | |
* / ___ \| |_| | |___
* /_/__ \_\____/ \____|
* / ___| ___| |_ _ _ _ __
* \___ \ / _ \ __| | | | '_ \
* ___) | __/ |_| |_| | |_) |
* |____/ \___|\__|\__,_| .__/
* |_|
*/
void ADC_Steup(void)
{
/*Set Asynch Clock to the Main clock*/
LPC_SYSCON->ADCCLKSEL = 0;
//Set the divider to 1 and enable. note, the HALT bit (30) and RESET (29) are not in the manual
LPC_SYSCON->ADCCLKDIV = 0;
/* Initialization ADC to 12 bit and set clock divide to 1 to operate synchronously at System clock */
Chip_ADC_Init(LPC_ADC, ADC_CR_RESOL(3) | ADC_CR_CLKDIV(0)| ADC_CR_ASYNC_MODE);
//select ADC Channel 1 as input
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 30, IOCON_FUNC0 | IOCON_ANALOG_EN| IOCON_INPFILT_OFF);
LPC_ADC->INSEL = 0x01;
Chip_ADC_SetupSequencer(LPC_ADC,ADC_SEQA_IDX,
ADC_SEQ_CTRL_SEQ_ENA |
ADC_SEQ_CTRL_CHANNEL_EN(ADC_INPUT_CHANNEL) |
ADC_SEQ_CTRL_TRIGGER(2) |
ADC_SEQ_CTRL_HWTRIG_POLPOS |
ADC_SEQ_CTRL_HWTRIG_SYNCBYPASS |
ADC_SEQ_CTRL_MODE_EOS |
ADC_SEQ_CTRL_SEQ_ENA);
/* Enable Sequence A interrupt */
Chip_ADC_EnableInt(LPC_ADC, ADC_INTEN_SEQA_ENABLE);
/* Calibrate ADC */
if(Chip_ADC_Calibration(LPC_ADC) == LPC_OK) {
/* Enable ADC SeqA Interrupt */
NVIC_EnableIRQ(ADC_SEQA_IRQn);
}
else {
DEBUGSTR("ADC Calibration Failed \r\n");
return ;
}
}
int main(void)
{
SystemCoreClockUpdate();
Board_Init();
DMA_Steup();
ADC_Steup();
SCT_PWM_Generate();
while(1)
{}
}
验证 构建项目,然后点击 进行调试; 生成正弦波:1 kHz,幅度:0~2 V,通过J9_1(P0_30-ADC1)将波形输入至ADC; 设置断点(图4),用于观察ADC转换值CapturedData[32]; 图4 4. 为了验证结果,我收集了多个数据组,并使用Excel将这些数据制成图表进行检查。图6是一个示例。 图5 图6 图7 图8 LPC54xxx LPCOpen 回复:DMA Ping-Pong 应用程序 您好@jeremyzhou, 我遵循您的实现并进行了修改,以更新LPC54102上SCT块的匹配寄存器。我正在向该寄存器输入正弦波的振幅值以生成脉宽调制(SPWM)。 程序有些正常运行,但我注意到程序忽略了查找表(LUT)中的一些值。您对如何解决这个问题有什么建议吗? 我将添加部分代码。 /*****************************************CODE************************************************/ #include "board.h" #define SCT_PWM LPC_SCT #define DMA_TRANSFER_SIZE 64 #define SCT_PWM_RATE 10000 /* PWM frequency 10 KHz */ #define SCT_PWM_PIN_OUT 4 /* COUT7 生成方波 */ #define SCT_PWM_OUT 1 /* OUT PWM 的索引 */ static CHIP_SYSCON_PLLCLKSRC_T curr_src=SYSCON_PLLCLKSRC_RTC; static volatile bool dmaDone = false;static int fmode; static int fmode; #define TABLE_SIZE 128 uint16_t sine_table[TABLE_SIZE] __attribute__((aligned(16))) = { 610, 610, 610, 610, 610, 610, 610, 610, 859, 859, 859, 859, 859, 859, 859, 859, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1191, 1191, 1191, 1191, 1191, 1191, 1191, 1191, 1217, 1217, 1217, 1217, 1217, 1217, 1217, 1217, 1139, 1139, 1139, 1139, 1139, 1139, 1139, 1139, 969, 969, 969, 969, 969, 969, 969, 969, 737, 737, 737, 737, 737, 737, 737, 737, 483, 483, 483, 483, 483, 483, 483, 483, 252, 252, 252, 252, 252, 252, 252, 252, 82, 82, 82, 82, 82, 82, 82, 82, 3, 3, 3, 3, 3, 3, 3, 3, 30, 30, 30, 30, 30, 30, 30, 30, 157, 157, 157, 157, 157, 157, 157, 157, 362, 362, 362, 362, 362, 362, 362, 362, 610, 610, 610, 610, 610, 610, 610, 610 }; ALIGN(512) DMA_CHDESC_T SCT_TransferDescriptors1,SCT_TransferDescriptors2; uint16_t CapturedData[32]; uint16_t DMA_Sum=0; void DMA_IRQHandler(void) { /* Rrror interrupt on channel 0? */ if ((Chip_DMA_GetIntStatus(LPC_DMA) & DMA_INTSTAT_ACTIVEERRINT) != 0) { /* This shouldn't happen for this simple DMA example, so set the LED to indicate an error occurred. 这是清除的正确方法 中止。*/ Chip_DMA_DisableChannel(LPC_DMA, DMA_CH0); while ((Chip_DMA_GetBusyChannels(LPC_DMA) & (1 << DMA_CH0)) != 0) {} Chip_DMA_AbortChannel(LPC_DMA, DMA_CH0); Chip_DMA_ClearErrorIntChannel(LPC_DMA, DMA_CH0); Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0); Board_LED_Set(0, true); } /* Clear DMA interrupt for the channel */ LPC_DMA->DMACOMMON[0].INTA = 1; } void DMA_Setup(void) { SCT_TransferDescriptors1.source = (uint32_t)(&sine_table[TABLE_SIZE-1]); SCT_TransferDescriptors1.dest = (uint32_t)(&(LPC_SCT->MATCHREL[1].U)); SCT_TransferDescriptors1.next = (uint32_t)(&SCT_TransferDescriptors2); SCT_TransferDescriptors1.xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_XFERCFG_WIDTH_16 | DMA_XFERCFG_SRCINC_1 | DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT((TABLE_SIZE)) | DMA_XFERCFG_RELOAD; // Configuración del segundo descriptor (igual que el primero, pero enlazando de regreso al primero) SCT_TransferDescriptors2.source = (uint32_t)(&sine_table[(TABLE_SIZE-1)]); SCT_TransferDescriptors2.dest = (uint32_t)(&(LPC_SCT->MATCHREL[1].U)); SCT_TransferDescriptors2.next = (uint32_t) (&SCT_TransferDescriptors1); SCT_TransferDescriptors2.xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_XFERCFG_WIDTH_16 | DMA_XFERCFG_SRCINC_1 | DMA_XFERCFG_DSTINC_0 | DMA_XFERCFG_XFERCOUNT((TABLE_SIZE)) | DMA_XFERCFG_RELOAD; /* DMA initialization - enable DMA clocking and reset DMA if needed */ Chip_DMA_Init(LPC_DMA); /* Enable DMA controller and use driver provided DMA table for current descriptors */ Chip_DMA_Enable(LPC_DMA); Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table)); /* Setup channel for the given configurations */ Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0); Chip_DMA_EnableIntChannel(LPC_DMA, DMA_CH0); Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_CH0, (DMA_CFG_HWTRIGEN | DMA_CFG_TRIGBURST_BURST | DMA_CFG_TRIGTYPE_LEVEL | DMA_CFG_TRIGPOL_LOW | DMA_CFG_CHPRIORITY(0))); /* Set ADC Sequence A interrupts for a DMA trigger */ LPC_INMUX->DMA_ITRIG_INMUX[0] = DMATRIG_SCT0_DMA0; /* Enable DMA interrupt */ NVIC_EnableIRQ(DMA_IRQn); /* Setup the initial descriptor */ Chip_DMA_SetupTranChannel(LPC_DMA, DMA_CH0, &SCT_TransferDescriptors1); /* Use the transfer configuration for our descriptors */ Chip_DMA_SetupChannelTransfer(LPC_DMA,DMA_CH0,SCT_TransferDescriptors1.xfercfg); Chip_DMA_SetValidChannel(LPC_DMA, DMA_CH0); } void SCT_PWM_Generate(void) { /* Initialize the SCT as PWM and set frequency */ Chip_SCTPWM_Init(SCT_PWM); Chip_SCTPWM_SetRate(SCT_PWM, SCT_PWM_RATE); /* Setup Board specific output pin */ Chip_IOCON_PinMuxSet(LPC_IOCON, 1, 1, IOCON_FUNC3 | IOCON_MODE_INACT | IOCON_DIGITAL_EN | IOCON_INPFILT_OFF); /* Use SCT0_OUT7 pin */ Chip_SCTPWM_SetOutPin(SCT_PWM, SCT_PWM_OUT, SCT_PWM_PIN_OUT); /* 从50%占空比开始 */ Chip_SCTPWM_SetDutyCycle(SCT_PWM, SCT_PWM_OUT, Chip_SCTPWM_PercentageToTicks(SCT_PWM, 10)); Chip_SCTPWM_Start(SCT_PWM); } int main(void) { SystemCoreClockUpdate(); Board_Init(); SystemCoreClockUpdate(); SCT_PWM_Generate(); DMA_Setup(); while(1) {} } 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
一个小问题。 该示例使用 SCT 触发 ADC。 对于我的代码,我使用了 CTIMER(match event)来触发 ADC 序列,这种方法似乎更简单,也更容易理解。 使用 SCT 有什么优势吗? 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
能否将描述符定义为常量,并在编译时进行设置,即放在闪存而不是RAM中? 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Hi dingelen, 感谢您的回复。 根据上述描述,我仍然不清楚“struck”问题的实际情况,因此建议您进行更多测试并提供更详细的信息。 祝您有美好的一天。 BR, Jeremy 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Hi Jeremy, 感谢您的快速回复。我认为使用LPC55也可以实现相同的功能,但我在这一部分遇到了一些困难: /* DMA initialization - enable DMA clocking and reset DMA if needed */ Chip_DMA_Init(LPC_DMA); /* Enable DMA controller and use driver provided DMA table for current descriptors */ Chip_DMA_Enable(LPC_DMA); Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table)); /* Setup channel 0 for the following configuration: - High channel priority - Interrupt A fires on descriptor completion */ Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0); Chip_DMA_EnableIntChannel(LPC_DMA, DMA_CH0); Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_CH0, //(DMA_CFG_PERIPHREQEN | (DMA_CFG_HWTRIGEN | DMA_CFG_TRIGBURST_BURST | DMA_CFG_TRIGTYPE_EDGE | DMA_CFG_TRIGPOL_LOW | //DMA_CFG_TRIGPOL_HIGH DMA_CFG_BURSTPOWER_1 | DMA_CFG_CHPRIORITY(0) ) ); //make sure ADC Sequence A interrupts is selected for for a DMA trigger LPC_INMUX->DMA_ITRIG_INMUX[0] = 0; /* Enable DMA interrupt */ NVIC_EnableIRQ(DMA_IRQn); // The 1st descriptor is set up through the registers. /* Setup transfer descriptor and validate it */ Chip_DMA_SetupTranChannel(LPC_DMA, DMA_CH0, &Initial_DMA_Descriptor); //Use the transfer configuration for our 4 main descriptors Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_CH0, ADC_TransferDescriptors[0].xfercfg); Chip_DMA_SetValidChannel(LPC_DMA, DMA_CH0) 所有这些功能似乎在LPC55 SDK中都不可用。SDK 中的示例也仅使用某种轮询式启动/停止方式,而非自动运行的设置。(请参见 lpcxpresso55s69_lpadc_dma 示例) 如果这最后一部分能够被移植,那就太好了。 谢谢! Tom 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Hi dingelen, 感谢您对演示的兴趣。 1) 是否有适用于 LPC55 系列的相同示例? --在审查了RM后,我发现LPC55xx系列也支持Ping-Pong功能,因此该示例可以移植到该系列。 祝您有美好的一天。 BR, Jeremy 回复:DMA Ping-Pong 应用程序 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
超级示例。比SDK里的好多了... 谢谢! LPC55系列是否有相同的示例可用?与LPC54系列相比,DMA的设置方式似乎有一些不同。
查看全文