cancel
Showing results for 
Search instead for 
Did you mean: 

Subtle Q on 32F4 dual DAC (32 bit data feed) mode

PHolt.1
Senior II

I am finding that with DAC1 triggered from TIM5, DAC2 can be set either for the same trigger (the doc implies that) or for no trigger ("software" trigger). Both work.

I would have expected software trigger / no trigger to work on DAC2 because the 32-bit mode feeds both DACs directly, so DAC2 should not care what caused the load to it.

Obviously they can't both trigger the DMA transfer! But ST might have put in logic to prevent DAC2 running unless its trigger is set the same as DAC1. And they could both trigger the DMA (although it would be pointless).

Entire code is below but my Q is around the init code for the two DACs. There is a bug in the code insertion on this forum and it clips a big chunk out but it is just a long comment and you can see the actual code.

void KDE_config_wave_gen( uint32_t *buffer, uint32_t num_values, uint16_t tim_pre, uint32_t tim_tc)
{
 
	// We get here with both DACs enabled from main.c, and outputting that init value
 
	TIM4->CR1 &= 0xfffe;				// disable TIM4 (DAC trigger, DAC then triggers DMA)
 
	// set both DAC outputs to Vref/2
	KDE_DAC_set_value(1, 2048);
	KDE_DAC_set_value(2, 2048);
 
	// DMA1_S5C5 EN=0 - disable DMA1 just in case (also needed at any time to write the address etc)
 
	DMA1_Stream5->CR &= 0xfffffffe;
	hang_around_us(10);					// this should wait on EN changing to 0, but is ok here
 
	// Config DAC1 for TIM5 trigger. DAC2 doesn't need anything because it gets loaded with the int32.
 
	DAC_ChannelConfTypeDef sConfig = {0};
	DAC_HandleTypeDef KDE_dac;
 
	KDE_dac.Instance = DAC;
	HAL_DAC_Init(&KDE_dac);
 
	// DAC1 config - gets fed with the DMA data (low 16 bits)
 
	sConfig.DAC_Trigger = DAC_TRIGGER_T5_TRGO;
	sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
	HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_1);
 
	// DAC2 config - gets fed with the DMA data (high 16 bits)
 
	sConfig.DAC_Trigger = DAC_TRIGGER_SOFTWARE;			// doc asks for DAC_TRIGGER_T5_TRGO for DAC2!
	sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;	// but either works.
	HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_2);
 
	// The following appears to do a spurious data transfer to both DACs, zeroing them
 
	DAC->CR = (1u << 12)				// DMAEN1=1 - DAC1 DMA1 enabled
			 |(5u << 3)					// TSEL1=5 - DAC1 trigger source Timer 4 (Timer 4 TRGO event)
			 |(1u << 2);				// TEN1=1 - DAC1 trigger enabled
 
	// set both DAC outputs to Vref/2 again, due to the above (there is a very short spike)
 
	KDE_DAC_set_value(1, 2048);
	KDE_DAC_set_value(2, 2048);
 
	// Timer 4 config - triggers both DACs
 
	RCC->APB1ENR |= (1u << 2);			// TIM4EN=1 - TIM 4 clock enable
	TIM4->CR1 = (1u << 7);				// Auto reload register (sample period) is buffered
	TIM4->CR2 = (2u << 4);				// master mode select for TRGO on update event
	TIM4->SMCR=0;						// disable slave mode, etc
	TIM4->PSC = tim_pre;				// prescaler for above
	TIM4->ARR = tim_tc;					// time constant
 
	// Config DMA1 Stream 7 Channel 7 to feed both DACs concurrently
 
	RCC->AHB1ENR |= (1u << 21);			// DMA1EN=1 - DMA1 clock enable
	hang_around_us(1);					// give it a chance to wake up
 
	DMA1->HIFCR = (0x03du << 8);		// clear Stream 5 interrupt flags
	DMA1_Stream5->CR = (0x07u << 25)	// DMA1, Stream5 channel 7 is the DAC
					  |(0u << 23)		// mburst single transfer
					  |(0u << 21)		// pburst single transfer
					  |(1u << 18)		// double buffer mode on
					  |(2u << 16)		// priority  = high
					  |(2u << 14)		// msize = 32 bits
					  |(2u << 11)		// psize = 32 bits (DAC using double register)
					  |(1u << 10)		// minc = 1
					  |(0u << 9)		// pinc = 0
					  |(1u << 8)		// circular mode buffering
					  |(1u << 6)		// memory to peripheral
					  |(0u << 5)		// dma is flow controller
					  |(0u << 4)		// no interrupts TCIE
					  |(0u << 3)		// no interrupts HTIE
					  |(0u << 2)		// no interrupts TEIE
					  |(0u << 1)		// no interrupts DMEIE
					  ;					// DMA is not yet enabled
	DMA1_Stream5->NDTR = num_values;					// buffer size
	DMA1_Stream5->PAR = (uint32_t) (&DAC1->DHR12RD);	// destination (DAC) address, 32 bit DAC mode
	DMA1_Stream5->M0AR = (uint32_t) buffer;				// both buffers are the same address
	DMA1_Stream5->M1AR = (uint32_t) buffer;				// code may alter this once the system is running
 
	DMA1_Stream5->CR |= 1;				// enable DMA to DACs
	TIM4->CR1 |= 1;						// start the timer
 
}

