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

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

656 Views
rwsa500
Contributor I

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?IMG_20191118_154642186.jpg

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

Labels (2)
Tags (3)
0 Kudos
Reply
1 Reply

550 Views
rwsa500
Contributor I

Okay so initialising the variables from within the function seemed to be an issue. 

void gpdma_config(uint32_t* source, uint16_t size, GPDMA_LLI_Type* LLI, GPDMA_Channel_CFG_Type* dma_cfg) {
  LLI->SrcAddr = (uint32_t) source;
  LLI->DstAddr = (uint32_t) &(LPC_DAC->DACR); // copy to dacr
  LLI->NextLLI = (uint32_t) LLI; // point to itself so it transfers infinitely
  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

  dma_cfg->ChannelNum = 0;
  dma_cfg->SrcMemAddr = (uint32_t) source;
  dma_cfg->DstMemAddr = (uint32_t) &(LPC_DAC->DACR);
  dma_cfg->DMALLI = (uint32_t) 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);
}

void gpdma_dac_config (uint16_t size, uint32_t transfer_freq) {
  uint32_t time_out = DAC_CLK_MHZ / (transfer_freq * size);
  DAC_CONVERTER_CFG_Type dac_cfg;
  dac_cfg.CNT_ENA = SET;
  dac_cfg.DMA_ENA = SET;

  dac_init();
  DAC_SetDMATimeOut(LPC_DAC, time_out);
  DAC_ConfigDAConverterControl(LPC_DAC, &dac_cfg);
}

void gpdma_dac_start(uint8_t ch) {
  GPDMA_ChannelCmd(ch, ENABLE);
}

This works fine, but you have to declare your dma cfg structs in the main, which feels messy. I would really like to know why this is the case. As far as I understand function scopes there really shouldn't be an issue here. 

0 Kudos
Reply