Using DMA to vary PWM duty cycle

Development environment specific, Arduino, Eclipse, VS2013, Em::Blocks etc
Post Reply
nr4ps
Posts: 9
Joined: Sun Oct 30, 2022 7:20 pm

Using DMA to vary PWM duty cycle

Post by nr4ps »

Hello,

I am trying to understand how to use DMA to vary PWM duty cycle so that I could put it through a low-pass filter to create a sine wave. I am using the rogerclarkmelbourne core. I tried doing it with dma_setup_transfer (commented out) and with dma_tube_config (which is evidently the preferred way of doing DMA now), but I'm not understanding how they're supposed to work. In either case I get a compiler error on the DMA_CCR_CIRC | DMA_CCR_MINC portion:

Compilation error: invalid conversion from 'const void*' to 'volatile void*' [-fpermissive]

Can anyone tell me what I'm doing wrong?

Code: Select all

#include "libmaple/dma.h"
#define filteredPWMpin PB0

// sinewave = round(512/2*(sin(dt:dt:2*pi)+1))
#define SINSIZE 64
const uint16_t Sinewave[SINSIZE] = 
{  281,  306,  330,  354,  377,  398,  418,  437,  454,  469,  482,  493,  501,  507,  511,  512,
   511,  507,  501,  493,  482,  469,  454,  437,  418,  398,  377,  354,  330,  306,  281,  256,
   231,  206,  182,  158,  135,  114,   94,   75,   58,   43,   30,   19,   11,    5,    1,    0,
     1,    5,   11,   19,   30,   43,   58,   75,   94,  114,  135,  158,  182,  206,  231,  256 };

#include <libmaple/timer.h>
timer_dev *timerdev;
uint8 cc_channel;
void pwmSetup (uint8 pin, uint16 duty_cycle) {
    if (pin >= BOARD_NR_GPIO_PINS) {
        return;
    }
    timerdev = PIN_MAP[pin].timer_device;
    cc_channel = PIN_MAP[pin].timer_channel;
    ASSERT(timerdev && cc_channel);
    timer_set_reload(timerdev, 512);
    timer_set_compare(timerdev, cc_channel, duty_cycle);
    dma_init(DMA1);
    __IO uint32 *ccr = &(timerdev->regs).gen->CCR1 + (cc_channel - 1);
    // dma_setup_transfer is deprecated in favor of dma_tube_config
    /*
    dma_setup_transfer( DMA1, DMA_CH1, Sinewave, DMA_SIZE_16BITS,
       ccr, 
       DMA_SIZE_16BITS,
       DMA_CCR_CIRC | DMA_CCR_MINC );
    dma_set_num_transfers(DMA1, DMA_CH1, SINSIZE);
    dma_enable(DMA1, DMA_CH1);
    */
    struct dma_tube_config myconfig;
    myconfig.tube_dst = ccr;
    myconfig.tube_dst_size = DMA_SIZE_16BITS;
    myconfig.tube_src = Sinewave;
    myconfig.tube_src_size = DMA_SIZE_16BITS;
    myconfig.tube_nr_xfers = SINSIZE;
    myconfig.tube_flags = DMA_CCR_CIRC | DMA_CCR_MINC;
    myconfig.tube_req_src = DMA_REQ_SRC_TIM4_CH2; //?
}

void setup() {
  // put your setup code here, to run once:
  pinMode(filteredPWMpin, PWM);
  pwmSetup(filteredPWMpin, 1);
}

void loop() {
  // put your main code here, to run repeatedly:

}
User avatar
Bakisha
Posts: 143
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Using DMA to vary PWM duty cycle

Post by Bakisha »

Replace

Code: Select all

const uint16_t Sinewave[SINSIZE] = 
with

Code: Select all

uint16_t Sinewave[SINSIZE] = 
I can't say will it work (i'm not familiar with dma functions in rogers core), but at least it will compile.
nr4ps
Posts: 9
Joined: Sun Oct 30, 2022 7:20 pm

Re: Using DMA to vary PWM duty cycle

Post by nr4ps »

Thank you! I must've misread which line was producing the error!
nr4ps
Posts: 9
Joined: Sun Oct 30, 2022 7:20 pm

Re: Using DMA to vary PWM duty cycle

Post by nr4ps »

Okay, so, I'm still struggling with this one. I have an example I found online that does this on pin PB7 but I'd really like to do this on PB0 if I can - or at least to understand why if I can't.

Using PB7, it works:

Code: Select all

#define SAMPLES 100
#include <libmaple/dma.h>
dma_tube_config dma_cfg ;

//int flag1 = 0;

int out1 = PB7;

int val1[SAMPLES];

int16 shift = 0;

int amp = 35;
int cnt = 0;
int time_track = 0;
float stp = 6.2831 / SAMPLES;
//120 deg = stp/3
int ret = 17;

