cancel
Showing results for 
Search instead for 
Did you mean: 

Driving DAC from circular buffer using DMA and TIM - why is it not working?

cbcooper
Senior

As the title says, I've got a (circular) buffer of uint16_t values that I want to send to the DAC every time the timer updates.  It seems like it's 95% working, in that if I manually trigger the DAC:

for (int i = 0; i < 10000; ++i)
{
   LL_DAC_TrigSWConversion(DAC1, LL_DAC_CHANNEL_1);
   HAL_Delay(1);
}

I can see my waveform on the output pin. But when I try to have the DMA send the values to the DAC, the output never changes.

One complication is that I need to run in two modes - in one mode I'm taking the ADC input, processing it, and sending that to the DAC, in the other mode (I'm calling "inject" mode) the ADC is ignored and the DAC values come from my memory buffer.

The code to switch between modes looks like this:

if (inject && !Inject)
{
	// Turn on inject mode
	// HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)Transfer, 4096, DAC_ALIGN_12B_R);
	LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
	while (LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_3));  // Wait till off
	LL_DAC_Disable(DAC1, LL_DAC_CHANNEL_1);
	//
	LL_DAC_ClearFlag_DMAUDR1(DAC1);
	LL_DMA_ClearFlag_TC3(DMA1);
	LL_DMA_ClearFlag_HT3(DMA1);
	LL_DMA_ClearFlag_TE3(DMA1);
	//
	LL_DAC_SetTriggerSource(DAC1, LL_DAC_CHANNEL_1, LL_DAC_TRIG_EXT_TIM4_TRGO);
	LL_DAC_EnableTrigger(DAC1, LL_DAC_CHANNEL_1);
	//
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
	LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_HALFWORD);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_HALFWORD);

	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)&DAC1->DHR12R1);
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_3, (uint32_t)Transfer);
	LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, 4096);

	// Enable DMA request on DAC channel
	LL_DAC_EnableDMAReq(DAC1, LL_DAC_CHANNEL_1);

	// Enable DMA channel
	LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
	LL_DAC_Enable(DAC1, LL_DAC_CHANNEL_1);
	//
	LL_TIM_SetTriggerOutput(TIM4, LL_TIM_TRGO_UPDATE);
	// LL_DAC_TrigSWConversion(DAC1, LL_DAC_CHANNEL_1);
	//
	/*
	for (int i = 0; i < 10000; ++i)
	{
	    LL_DAC_TrigSWConversion(DAC1, LL_DAC_CHANNEL_1);
		HAL_Delay(1);
	}
	*/
	Inject = inject;
}
else if ((!inject) && Inject)
{
	// Turn off inject mode
	LL_DAC_SetTriggerSource(DAC1, LL_DAC_CHANNEL_1, LL_DAC_TRIG_SOFTWARE);
	LL_DAC_DisableTrigger(DAC1, LL_DAC_CHANNEL_1);
	// LL_DAC_Disable(DAC1, LL_DAC_CHANNEL_1);
	//
	LL_DAC_DisableDMAReq(DAC1, LL_DAC_CHANNEL_1);
	LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
	//
	Inject = inject;
}

Here are the init functions created from the IOC file:

static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}
static void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 239;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 10;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */

}
static void MX_DAC1_Init(void)
{

  /* USER CODE BEGIN DAC1_Init 0 */

  /* USER CODE END DAC1_Init 0 */

  DAC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN DAC1_Init 1 */

  /* USER CODE END DAC1_Init 1 */

  /** DAC Initialization
  */
  hdac1.Instance = DAC1;
  if (HAL_DAC_Init(&hdac1) != HAL_OK)
  {
    Error_Handler();
  }

  /** DAC channel OUT1 config
  */
  sConfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
  sConfig.DAC_DMADoubleDataMode = DISABLE;
  sConfig.DAC_SignedFormat = DISABLE;
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_T4_TRGO;
  sConfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_EXTERNAL;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DAC1_Init 2 */

  /* USER CODE END DAC1_Init 2 */

}
static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

What am I doing wrong?  Thanks for any help!

1 ACCEPTED SOLUTION

Accepted Solutions

I don't know which change did it, but it's working now!  For reference, here's my code:

