Hi There,
I'm trying to create a function generator using the DMA mem-to-peripheral transfer controller. I wrote some code to test the DMA to DAC functionality and it worked perfectly, producing a lovely and clean sine wave:
#include "lpc_types.h"
#include "lpc17xx_gpdma.h"
#include "lpc17xx_dac.h"
#include <math.h>
#include "dac.h"
#include "../../lib/serial.h"
#define NUMBER_OF_SAMPLES 64
#define PI (float)(3.141592654)
#define DAC_CLK_MHZ 25000000
#define SIGNAL_FREQ 60
int main () {
int32_t sinewave[NUMBER_OF_SAMPLES];
GPDMA_LLI_Type LLIO;
serial_init();
serial_printf("Starting Stage2\r\n");
serial_printf("Preparing sinewave \r\n");
int i;
for (i = 0; i < NUMBER_OF_SAMPLES; i++) {
sinewave[i] = 512 * sin(2*PI*i/NUMBER_OF_SAMPLES);
sinewave[i] = ((sinewave[i] + 512)
<< 6)
| 1 << 16;
}
dac_pincfg();
LLIO.SrcAddr = (uint32_t) sinewave;
LLIO.DstAddr = (uint32_t) &(LPC_DAC->DACR);
LLIO.NextLLI = (uint32_t) &LLIO;
LLIO.Control = NUMBER_OF_SAMPLES
| 2 << 18
| 2 << 21
| 1 << 26;
GPDMA_Channel_CFG_Type dma_cfg;
dma_cfg.ChannelNum = 0;
dma_cfg.SrcMemAddr = (uint32_t) sinewave;
dma_cfg.DstMemAddr = (uint32_t) &(LPC_DAC->DACR);
dma_cfg.DMALLI = (uint32_t) &LLIO;
dma_cfg.TransferSize = NUMBER_OF_SAMPLES;
dma_cfg.TransferType = GPDMA_TRANSFERTYPE_M2P;
dma_cfg.SrcConn = 0;
dma_cfg.DstConn = GPDMA_CONN_DAC;
serial_printf("Config gpdma\r\n");
GPDMA_Init();
GPDMA_Setup(&dma_cfg);
uint32_t time_out = DAC_CLK_MHZ / (SIGNAL_FREQ * NUMBER_OF_SAMPLES);
DAC_CONVERTER_CFG_Type dac_cfg;
dac_cfg.CNT_ENA = SET;
dac_cfg.DMA_ENA = SET;
serial_printf("Config DAC\r\n");
DAC_Init(LPC_DAC);
DAC_SetDMATimeOut(LPC_DAC, time_out);
DAC_ConfigDAConverterControl(LPC_DAC, &dac_cfg);
serial_printf("Starting GPDMA transfer\r\n");
GPDMA_ChannelCmd(0, ENABLE);
while(1);
}
The next logical step was to extract the DMA initialisation into a function, which accepts a pointer to the data to pass. Using the exact same configuration options, instead passing them through a function I got nothing on the analogue output. I am generating the sine LUT in main still. I had a feeling it would be due to some weird scope issues, because when I moved the LUT generating code into the GPDMA function I got a sinewave on the out, albeit a very noisy one. So I changed the GPDMA lib to take a function that generates the LUT:
#include "lpc_types.h"
#include "lpc17xx_gpdma.h"
#include "lpc17xx_dac.h"
#include <math.h>
#include "gpdma_dac.h"
void gpdma_dac_func_config(uint32_t (*func)(uint32_t), uint32_t size, uint32_t freq, uint32_t repeat) {
uint32_t data[size];
uint32_t i;
for (i = 0; i < size; i++) {
data[i] = func(i);
}
GPDMA_LLI_Type dma_lli;
dma_lli.SrcAddr = (uint32_t) data;
dma_lli.DstAddr = (uint32_t) &(LPC_DAC->DACR);
dma_lli.NextLLI = (uint32_t) (repeat ? &dma_lli : 0);
dma_lli.Control = size
| 2 << 18
| 2 << 21
| 1 << 26;
GPDMA_Channel_CFG_Type dma_cfg;
dma_cfg.ChannelNum = 0;
dma_cfg.SrcMemAddr = (uint32_t) data;
dma_cfg.DstMemAddr = (uint32_t) &(LPC_DAC->DACR);
dma_cfg.DMALLI = (uint32_t) &dma_lli;
dma_cfg.TransferSize = size;
dma_cfg.TransferType = GPDMA_TRANSFERTYPE_M2P;
dma_cfg.SrcConn = 0;
dma_cfg.DstConn = GPDMA_CONN_DAC;
GPDMA_Init();
GPDMA_Setup(&dma_cfg);
DAC_CONVERTER_CFG_Type dac_cfg;
dac_cfg.CNT_ENA = SET;
dac_cfg.DMA_ENA = SET;
uint32_t timeout = DAC_CLK_MHZ / (freq * size);
DAC_SetDMATimeOut(LPC_DAC, timeout);
DAC_ConfigDAConverterControl(LPC_DAC, &dac_cfg);
}
void gpdma_dac_start() {
GPDMA_ChannelCmd(0, ENABLE);
}
void gpdma_dac_stop() {
GPDMA_ChannelCmd(0, DISABLE);
}
#include "lpc_types.h"
#include "lpc17xx_gpdma.h"
#include "lpc17xx_dac.h"
#include <math.h>
#include "gpdma_dac.h"
#include "../../lib/serial.h"
#define NUMBER_OF_SAMPLES 64
#define PI (float)(3.141592654)
#define DAC_CLK_MHZ 25000000
#define SIGNAL_FREQ 60
uint32_t sinegen(uint32_t i) {
uint32_t out = 512 * sin(2*PI*i/NUMBER_OF_SAMPLES);
out = ((out + 512)
<< 6)
| 1 << 16;
return out;
}
int main () {
serial_init();
dac_init();
serial_printf("Starting Stage2\r\n");
serial_printf("Configuring gpdma\r\n");
gpdma_dac_func_config(sinegen, NUMBER_OF_SAMPLES, SIGNAL_FREQ, 1);
serial_printf("Starting dma transer\r\n");
gpdma_dac_start();
while(1);
}
This produces the same noisy sine wave before. As you can see it looks like only parts of the memory are being transferred properly. Is this something to do with function scope or memory that I don't understand. Should I be initialising the LUT somewhere else? Bare in mind that when I just passed a pointer to the LUT (genned in main) to gpdma_dac_config(), the output was a constant zero. Maybe it is optimised out?
I'm at a real loss with this one. I'm not really sure how to debug it either. Can you get a memory dump, so I can verify the integrity of the LUT?
Any help would be much appreciated!
Many thanks,
Rory