timer_dev *dev1 = PIN_MAP[out1].timer_device;
uint8 cc_channel1 = PIN_MAP[out1].timer_channel;

/*
void fun()
{
  flag1++;
}*/

void timer_conf()
{
  timer_dma_set_base_addr(dev1, TIMER_DMA_BASE_CCR2);
  timer_dma_set_burst_len(dev1, 1);
  timer_dma_enable_req(dev1, cc_channel1);
  timer_set_reload(dev1, 102);
  timer_set_prescaler(dev1, 0);
}

void dma_conf()
{
  dma_init(DMA1);
  /* T4C2 DMA C4 */
  dma_cfg.tube_dst = &(dev1->regs.gen->DMAR);
  dma_cfg.tube_dst_size = DMA_SIZE_32BITS;
  dma_cfg.tube_src = val1;
  dma_cfg.tube_src_size = DMA_SIZE_32BITS;

  dma_cfg.tube_nr_xfers = SAMPLES;
  dma_cfg.tube_flags = DMA_CFG_SRC_INC | DMA_CFG_CIRC; // | DMA_CFG_CMPLT_IE;

  dma_cfg.tube_req_src = DMA_REQ_SRC_TIM4_CH2;
  dma_cfg.target_data = 0;

  ret = dma_tube_cfg(DMA1, DMA_CH4, &dma_cfg);
}

void dma_start()
{
  //dma_attach_interrupt(DMA1, DMA_CH4, fun);

  dma_enable(DMA1, DMA_CH4);
  timer_resume(dev1); 
}

void init_wave()
{
  int i;
  for (i = 0; i < SAMPLES; i++)
  {
    val1[i] = 50 + amp * sin(stp * i);
  }
}


void setup() {
  int i;
  pinMode(out1, PWM);

  Serial.begin(9600);

  timer_conf();
  dma_conf();
  dma_start();

  init_wave();

}

void loop() {

}

Trying to adapt it for PB0 below. From pinout diagrams online, it seemed that I should change DMA_REQ_SRC_TIM4_CH2 to TIM3_CH3 due to this pin choice. Table 78 of the datasheet shows this corresponding to DMA channel 2 (instead of 4 for PB7). I also used TIMER_DMA_BASE_CCR2 to CCR3 because I take it we're supposed to use the CCR number that matches the timer channel number. But still no PWM is happening on PB0. Am I missing something? Some kind of conflict with TIM3_CH3 or with DMA tube 2?

Code: Select all

#define SAMPLES 100
#include <libmaple/dma.h>
dma_tube_config dma_cfg ;

//int flag1 = 0;

int out1 = PB0;

int val1[SAMPLES];

int16 shift = 0;

int amp = 35;
int cnt = 0;
int time_track = 0;
float stp = 6.2831 / SAMPLES;
//120 deg = stp/3
int ret = 17;

timer_dev *dev1 = PIN_MAP[out1].timer_device;
uint8 cc_channel1 = PIN_MAP[out1].timer_channel;

/*
void fun()
{
  flag1++;
}*/

void timer_conf()
{
  timer_dma_set_base_addr(dev1, TIMER_DMA_BASE_CCR3); // I think we use the CCR corresponding to the timer channel number. (Used when read/write access are done through the TIMx_DMAR address)
  timer_dma_set_burst_len(dev1, 1);
  timer_dma_enable_req(dev1, cc_channel1);
  timer_set_reload(dev1, 102);
  timer_set_prescaler(dev1, 0);
}

void dma_conf()
{
  dma_init(DMA1);
  /* T4C2 DMA C4 */
  dma_cfg.tube_dst = &(dev1->regs.gen->DMAR);
  dma_cfg.tube_dst_size = DMA_SIZE_32BITS;
  dma_cfg.tube_src = val1;
  dma_cfg.tube_src_size = DMA_SIZE_32BITS;

  dma_cfg.tube_nr_xfers = SAMPLES;
  dma_cfg.tube_flags = DMA_CFG_SRC_INC | DMA_CFG_CIRC; // | DMA_CFG_CMPLT_IE;

  dma_cfg.tube_req_src = DMA_REQ_SRC_TIM3_CH3;
  dma_cfg.target_data = 0;

  ret = dma_tube_cfg(DMA1, DMA_CH2, &dma_cfg); // Table 78
}

void dma_start()
{
  //dma_attach_interrupt(DMA1, DMA_CH4, fun);

  dma_enable(DMA1, DMA_CH2);
  timer_resume(dev1); 
}

void init_wave()
{
  int i;
  for (i = 0; i < SAMPLES; i++)
  {
    val1[i] = 50 + amp * sin(stp * i);
  }
}


void setup() {
  int i;
  pinMode(out1, PWM);

  Serial.begin(9600);

  timer_conf();
  dma_conf();
  dma_start();

  init_wave();

}

void loop() {

}
Post Reply

Return to “IDE's”