void MyInit_DAC()
{
	__HAL_RCC_DAC1_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();

	// Initialize GPIO A4 is an analog output
	LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_ANALOG);
	LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_4, LL_GPIO_PULL_NO);

	// MCR is reset to zero, and all HAL did was clear some bits.
	DAC1->MCR = 0;

	// The top half of CR is for channel 2, the bottom half has these bits:
	// [14] CEN1: DAC channel1 calibration enable (0 = normal operating mode)
	// [13] DMAUDRIE1: DAC channel 1 DMA underrun interrupt enable (0 = interrupt disabled)
	// [12] DMAEN1: DAC channel1 DMA enable (we will set this in CopyMemToDAC())
	// [11:8] MAMP1: DAC channel1 mask/amplitude selector (not used, leave 0)
	// [7:6] WAVE1: DAC channel1 noise/triangle wave generation enable (not used, leave 0)
	// [5:2] TSEL1: DAC channnel1 trigger selection (0 = SWTRIG1)
	// [1] TEN1: DAC channel1 trigger enable (0 = disabled)
	// [0] EN1: DAC channel1 enable
	DAC1->CR = DAC_CR_EN1;
}

 

void MyMX_DMA_Init_For_MemtoDAC(void)
{
	/* DMA controller clock enable */
    SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMAMUX1EN);
    SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN);

	uint32_t tmp;

	/* Get the CR register value */
	tmp = DMA1_Channel1->CCR;

	/* Clear PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR and MEM2MEM bits */
	tmp &= ((uint32_t)~(DMA_CCR_PL    | DMA_CCR_MSIZE  | DMA_CCR_PSIZE  |
	                    DMA_CCR_MINC  | DMA_CCR_PINC   | DMA_CCR_CIRC   |
	                    DMA_CCR_DIR   | DMA_CCR_MEM2MEM));

	/* Prepare the DMA Channel configuration */
	tmp |=  DMA_CCR_MINC |		// Memory increment yes (peripheral increment no)
	  DMA_PDATAALIGN_WORD |		// Peripheral takes a 32-bit word
	  DMA_MDATAALIGN_HALFWORD |		// Memory supplies a 16-bit halfword
	  DMA_CIRCULAR | // Circular (once memory buffer is exhausted, go back to top and send again)
	  DMA_PRIORITY_LOW |
	  DMA_MEMORY_TO_PERIPH;

	/* Write to DMA Channel CR register */
	DMA1_Channel1->CCR = tmp;

	/*
	 * "The mapping of resources to DMAMUX is hardwired.
        DMAMUX is used with DMA1 and DMA2:
        For category 3 and category 4 devices:
        • DMAMUX channels 0 to 7 are connected to DMA1 channels 1 to 8
        • DMAMUX channels 8 to 15 are connected to DMA2 channels 1 to 8
        For category 2 devices:
		• DMAMUX channels 0 to 5 are connected to DMA1 channels 1 to 6
		• DMAMUX channels 6 to 11 are connected to DMA2 channels 1 to 6"
	 */

	/* Set peripheral request to DMAMUX channel */
	DMAMUX1_Channel0->CCR = DMA_REQUEST_DAC1_CHANNEL1;

	/* Clear the DMAMUX synchro overrun flag */
	DMAMUX1_ChannelStatus->CFR = 1;
}

 

void CopyMemToDAC()
{
	// Disable channel 1 of the DAC while we set up the transfer
	DAC1->CR &= ~DAC_CR_EN1;

	// Put some known value as the DAC output
	DAC1->DHR12R1 = 123;

	// Disable DMA1 channel 1 while we update its settings
	DMA1_Channel1->CCR &= ~DMA_CCR_EN;

	/* Clear the DMAMUX synchro overrun flag */
	DMAMUX1_ChannelStatus->CFR = 1;

	/* Clear all flags */
	DMA1->IFCR = 0x0F;

	/* Configure DMA1 Channel 1 data length */
	DMA1_Channel1->CNDTR = 4096;		// "Number of data to transfer"

	/* Configure DMA1 Channel 1 destination address */
	DMA1_Channel1->CPAR = (uint32_t) &DAC1->DHR12R1;

	/* Configure DMA1 Channel 1 source address */
	DMA1_Channel1->CMAR = (uint32_t) Transfer;

	// Clear any DAC under-run errors
	WRITE_REG(DAC1->SR, DAC_SR_DMAUDR1);

	// Clear other errors
	LL_DMA_ClearFlag_TC1(DMA1);
	LL_DMA_ClearFlag_HT1(DMA1);
	LL_DMA_ClearFlag_TE1(DMA1);

	// Enable DMA1 channel 1
	DMA1_Channel1->CCR |= DMA_CCR_EN;

	// Enable DMA on DAC1 channel 1
	SET_BIT(DAC1->CR, DAC_CR_DMAEN1);

	// Tell DAC1 channel 1 to trigger on dac_chx_trg5; according to table 186 on page 718
	// this sets the source to TIM4_TRGO
	DAC1->CR = (DAC1->CR & ~DAC_CR_TSEL1_Msk) | (5 << DAC_CR_TSEL1_Pos) | DAC_CR_TEN1;

	// Re-enable channel 1 of the DAC to start the transfer
	DAC1->CR |= DAC_CR_EN1;
}

 

 

