cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 - DMA + DAC - How to program ?

TMA1of3
Associate III

dear ST,

I'm trying to program the STM32H743 to use DMA control of its DAC - my understanding is this involves use of a Timer also.  

'Circular' mode would be relevant meaning the file being output (just a sine wave for now) is continually replayed. Memory to peripheral is I understand the correct mode for this application. And the Timer's 'update event' output is I think supposed to be used as the trigger output.

Initially I tried to implement this through MXCube without success. Next I found an example code kindly posted by someone although this was for the STM32G071, but still I thought it could be modified to suit the H743 as they seem to have similar peripherals. While the modified code runs it does not produce any output on the PA4 pin (due no doubt to my invalid modifications), but can you help me to understand the mistakes, please ?

I see the G071 datasheet makes no mention of 'BDMA', unlike the H743, but I'm unclear as to how the BDMA relates to the DMA in the H743. I think everything would be explained in the datasheet, but it's not a simple piece of information hence my request for your help in explaining, please.

Below is the code:

// Enable peripherals: GPIOA, DMA, DAC, TIM6.

 RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN;       // enables GPIOA

 RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;       // enables DMA1 clock source, DMAmux1 clock enabled ?

 RCC->APB1LENR |= RCC_APB1LENR_DAC12EN;      // enables DAC1 (and DAC2) peripheral clock

 RCC->APB1LENR |= RCC_APB1LENR_TIM6EN;       // enables TIM6 clock

 // Pin A4 output type: Analog.

 GPIOA->MODER  &= ~( 0x3 << ( 4 * 2 ) );

 GPIOA->MODER  |= ( 0x3 << ( 4 * 2 ) );

 // DMA configuration (channel 1).

 // CCR register:

 // - Memory-to-peripheral

 // - Circular mode enabled.

 // - Increment memory ptr, don't increment periph ptr.

 // - 16-bit data size for both source and destination.

 // - High priority.

 uint32_t dma_ccr_clr = ~( BDMA_CCR_MEM2MEM |

              BDMA_CCR_PL |

              BDMA_CCR_MSIZE |

              BDMA_CCR_PSIZE |

              BDMA_CCR_PINC |

              BDMA_CCR_EN );

 uint32_t dma_ccr_set = ( ( 0x2 << BDMA_CCR_PL_Pos ) |

              ( 0x1 << BDMA_CCR_MSIZE_Pos ) |

              ( 0x1 << BDMA_CCR_PSIZE_Pos ) |

              BDMA_CCR_MINC |

              BDMA_CCR_CIRC |

              BDMA_CCR_DIR );

 BDMA_Channel1->CCR &= dma_ccr_clr;

 BDMA_Channel1->CCR |= dma_ccr_set;

 // Select DAC Ch1 as DMA Ch1 request source in DMAMUX.

 // Note: DMAMUX channel numbers are slightly confusing in

 // the documentation. They aren't reliably 0- or 1-indexed.

 DMAMUX1_Channel0->CCR &= ~( DMAMUX_CxCR_DMAREQ_ID );

 DMAMUX1_Channel0->CCR |= ( 0x8 << DMAMUX_CxCR_DMAREQ_ID_Pos );

 // Set DMA source and destination addresses.

 // Source: Address of the sine wave buffer in memory.

 BDMA_Channel1->CM1AR = ( uint32_t )&SINE_WAVE;

 // Dest.: DAC1 Ch1 '12-bit right-aligned data' register.

 BDMA_Channel1->CPAR = ( uint32_t )&( DAC1->DHR12R1 );

 // Set DMA data transfer length (# of sine wave samples).

 BDMA_Channel1->CNDTR = ( uint16_t )SINE_SAMPLES;

 // Enable DMA1 Channels 1/2.

 BDMA_Channel1->CCR |= ( BDMA_CCR_EN );

 // TIM6 configuration.

 // Set prescaler and autoreload for a 440Hz sine wave.

 TIM6->PSC = ( 0x0000 );

 TIM6->ARR = ( SystemCoreClock / ( 440 * SINE_SAMPLES ) );

 // Enable trigger output on timer update events.

 TIM6->CR2 &= ~( TIM_CR2_MMS );

 TIM6->CR2 |= ( 0x2 << TIM_CR2_MMS_Pos );

    

 // Start the timer.

 TIM6->CR1 |= ( TIM_CR1_CEN );

  

 // DAC configuration.

 // Set trigger source to TIM6 TRGO.

 DAC1->CR &= ~( DAC_CR_TSEL1 );

 DAC1->CR |= ( 0x5 << DAC_CR_TSEL1_Pos );

  

 // Set outputs to buffered GPIO 'normal mode'.

 DAC1->MCR &= ~( DAC_MCR_MODE1 );

 // Enable DAC DMA requests.

 DAC1->CR |= ( DAC_CR_DMAEN1 );

  

 // Enable DAC Channels.

 DAC1->CR |= ( DAC_CR_EN1 );

  

 // Enable DAC channel trigger.

 DAC1->CR |= ( DAC_CR_TEN1 );