13 REPLIES 13

What is Q? Question? And what is the question?

Also, if you discuss DAC, read out and post its registers content.

JW

PHolt.1
Senior II

This bit

// DAC1 config - gets fed with the DMA data (low 16 bits)

sConfig.DAC_Trigger = DAC_TRIGGER_T5_TRGO;

sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_1);

// DAC2 config - gets fed with the DMA data (high 16 bits)

sConfig.DAC_Trigger = DAC_TRIGGER_SOFTWARE; // doc asks for DAC_TRIGGER_T5_TRGO for DAC2!

sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // but either works.

HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_2);

PHolt.1
Senior II

It is working ok but I wonder if DAC2 should be DAC_TRIGGER_T5_TRGO or DAC_TRIGGER_SOFTWARE

Both of them work but which one is right?

It seems that if one writes 32 bits to DAC1->DHR12RD then it doesn't matter.

I don't understand the Cube/HAL gibberish and have no intention to read its source now. That's why I asked content of DAC registers - the hardware works out of registers, not out of any arbitrary "library" code.

What's content of DAC_CR.TEN2? (read: read its description in RM).

JW

PHolt.1
Senior II

My Q (Q=question, indeed) centres on whether DAC2 needs to be in any special mode, in order to receive the top half of the 32 bit value.

The way the 32 bit mode works (I have spent hours googling of course, but most example online are useless) is that only one of the two DACs need to trigger the loading process. The other DAC seems to "just work" when the 32 bit value is loaded into DHR12RD.

In this case (a waveform generator) TIM4 triggers DAC1, and DAC1 triggers DMA1 to load the 32 bit value, which incidentally is also loaded into DAC2.

As I said, I have a working system but don't want to do something which could break.

After the above init, this is in there

https://peter-ftp.co.uk/screenshots/202111264713675013.jpg

and after

DAC->CR = (1u << 12) // DMAEN1=1 - DAC1 DMA1 enabled

|(5u << 3) // TSEL1=5 - DAC1 trigger source Timer 4 (Timer 4 TRGO event)

|(1u << 2); // TEN1=1 - DAC1 trigger enabled

this is what is in there

https://peter-ftp.co.uk/screenshots/202111262013685213.jpg

Thank you for looking at it.

To me it makes sense that DAC2 config is irrelevant so long as it is enabled.

You can copy/paste pictures here.

OK so the latter one:0693W00000GZ4c8QAD.png 

is taken from the state when DMA works and generates varying output on both DAC1 and DAC2?

That's strange as DAC_CR=0x102c thus both DACs are disabled ...

JW

PHolt.1
Senior II

The latest complete code is here. The wave generation starts after the last line, when TIM4 is started. The DAC regs at that point are as in the pic. The function KDE_DAC_set_value enables the DAC so that's why they are both enabled.

I may not have made my Q clear. It concerns whether DAC2 needs any special config (trigger from a timer, or request a DMA) when the 32 bit mode is being used.

0693W00000GZ4pbQAD.png 