View solution in original post

19 REPLIES 19
cbcooper
Senior

I picked the wrong label, this is on a NUCLEO-G491RE board which has the STM32G491 chip.

 

I added an interface so that the STM32 could report back to the PC on the values of some registers.

TIM4->CNT increments as expected.

DMA1->ISR is always 0x0

DMA1_Channel3->CMAR is always 0x20000330 which is the address of my circular buffer

DMA1_Channel3->CNDTR is always 0x1000 which is the size, in bytes, of the circular buffer

DMA1_Channel3->CCR is always 0x5B1

  109876543210

= 010110110001

   | || ||   |

   | || ||   += Bit 0 = channel enable

   | || |+= Bit 4 = direction (read from memory)

   | || += Bit 5 = circular mode enabled

   | |+= Bit 7 = memory increment mode enabled

   | += Bit 9/8 = 01 = peripheral size 16 bits

   += Bit 11/10 = 01 = memory size 16 bits

DMA1_Channel3->CPAR is always 0x50000808 which I think is right for DAC1

DAC1->CR is always 0x1017

5432109876543210

0001000000010111

   |         |||

   |         ||+= bit 0 = channel 1 enabled

   |         |+= bit 1 = channel 1 trigger enabled

   |         += bit 5..2 = 0101 = dac_ch1_trg5 = TIM4_TRGO

   += bit 12 = channel 1 DMA enabled

DAC1->SR is always 0x2800

5432109876543210

0010100000000000

  | |

  | += bit 11 = DAC channel 1 ready

  += bit 13 = DAC channel DMA underrun

The doc says DMA underrun means "the currently selected trigger is driving DAC channel1 conversion at a frequency higher than the DMA service capability rate"

which I don't believe, I've got TIM4 prescaler set to 23990, I think I'm running at 72 Mhz so TIM4 is incrementing at 3,000 hz with a period of 100 so it's trying to update the DAC at 30 Hz.

But if I enable the DAC's DMA underrun interrupt and in the ISR clear the underrun and increment a counter, there are definitely underruns occuring, and I think it happens every time TIM4 rolls over.

waclawek.jan
Super User

Maybe incorrect DMAMUX setting?

JW

Good suggestion! 

I added

LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMAMUX_REQ_DAC1_CH1);

and it didn't seem to make any difference

 

waclawek.jan
Super User

Read out and check/post content of TIM, DAC, DMA and DMAMUX registers.

DAC underrun may occur because of sequencing of events: the DMA must be up and running before the trigger source. I don't use Cube and don't understand its gobbledygook to be able to judge whether this is or is not the case.

You can try single-stepping your code to see where does the underrun happen. You appear to have experimented with manual trigger, was that with or without DMA? You can manual trigger DAC also with stopped code execution, directly from the debugger.

The DS is not entirely clear whether DAC stops triggering upon underrun, but the requirement to restart the DAC appears to hint so.

JW

 

I slowed down the timer so I could see things changing:

htim4.Init.Prescaler = 23990;
htim4.Init.Period = 100;