6 REPLIES 6
FBL
ST Employee

Hello @Community member​ 

You can start with this example H743 using DAC peripheral to generate two signals based on the DMA

controller.STM32Cube\Repository\STM32Cube_FW_H7_V1.10.0\Projects\NUCLEO-H743ZI\Examples\DAC\DAC_SignalsGeneration

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

TMA1of3
Associate III

Thank-you F.Belaid.

I tried to implement something based on that code example but was only partially successful – there was a sound emanating from PA4 albeit not a sinewave (heard through the speaker), but also an associated hard fault.

In main() I execute the following initialisation once:

void main_dac6_init(void)

{

 /*##-1- Configure the DAC peripheral #######################################*/

 DacHandle.Instance = DACx;

 TIMER6_Config();

 HAL_DAC1_MspInit(&DacHandle);

}

This function calls other functions:

void TIMER6_Config(void)

{

 static TIM_HandleTypeDef htim;

 TIM_MasterConfigTypeDef  sMasterConfig;

 /*##-1- Configure the TIM peripheral #######################################*/

 /* Time base configuration */

 htim.Instance = TIM6;

 htim.Init.Period           = 0x7FF;

  htim.Init.Prescaler        = 0;

 htim.Init.ClockDivision    = 0;

 htim.Init.CounterMode      = TIM_COUNTERMODE_UP;

 htim.Init.RepetitionCounter = 0;

 HAL_TIM_Base_Init(&htim);

 /* TIM6 TRGO selection */

 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

 HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);

 /*##-2- Enable TIM peripheral counter ######################################*/

 HAL_TIM_Base_Start(&htim);

}

void HAL_DAC1_MspInit(DAC_HandleTypeDef *hdac)

{

 GPIO_InitTypeDef         GPIO_InitStruct;

 static DMA_HandleTypeDef hdma_dac1;

 /*##-1- Enable peripherals and GPIO Clocks #################################*/

 /* Enable GPIO clock ****************************************/

 DACx_CHANNEL_GPIO_CLK_ENABLE();

 /* DAC Periph clock enable */

 DACx_CLK_ENABLE();

 /* DMA1 clock enable */

 DMAx_CLK_ENABLE();

 /*##-2- Configure peripheral GPIO ##########################################*/

 /* DAC Channel1 GPIO pin configuration */

 GPIO_InitStruct.Pin = DACx_CHANNEL_PIN;

 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

 GPIO_InitStruct.Pull = GPIO_NOPULL;

 HAL_GPIO_Init(DACx_CHANNEL_GPIO_PORT, &GPIO_InitStruct);

 /*##-3- Configure the DMA ##########################################*/

 /* Set the parameters to be configured for DACx_DMA_STREAM */

 hdma_dac1.Instance = DACx_DMA_INSTANCE;

 hdma_dac1.Init.Request = DMA_REQUEST_DAC1;

 hdma_dac1.Init.Direction = DMA_MEMORY_TO_PERIPH;

 hdma_dac1.Init.PeriphInc = DMA_PINC_DISABLE;

 hdma_dac1.Init.MemInc = DMA_MINC_ENABLE;

 hdma_dac1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

 hdma_dac1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

 hdma_dac1.Init.Mode = DMA_CIRCULAR;

 hdma_dac1.Init.Priority = DMA_PRIORITY_HIGH;

 HAL_DMA_Init(&hdma_dac1);

 /* Associate the initialized DMA handle to the DAC handle */

 __HAL_LINKDMA(hdac, DMA_Handle1, hdma_dac1);

 /*##-4- Configure the NVIC for DMA #########################################*/

 /* Enable the DMA1_Stream5 IRQ Channel */

 HAL_NVIC_SetPriority(DACx_DMA_IRQn, 2, 0);

 HAL_NVIC_EnableIRQ(DACx_DMA_IRQn);

}

When an external pushbutton is activated the following function is called which is supposed to start DAC conversions under DMA control in association with the Timer (is my understanding):

