GrumpyOldPizza / arduino-STM32L4

69 stars 60 forks source link

Implementing DMA for DAC #50

Open jacky4566 opened 4 years ago

jacky4566 commented 4 years ago

So it seems that calls to the DAC take quite a bit of time so I would like to implement DMA transfers.

I tried my hand to use your DMA library but I cant seem to get it to fire. My code will complile but nothing happens :/

DMA feeding DAC code:

#include "stm32l4_dma.h"

stm32l4_dma_t stm32l4_DAC_DMA;

static int DACresolution = 12;

uint32_t i;

uint32_t DAC_DMA_OPTION = DMA_OPTION_MEMORY_TO_PERIPHERAL |
                          DMA_OPTION_PERIPHERAL_DATA_SIZE_32 |
                          DMA_OPTION_MEMORY_DATA_SIZE_32 |
                          DMA_OPTION_MEMORY_DATA_INCREMENT |
                          DMA_OPTION_PRIORITY_MEDIUM ;

void setup() {
  Serial.begin(9600);
  while (!Serial) {}
  analogWriteResolution(DACresolution);
  analogWrite(PIN_DAC0, i);
  analogWrite(PIN_DAC1, i);

  DAC->CR = (uint32_t) DAC->CR | 0x3000; //enable DAC DMA and underrun

  stm32l4_dma_create(&stm32l4_DAC_DMA, DMA_CHANNEL_DMA1_CH3_DAC1, DMA_OPTION_PRIORITY_LOW);
  stm32l4_dma_enable(&stm32l4_DAC_DMA, NULL, NULL);
}

void loop() {
  i = (i + 64) & DAC_DHR12R1_DACC1DHR;

  stm32l4_dma_start(&stm32l4_DAC_DMA, (uint32_t)&DAC->DHR12R1,(uint32_t)i, 1, DAC_DMA_OPTION);    //This should trigger a DMA trandfer to the DAC->DHR12R1 register.

}
GrumpyOldPizza commented 4 years ago

Almost there ;-)

First off, you should be using circular DMA instead of oneshot. Use the half-done / full-done to implement a simple double buffered scheme.

In DAC->CR, you need to enable an external trigger (no underrun or such). The external trigger (TSEL) should be set to TIM7_TRIG. TIM7 is used for Tone()/noTone(), so it seems appropriate to use that one.

You can look at the stm32l4_wiring_tone.c code to see how it's kind of used, except you don't need to interrupt callback.

jacky4566 commented 4 years ago

Thanks for the help GrumpyOldPizza. Still a bit stuck here. I was trying one shot mode just to get things working but circular mode also doesn't seem to be helping.

I have implement the Timer7 at 1MHZ and enabled the appropriate TSEL and TEN1. The DAC appears to be working off that source now. Plugging values into the second channel (DHR12R2) transfer correctly.

With a circular DMA how does it know when to do transfers? Do i need to point the Timer 7 to the DMA as well?

Here is the code so far.

#include "stm32l4_dma.h"
#include "stm32l4_timer.h"

stm32l4_dma_t stm32l4_DAC_DMA;
stm32l4_timer_t stm32l4_DAC_timer;

static int DACresolution = 12;

uint32_t desiredValue;

uint32_t DAC_DMA_OPTION = DMA_OPTION_MEMORY_TO_PERIPHERAL |
                          DMA_OPTION_CIRCULAR |
                          DMA_OPTION_PERIPHERAL_DATA_SIZE_32 |
                          DMA_OPTION_MEMORY_DATA_SIZE_32 |
                          DMA_OPTION_PRIORITY_MEDIUM ;

void setup() {
  Serial.begin(9600);
  analogWriteResolution(DACresolution);
  analogWrite(PIN_DAC0, 0); //use board manager to setup dac
  analogWrite(PIN_DAC1, 0); //use board manager to setup dac

  //Setup timer 7 to trigger arbitraty 1MHz (@80MHZ)
  stm32l4_timer_create(&stm32l4_DAC_timer, TIMER_INSTANCE_TIM7, NULL, 0);
  stm32l4_timer_enable(&stm32l4_DAC_timer, (stm32l4_timer_clock(&stm32l4_DAC_timer) / 4000000) - 1, 4 - 1, TIMER_OPTION_COUNT_PRELOAD, NULL, NULL, 0);

  //Setup DMA to transfer into DAC
  stm32l4_dma_create(&stm32l4_DAC_DMA, DMA_CHANNEL_DMA1_CH3_DAC1, DMA_OPTION_PRIORITY_MEDIUM);
  stm32l4_dma_enable(&stm32l4_DAC_DMA, NULL, NULL);
  stm32l4_dma_start(&stm32l4_DAC_DMA, (uint32_t)&DAC->DHR12R1, (uint32_t)desiredValue, 1, DAC_DMA_OPTION);

  //enable DAC DMA, Trigger on TIM7, Timer trigger EN
  DAC->CR = (uint32_t) DAC->CR | 0x1014;

  //start timer
  stm32l4_timer_start(&stm32l4_DAC_timer, false);
}

void loop() {
  desiredValue = desiredValue + 64;
  DAC->DHR12R2 = desiredValue; //Verify operation on channel 2
  delay(100); //keep things slow
}