and I have an interface that lets me query a few registers with a Python program (so it's faster than me typing in debugging commands) and it shows me this:

tim4_cnt=46, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=42064
tim4_cnt=61, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=42064
tim4_cnt=75, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=42064
tim4_cnt=90, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=42064
tim4_cnt=4, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=42065

'num_under' is incremented in MyTIM6_DAC_IRQHandler() when it sees the underrun flag is set, and 'xfer' is the address of my uint16_t buffer that I'm trying to send to the DAC.

I'm mostly not using Cube's auto-generated code, only in the 'Init()' functions.

When I add this code:

for (int i = 0; i < 10000; ++i)
{
    LL_DAC_TrigSWConversion(DAC1, LL_DAC_CHANNEL_1);
    HAL_Delay(1);
}

I'm adding it to the version of the code that is trying to use DMA.  It used to work (would change the value of the DAC) but I just now tried it and it didn't do anything.

 

I mentioned this program has two modes -

* Mode 1: program reads in ADC value, processes it, writes to DAC

* Mode 2: program ignores ADC, drives DAC directly from memory buffer (hopefully using DMA)

The program wakes up in mode 1 and I see this in the registers:

tim4_cnt=85, dma1_isr=0x0, dma1_ccr=0x0, dma1_cmar=0x0, dma1_cndtr=0x0, xfer=0x20000330, dma_cpar=0x0, dac_cr=0x17, dac_sr=0x800, num_under=0, dac1.DHR12R1=0xF75, DMAMUX1_Channel2->CCR=0x0
tim4_cnt=0, dma1_isr=0x0, dma1_ccr=0x0, dma1_cmar=0x0, dma1_cndtr=0x0, xfer=0x20000330, dma_cpar=0x0, dac_cr=0x17, dac_sr=0x800, num_under=0, dac1.DHR12R1=0xB24, DMAMUX1_Channel2->CCR=0x0
tim4_cnt=17, dma1_isr=0x0, dma1_ccr=0x0, dma1_cmar=0x0, dma1_cndtr=0x0, xfer=0x20000330, dma_cpar=0x0, dac_cr=0x17, dac_sr=0x800, num_under=0, dac1.DHR12R1=0x3E4, DMAMUX1_Channel2->CCR=0x0
tim4_cnt=33, dma1_isr=0x0, dma1_ccr=0x0, dma1_cmar=0x0, dma1_cndtr=0x0, xfer=0x20000330, dma_cpar=0x0, dac_cr=0x17, dac_sr=0x800, num_under=0, dac1.DHR12R1=0x0, DMAMUX1_Channel2->CCR=0x0
tim4_cnt=50, dma1_isr=0x0, dma1_ccr=0x0, dma1_cmar=0x0, dma1_cndtr=0x0, xfer=0x20000330, dma_cpar=0x0, dac_cr=0x17, dac_sr=0x800, num_under=0, dac1.DHR12R1=0xF78, DMAMUX1_Channel2->CCR=0x0

When I switch to mode 2 I see this:

tim4_cnt=10, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=730, dac1.DHR12R1=0xAC4, DMAMUX1_Channel2->CCR=0x6
tim4_cnt=26, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=730, dac1.DHR12R1=0xAC4, DMAMUX1_Channel2->CCR=0x6
tim4_cnt=43, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=730, dac1.DHR12R1=0xAC4, DMAMUX1_Channel2->CCR=0x6
tim4_cnt=59, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=730, dac1.DHR12R1=0xAC4, DMAMUX1_Channel2->CCR=0x6
tim4_cnt=76, dma1_isr=0x900, dma1_ccr=0x5B0, dma1_cmar=0x20000330, dma1_cndtr=0x1000, xfer=0x20000330, dma_cpar=0x50000808, dac_cr=0x3017, dac_sr=0x800, num_under=730, dac1.DHR12R1=0xAC4, DMAMUX1_Channel2->CCR=0x6

DMA1.ISR being set to 0x900 is new, I'm guessing that's because I've now got the DMAMUX set correctly

 

waclawek.jan
Super User

> When I switch to mode 2 I see this:

> num_under=730,

> dma1_ccr=0x5B0

So, the DAC underruns 730x before you even started the DMA?

You may want to write a simple example only with the DAC and DMA (and then perhaps TIM-triggered, once software ttrigger with DAC works) and nothing else.

JW

The DAC underruns 1x every time the timer rolls over so the '730' value just indicates how long it took the human (me) to tell the program to switch modes, and then move back to my PC and run the Python program that queried the register values.

waclawek.jan
Super User

Ah, I've just realized what's the root cause of the Transfer Error at DMA is: that DAC in 'G4 is at AHB, and does not support other than word-size transfers.

JW