void Test_DAC6_Start()

{

 /* TIM6 Periph clock enable */

 __HAL_RCC_TIM6_CLK_ENABLE();

 DAC_Ch1_SineWaveConfig();

}

And the above function calls the following:

static void DAC_Ch1_SineWaveConfig(void)

{

 /*##-1- Initialize the DAC peripheral ######################################*/

 if (HAL_DAC_Init(&DacHandle) != HAL_OK)

 {

   /* Initialization Error */

   Error_Handler2();

 }

 /*##-1- DAC channel1 Configuration #########################################*/

 sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;

 sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

 if (HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DACx_CHANNEL) != HAL_OK)

 {

   /* Channel configuration Error */

   Error_Handler2();

 }

 /*##-2- Enable DAC selected channel and associated DMA #############################*/

 if (HAL_DAC_Start_DMA(&DacHandle, DACx_CHANNEL, (uint32_t *)SINE_WAVE, SINE_SAMPLES, DAC_ALIGN_12B_R) != HAL_OK)

 {

   /* Start DMA Error */

   Error_Handler2();

 }

}

You can see above where I have transplanted my own sinewave array which is just the following:

#define _AMP(x) ( x / 8 )

const size_t SINE_SAMPLES = 32;

const uint16_t SINE_WAVE[] = {

 _AMP(2048), _AMP(2447), _AMP(2831), _AMP(3185),

 _AMP(3495), _AMP(3750), _AMP(3939), _AMP(4056),

 _AMP(4095), _AMP(4056), _AMP(3939), _AMP(3750),

 _AMP(3495), _AMP(3185), _AMP(2831), _AMP(2447),

 _AMP(2048), _AMP(1649), _AMP(1265), _AMP(911),

 _AMP(601), _AMP(346), _AMP(157), _AMP(40),

 _AMP(0),   _AMP(40),  _AMP(157), _AMP(346),

 _AMP(601), _AMP(911), _AMP(1265), _AMP(1649)

};

There is also:

void DACx_DMA_IRQHandler(void)

{

 HAL_DMA_IRQHandler(DacHandle.DMA_Handle1);

}

And:

DAC_HandleTypeDef   DacHandle;

static DAC_ChannelConfTypeDef sConfig;

As soon as SineWaveConfig() is called the sound is heard, but the code crashes. Any suggestions would be much appreciated, thank-you.

TMA1of3
Associate III

Hi again F.Belaid.

I’ve done some further evaluation and testing based on the sample project you recommended (NUCLEO-H743ZI\Examples\DAC\DAC_SignalsGeneration). I was previously looking at example “STM32H743I-EVAL\Examples\DAC\DAC_SignalsGeneration�?, prior to my reaching-out here, although I think these two examples are the same, but please correct me if I’m mistaken.

Each example project refers to a Triangle Wave generator, through DAC_Ch1_TriangleConfig(), and an Escalator Wave generator through DAC_Ch1_EscalatorConfig(). The setup/initialisation code seems to be exactly the same with the only difference being which of these two functions is then called.

I seem to be having some good success with the triangle generation, but not with the escalator. The escalator waveform is output on PA4, when DAC_Ch1_EscalatorConfig() is called, but the code then immediately hits a hardfault (although the waveform continues on the pin).

The triangle wave code calls HAL_DACEx_TriangleWaveGenerate(), HAL_DAC_Start(), and HAL_DAC_SetValue(). The escalator code (which is more important for our application) instead calls HAL_DAC_Start_DMA() and I think the problem involves this function as all other parts of the code seem to be the same.

The initialisation code calls MPU_Config(), CPU_CACHE_Enable(), HAL_Init(), and SystemClock_Config(). In this project I’ve excluded SystemClock_Config() as I notice the code will reset if this is executed. The Triangle Wave generator works even without SystemClock_Config(), but the Escalator code does not function properly regardless.

Do you know why the Escalator code is not working here, please ? The code is copied from the example provided except for the exclusions mentioned. Your assistance would be greatly appreciated.

I mentioned function DAC_Ch1_SineWaveConfig() in my previous post - that is the same as the Escalator function just with a sine-wave in the array instead of an escalator shape.

