AnsweredAssumed Answered

Weird DMA memory issues after extracting code into a function (LPC17xx)

Question asked by Rory Anderson on Nov 18, 2019
Latest reply on Nov 19, 2019 by Rory Anderson

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");
// prepare sine wave
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) // offset by 512 (half of DAC val)
<< 6) // DACR bit value is 6-15
| 1 << 16; // Set DACR bias bit (bit 16)
}

dac_pincfg();

LLIO.SrcAddr = (uint32_t) sinewave;
LLIO.DstAddr = (uint32_t) &(LPC_DAC->DACR); // copy to dacr
LLIO.NextLLI = (uint32_t) &LLIO; // point to itself so it transfers infinitely
LLIO.Control = NUMBER_OF_SAMPLES // dma transfer size bits [bits 0-11]
| 2 << 18 // source w1000000idth 32bit
| 2 << 21 // dest width 32bit
| 1 << 26; // increment source by one after each transfer


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; // mem to peripheral
dma_cfg.SrcConn = 0;
dma_cfg.DstConn = GPDMA_CONN_DAC; // use the dac timer

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:

// gpdma_dac.c

#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); // copy to dacr
dma_lli.NextLLI = (uint32_t) (repeat ? &dma_lli : 0); // point to itself so it transfers infinitely
dma_lli.Control = size // dma transfer size bits [bits 0-11]
| 2 << 18 // source width 32bit
| 2 << 21 // dest width 32bit
| 1 << 26; // increment source by one after each transfer

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; // mem to peripheral
dma_cfg.SrcConn = 0;
dma_cfg.DstConn = GPDMA_CONN_DAC; // use the dac timer

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);
}
// main.c

#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) // offset by 512 (half of DAC val)
<< 6) // DACR bit value is 6-15
| 1 << 16; // Set DACR bias bit (bit 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?output of the noisy function gen

 

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

Outcomes