void KDE_config_wave_gen( uint32_t *buffer, uint32_t num_values, uint16_t tim_pre, uint32_t tim_tc)
{
 
	// We get here with both DACs enabled from main.c, and outputting that init value
	// which can be defined in config.ini under dac_init
 
	TIM4->CR1 &= 0xfffe;				// disable TIM4 (DAC trigger, DAC then triggers DMA)
 
	// set both DAC outputs to Vref/2
	KDE_DAC_set_value(1, 2048);
	KDE_DAC_set_value(2, 2048);
 
	// DMA1_S5C5 EN=0 - disable DMA1 just in case (also needed at any time to write the address etc)
 
	DMA1_Stream5->CR &= 0xfffffffe;
	hang_around_us(10);					// this should wait on EN changing to 0, but is ok here
 
	// Config DAC1 for TIM5 trigger. DAC2 doesn't need anything because it gets loaded with the int32.
 
	DAC_ChannelConfTypeDef sConfig = {0};
	DAC_HandleTypeDef KDE_dac;
 
	KDE_dac.Instance = DAC;
	HAL_DAC_Init(&KDE_dac);
 
	// DAC1 config - gets fed with the DMA data (low 16 bits)
 
	sConfig.DAC_Trigger = DAC_TRIGGER_T5_TRGO;
	sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
	HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_1);
 
	// DAC2 config - gets fed with the DMA data (high 16 bits)
 
	sConfig.DAC_Trigger = DAC_TRIGGER_SOFTWARE;			// doc asks for DAC_TRIGGER_T5_TRGO for DAC2!
	sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;	// but either works.
	HAL_DAC_ConfigChannel(&KDE_dac, &sConfig, DAC_CHANNEL_2);
 
	// The following appears to do a spurious data transfer to both DACs, zeroing them
 
	DAC->CR = (1u << 12)				// DMAEN1=1 - DAC1 DMA1 enabled
			 |(5u << 3)					// TSEL1=5 - DAC1 trigger source Timer 4 (Timer 4 TRGO event)
			 |(1u << 2);				// TEN1=1 - DAC1 trigger enabled
 
	// set both DAC outputs to Vref/2 again, due to the above (there is a very short spike)
 
	KDE_DAC_set_value(1, 2048);
	KDE_DAC_set_value(2, 2048);
 
	// Timer 4 config - triggers both DACs
 
	RCC->APB1ENR |= (1u << 2);			// TIM4EN=1 - TIM 4 clock enable
	TIM4->CR1 = (1u << 7);				// Auto reload register (sample period) is buffered
	TIM4->CR2 = (2u << 4);				// master mode select for TRGO on update event
	TIM4->SMCR=0;						// disable slave mode, etc
	TIM4->PSC = tim_pre;				// prescaler for above
	TIM4->ARR = tim_tc;					// time constant
 
	// Config DMA1 Stream 7 Channel 7 to feed both DACs concurrently
 
	RCC->AHB1ENR |= (1u << 21);			// DMA1EN=1 - DMA1 clock enable
	hang_around_us(1);					// give it a chance to wake up
 
	DMA1->HIFCR = (0x03du << 8);		// clear Stream 5 interrupt flags
	DMA1_Stream5->CR = (0x07u << 25)	// DMA1, Stream5 channel 7 is the DAC
					  |(0u << 23)		// mburst single transfer
					  |(0u << 21)		// pburst single transfer
					  |(1u << 18)		// double buffer mode on
					  |(2u << 16)		// priority  = high
					  |(2u << 14)		// msize = 32 bits
					  |(2u << 11)		// psize = 32 bits (DAC using double register)
					  |(1u << 10)		// minc = 1
					  |(0u << 9)		// pinc = 0
					  |(1u << 8)		// circular mode buffering
					  |(1u << 6)		// memory to peripheral
					  |(0u << 5)		// dma is flow controller
					  |(0u << 4)		// no interrupts TCIE
					  |(0u << 3)		// no interrupts HTIE
					  |(0u << 2)		// no interrupts TEIE
					  |(0u << 1)		// no interrupts DMEIE
					  ;					// DMA is not yet enabled
	DMA1_Stream5->NDTR = num_values;					// buffer size
	DMA1_Stream5->PAR = (uint32_t) (&DAC1->DHR12RD);	// destination (DAC) address, 32 bit DAC mode
	DMA1_Stream5->M0AR = (uint32_t) buffer;				// both buffers are the same address
	DMA1_Stream5->M1AR = (uint32_t) buffer;				// code may alter this once the system is running
 
	DMA1_Stream5->CR |= 1;				// enable DMA to DACs
	TIM4->CR1 |= 1;						// start the timer
 
}

PHolt.1
Senior II

The comment "Timer 4 config - triggers both DACs" is wrong. Only DAC1 is being triggered by TIM4 TRGO. Then DAC1 initiates the DMA transfer of a single 32 bit value, which gets written to DAC1->DHR12RD.

LCE
Principal

Hi,

I'm probably not of much help, but I was curious...

Interesting that you set the (in my opinion more complicated) DMA registers bit by bit, and for the DAC you use the HAL stuff.

In case you used CubeMX to setup the DAC, beware! There's some stuff missing sometimes.

Concerning the DMA, why Double Buffer Mode ... okay, just saw your comment.

But why:

sConfig.DAC_Trigger = DAC_TRIGGER_T5_TRGO;

Looks like you choose timer 5 as trigger, but you set up timer 4?