And just in relation to the Triangle code can you please advise how to change the frequency of this signal ? I can alter the amplitude by changing from DAC_TRIANGLEAMPLITUDE_1023 (in function HAL_DACEx_TriangleWaveGenerate) to another of the options, but the frequency seems not to be so easy. I tried changing the fourth parameter in function call HAL_DAC_SetValue(), currently 0x100, but that had very little effect and changing the parameters associated with the initialisation of Timer6 had zero effect (things like Period, Prescaler, ClockDivision, and RepetitionCounter). Basically we need something in the audible frequency range, and the existing settings have the triangle at around 4 hertz (~250ms peak to peak).

TMA1of3
Associate III

I've now seen a post titled "DMA is not working on STM32H7 devices" in the Knowledge Database, and was wondering if this is the reason for the issues I'm observing ? Is it a related problem explaining my experience with DMA/DAC performance ? If so what is the workaround ? The Knowledge Database details a large amount of information which is difficult to understand, so can you explain it more simply assuming it is a related issue ?

FBL
ST Employee

Hello @Community member​ 

  1. There is nothing wrong with the escalator wave. Maybe you should verify you scaling.
  2. The fourth parameter of HAL_DAC_SetValue() is data to be loaded in the holding register. You have to adapt instead Timer6 parameters like Period, Prescaler ...
  3. Try to increase Prescaler so that you can adapt the period and it will covers 250ms.

I hope this would be some of help. When your question is answered, please close this topic by choosing Select as Best. This will help other users find that answer faster.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

TMA1of3
Associate III

Hello F.Belaid, and thank-you very much.

I think the hard-fault problem has been resolved. The pre-existing code was already using Stream5 for ADC/DMA, and so too was this ST sample code for DAC/DMA hence this was the problem I believe. I then chose Stream0 for DAC/DMA and it seems to be Ok now. Am I correct to assume any unused Stream can be chosen ? Or are there restrictions/rules governing this choice ?

Either way the code is reliably outputting a waveform on PA4. For the escalator waveform it appears as expected, but when I introduced a sine-wave initially something was still wrong. I noticed the sample code used uint8_t array values together with DAC_ALIGN_8B_R in HAL_DAC_Start_DMA(). I originally had uint16_t and DAC_ALIGN_12B_R which didn’t work. Should 12B work properly when the array values are less than 12 bits in length ?

When I changed to Stream0 I also modified the following:

void DMA1_Stream0_IRQHandler(void)

{ HAL_DMA_IRQHandler(DacHandle.DMA_Handle1); }

If I set a breakpoint here should I expect the code to reach it ? It doesn’t, and I don’t know if that indicates a problem ?

And can I use another Timer instead of Timer6 ? Timer6 is already being used in the pre-existing code, and it might be affected by the different Timer6 setup in this DAC/DMA sample.

At the moment I’m seeing a ~260Hz sine-wave on PA4, and this isn’t changing when I change Timer6 values such as Period, Prescaler and Clock Division (which you have also confirmed should be tried), but it doesn’t seem to help. What is the equation relating output frequency to such parameters, including Timer6, that are set in the code ?

I removed function SystemClock_Config() from the sample code because with this included the micro was permanently resetting. Is there some part of this function that must be used ? In the pre-existing code I see the following in terms of something that might be clock configuration related (right at the start you see SystemClock_Config() is deliberately commented-out by the previous engineer) :

 // Clock Config

 //SystemClock_Config();

 // Turn on GPIOA, GPIOB, GPIOC GPIOE and SYSCFG

 RCC->AHB4ENR |= (RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN);

 RCC->APB4ENR |= (RCC_APB4ENR_SYSCFGEN);

And in other spots:

 __HAL_RCC_D2SRAM1_CLK_ENABLE();

 __HAL_RCC_D2SRAM2_CLK_ENABLE();

 __HAL_RCC_D2SRAM3_CLK_ENABLE();

 // activate SRAM1\2\3

 RCC->AHB2ENR |= (0x7 << 29);

   /*##-1- Enable peripherals and GPIO Clocks #################################*/

   // ADC1 Periph clock enable

   __HAL_RCC_ADC12_CLK_ENABLE();

   __HAL_RCC_ADC3_CLK_ENABLE();

   //DAC Periph clock enable

   __HAL_RCC_DAC12_CLK_ENABLE();

   //__HAL_RCC_DAC2_CLK_ENABLE();

   // Enable the DMA Clock

   __HAL_RCC_DMA1_CLK_ENABLE();

I don’t want to inadvertently affect any existing performance by changing clock settings, but must there be some change made to have the sine-wave being output at higher frequencies ?

The two functions in CPU_CACHE_Enable() don’t do anything so it can’t be needed, but is MPU_Config() needed to implement this DMA/DAC functionality ? Thank-you.