2020-05-12 05:54 PM
Trying to get a basic sample DAC1 with DMA, triggered by TIM2 working.
This should be trivial, works fine on SMT32F3. I'm using a Nucleo-32 SMT32G431.
Using Cube IDE. The only non IDE-generated code in main is:
/* USER CODE BEGIN 2 */
static uint16_t x[4];
x[0]= 100;
x[1]= 1000;
x[2]= 2000;
x[3]= 3000;
HAL_TIM_Base_Start_IT(&htim2);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)x, 4, DAC_ALIGN_12B_R);
/* USER CODE END 2 */
IOC configuration:
Compared to some DAC/DMA examples - everything seems fine (and works on a Nucleo-32 SMT32F3. Please help - this is extremely frustrating and has held up a a project for 3 days.
test1.ioc and main.c files attached.
Thank you!
Paul
2020-05-13 09:28 AM
Thank you for responding!
Here it is (trying to trigger DMA to DAC1 using TIM6).
Configuration code followed by output:
// ---------------------------------------
// based on https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
// Enable peripherals: GPIOA, DMA, DAC, TIM6.
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
RCC->AHB2ENR |= ( RCC_AHB2ENR_DAC1EN);
RCC->APB1ENR1 |= (RCC_APB1ENR1_TIM6EN);
// 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 = ~( DMA_CCR_MEM2MEM |
DMA_CCR_PL |
DMA_CCR_MSIZE |
DMA_CCR_PSIZE |
DMA_CCR_PINC |
DMA_CCR_EN |
DMA_CCR_HTIE |
DMA_CCR_TCIE |
0);
uint32_t dma_ccr_set = ( ( 0x2 << DMA_CCR_PL_Pos ) |
( 0x1 << DMA_CCR_MSIZE_Pos ) |
( 0x1 << DMA_CCR_PSIZE_Pos ) |
DMA_CCR_MINC |
DMA_CCR_CIRC |
DMA_CCR_DIR );
DMA1_Channel1->CCR &= dma_ccr_clr;
DMA1_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 wave buffer in memory.
DMA1_Channel1->CMAR = ( uint32_t )&x[0];
// Dest.: DAC1 Ch1 '12-bit right-aligned data' register.
DMA1_Channel1->CPAR = ( uint32_t )&( DAC1->DHR12R1 );
// Set DMA data transfer length (# of wave samples).
DMA1_Channel1->CNDTR = ( uint16_t )400;
// Enable DMA1 Channels 1/2.
DMA1_Channel1->CCR |= ( DMA_CCR_EN );
// TIM6 configuration.
// Set prescaler and autoreload (irrelevant, just slow enough to test)
TIM6->PSC = 10000;
TIM6->ARR = 1000;
// 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 (event channel 7)
DAC1->CR &= ~( DAC_CR_TSEL1 );
DAC1->CR |= ( 0x7 << 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 );
#endif
p1_console_printf("ready!\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
static int n;
n++;
p1_console_printf("#%d >>>>>>>>>>>>>>\n", n);
#define P(x) pr(#x, x)
p1_console_printf("TIMER =====\n");
P(TIM6->CR1);
P(TIM6->CR2);
P(TIM6->PSC);
P(TIM6->ARR);
P(TIM6->DIER);
P(TIM6->SR);
P(TIM6->EGR);
P(TIM6->CNT);
p1_console_printf("DMA =====\n");
P(DMA1->ISR);
P(DMA1_Channel1->CCR);
P(DMA1_Channel1->CMAR);
P(DMA1_Channel1->CPAR);
P(DMA1_Channel1->CNDTR);
p1_console_printf("DAC =====\n");
P(DAC1->CR);
P(DAC1->SR);
P(DAC1->DOR1);
HAL_Delay(5000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
Ouput (two set, 5sec apart - the timer is spinning, DAC seems to report DMA underrun???
#11 >>>>>>>>>>>>>>
TIMER =====
TIM6->CR1 = 0x00000001 (1 dec) 31:00000000 23:00000000 15:00000000 7:00000001
TIM6->CR2 = 0x00000020 (32 dec) 31:00000000 23:00000000 15:00000000 7:00100000
TIM6->PSC = 0x00002710 (10000 dec) 31:00000000 23:00000000 15:00100111 7:00010000
TIM6->ARR = 0x000003E8 (1000 dec) 31:00000000 23:00000000 15:00000011 7:11101000
TIM6->DIER = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
TIM6->SR = 0x00000001 (1 dec) 31:00000000 23:00000000 15:00000000 7:00000001
TIM6->EGR = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
TIM6->CNT = 0x00000040 (64 dec) 31:00000000 23:00000000 15:00000000 7:01000000
DMA =====
DMA1->ISR = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
DMA1_Channel1->CCR = 0x000025B1 (9649 dec) 31:00000000 23:00000000 15:00100101 7:10110001
DMA1_Channel1->CMAR = 0x20000000 (536870912 dec) 31:00100000 23:00000000 15:00000000 7:00000000
DMA1_Channel1->CPAR = 0x50000808 (1342179336 dec) 31:01010000 23:00000000 15:00001000 7:00001000
DMA1_Channel1->CNDTR = 0x00000190 (400 dec) 31:00000000 23:00000000 15:00000001 7:10010000
DAC =====
DAC1->CR = 0x0000101F (4127 dec) 31:00000000 23:00000000 15:00010000 7:00011111
DAC1->SR = 0x00002800 (10240 dec) 31:00000000 23:00000000 15:00101000 7:00000000
DAC1->DOR1 = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
#12 >>>>>>>>>>>>>>
TIMER =====
TIM6->CR1 = 0x00000001 (1 dec) 31:00000000 23:00000000 15:00000000 7:00000001
TIM6->CR2 = 0x00000020 (32 dec) 31:00000000 23:00000000 15:00000000 7:00100000
TIM6->PSC = 0x00002710 (10000 dec) 31:00000000 23:00000000 15:00100111 7:00010000
TIM6->ARR = 0x000003E8 (1000 dec) 31:00000000 23:00000000 15:00000011 7:11101000
TIM6->DIER = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
TIM6->SR = 0x00000001 (1 dec) 31:00000000 23:00000000 15:00000000 7:00000001
TIM6->EGR = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
TIM6->CNT = 0x000003B2 (946 dec) 31:00000000 23:00000000 15:00000011 7:10110010
DMA =====
DMA1->ISR = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
DMA1_Channel1->CCR = 0x000025B1 (9649 dec) 31:00000000 23:00000000 15:00100101 7:10110001
DMA1_Channel1->CMAR = 0x20000000 (536870912 dec) 31:00100000 23:00000000 15:00000000 7:00000000
DMA1_Channel1->CPAR = 0x50000808 (1342179336 dec) 31:01010000 23:00000000 15:00001000 7:00001000
DMA1_Channel1->CNDTR = 0x00000190 (400 dec) 31:00000000 23:00000000 15:00000001 7:10010000
DAC =====
DAC1->CR = 0x0000101F (4127 dec) 31:00000000 23:00000000 15:00010000 7:00011111
DAC1->SR = 0x00002800 (10240 dec) 31:00000000 23:00000000 15:00101000 7:00000000
DAC1->DOR1 = 0x00000000 (0 dec) 31:00000000 23:00000000 15:00000000 7:00000000
2020-05-13 11:17 AM
You haven't posted the DMAMUX registers.
TIM6->DIER = 0x00000000
There is nothing which would trigger the DMA - TIMx_DIER.UDE bit is supposed to be set.
Ah you trigger DAC using TIM6 and then trigger DMA from DAC... okay I need to have a look at that.
JW
2020-05-13 11:32 AM
DMAMUX1_Channel0->CCR |= ( 0x8 << DMAMUX_CxCR_DMAREQ_ID_Pos );
Isn't it 0x6 rather than 0x8?
And again the numbering is not 0-based so I wouldn't be that sure with that either... [EDIT] it's okay, 0 is not in the table as it is special and when used it means "this channel is off"[/EDIT]
JW
2020-05-13 01:13 PM
DMAMUX and GPIOA should be enabled in RCC->****ENR too. Enable GPIOA first, because a few cycles delay is usually needed before the peripheral becomes active.
Yes, DMA request ID should be 6 instead of 8 if DMA is triggered by the DAC.
in stm32g4xx_hal_dma.h:
#define DMA_REQUEST_DAC1_CHANNEL1 6U
22.7 DAC registers
[...] The peripheral registers have to be accessed by words (32-bit).
so change the DMA PSIZE parameter to 2 (32 bits)
( 0x2 << DMA_CCR_PSIZE_Pos ) |
MSIZE can remain 01 (16 bits), DMA will pad the data with 0s when extending from 16 to 32 bits.
2020-05-14 04:40 AM
Thank you very much for your help - got it to work. Seems that the CUBE IDE code generator and HAL are simply ignoring DMAMUX.
2020-05-14 05:27 AM
I'm not going to investigate this, but IIRC DMAMUX in Cube (some other STM32 family's which has DMAMUX I looked at some time ago) is set automagically inside some of the usual DMA setup functions, so CubeMX does not need to be aware of it. I may be mistaken, I obviously don't Cube.
JW
2020-09-22 03:47 PM
Great thread! I am facing the same issue on a STM32G431 processor, I copied the code posted above with the proposed changes, but the DAC out is not updating, despite a running timer.
I suspect that I missed something in the DMAMUX, is there anyone so kind to explain whether more DMAMUX registers need to be updated? Thanks!
2023-07-31 04:14 AM
Me too. Only strangely the version I made for the nucleo board works on the 100 pin chip used for the PCBs made. I solved the ADC by using a HAL_DMA_init() call that I never had to use before (perhaps later versions of the CubeMX used to put this call in and now don't?). However the DAC output which used to work fine on a neucleo board simply does not start. The
if (HAL_DAC_Start_DMA(&hdac3, DAC_CHANNEL_2, (uint32_t*) &dac_data_out[0], size,
DAC_ALIGN_12B_R) != HAL_OK) {
Error_Handler();
}
Returns HAL_OK. The DMA simply just does not start.
2023-09-08 07:28 PM
I tried with STM32G431CBT6 but failed by Arduino IDE.
bool initDMA( uint8_t pin, uint8_t *wave, uint32_t freq, uint32_t len ) {
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/ -- DAC configuration.
//-------------------------
RCC->AHB2ENR |= ( RCC_AHB2ENR_DAC1EN);
RCC->APB1ENR1 |= (RCC_APB1ENR1_TIM6EN);
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN;
// https://community.st.com/t5/stm32-mcus-products/stm32g431kb-dac-dma-not-working/td-p/251096/page/2
if( dac_channel == 0 ) {
DMAMUX1_Channel2->CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
DMAMUX1_Channel2->CCR |= ( 0x6 << DMAMUX_CxCR_DMAREQ_ID_Pos );
//DMAMUX1_Channel2->CCR |= DMA_REQUEST_DAC1_CHANNEL1; // DAC1 CH1 6
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // It must not be written when the channel is enabled
DMA1_Channel3->CCR = DMA_CCR_MINC // memory increment
| DMA_CCR_DIR // memory to peripheral
//| ( 0x2 << DMA_CCR_PSIZE_Pos ) // 32 bit
| DMA_CCR_CIRC; // curcular mode
//
DMA1_Channel3->CNDTR = len; // how many data to transfer
DMA1_Channel3->CMAR = (uint32_t)&wave[ 0 ];
DMA1_Channel3->CPAR = (uint32_t)&( DAC->DHR8R1 ); // destination address - DAC 8 bit output register
DMA1_Channel3->CCR |= DMA_CCR_EN;
}
else {
DMAMUX1_Channel3->CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
DMAMUX1_Channel3->CCR |= ( 0x7 << DMAMUX_CxCR_DMAREQ_ID_Pos );
//DMAMUX1_Channel3->CCR |= DMA_REQUEST_DAC1_CHANNEL2; // DAC1 CH2 7
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
DMA1_Channel4->CCR = DMA_CCR_MINC // memory increment
| DMA_CCR_DIR // memory to peripheral
//| ( 0x2 << DMA_CCR_PSIZE_Pos ) // 32 bit
| DMA_CCR_CIRC; // curcular mode
//
DMA1_Channel4->CNDTR = len; // how many data to transfer
DMA1_Channel4->CMAR = (uint32_t)wave;
DMA1_Channel4->CPAR = (uint32_t)&( DAC->DHR8R2 ); // destination address - DAC 8 bit output register
}
//-------------------------------------------------
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
TIM6->CR1 &= ~TIM_CR1_CEN;
TIM6->ARR = SystemCoreClock / ( freq * len ) - 1;
TIM6->CR2 &= ~TIM_CR2_MMS;
TIM6->CR2 |= 2 << TIM_CR2_MMS_Pos; // the update event is selected as trigger output
TIM6->CR1 |= TIM_CR1_CEN;
//-----------------------------------------------------------------------
pinMode( pin, OUTPUT );
switch( pin ) {
case PA4 :
GPIOA->MODER &= ~( 0x3 << ( 4 * 2 ) );
GPIOA->MODER |= ( 0x3 << ( 4 * 2 ) );
//GPIOA->MODER |= GPIO_MODER_MODE4;
dac_channel = 0;
DAC->CR &= ~( DAC_CR_EN1 | DAC_CR_CEN1 ); // for register access
DAC->MCR &= ~DAC_MCR_MODE1;
DAC->CR |= DAC_CR_TEN1; // trigger enable
DAC->CR |= DAC_CR_DMAEN1; // DMA 사용
DAC->CR &= ~DAC_CR_TSEL1; // DAC channel1 trigger selection
DAC->CR |= DAC_TRIGGER_T6_TRGO; // DAC_TRIGGER_T6_TRGO
DAC->CR |= DAC_CR_EN1;
while( ( DAC->SR & DAC_SR_DAC1RDY ) == 0 );
break;
case PA5 :
GPIOA->MODER &= ~( 0x3 << ( 5 * 2 ) );
GPIOA->MODER |= ( 0x3 << ( 5 * 2 ) );
//GPIOA->MODER |= GPIO_MODER_MODE5;
dac_channel = 1;
DAC->CR &= ~( DAC_CR_EN2 | DAC_CR_CEN2 ); // for register access
DAC->MCR &= ~DAC_MCR_MODE2;
DAC->CR |= DAC_CR_TEN2; // trigger enable
DAC->CR |= DAC_CR_DMAEN2; // DMA 사용
DAC->CR &= ~DAC_CR_TSEL2; // DAC channel2 trigger selection
DAC->CR |= DAC_TRIGGER_T6_TRGO; // DAC_TRIGGER_T6_TRGO 7
DAC->CR |= DAC_CR_EN2;
while( ( DAC->SR & DAC_SR_DAC2RDY ) == 0 );
break;
default : return false;
}
return true;
}
2023-09-08 10:39 PM
"Manually" filled DAC (in a loop, using loopdelay) works?
TIM6_CNT, DMA_NDTR do change when observed in debugger?
Read out and check/post content of TIM, DMA, DAC and relevant GPIO registers.
JW