2024-06-12 01:24 AM - last edited on 2024-06-12 09:27 AM by SofLit
I tried everything to send a simple wave through the DAC pin using HAL and STM32CubeIDE to configure the project. What I've checked that works:
- Clock source
- Trigger source
- Output pin configuration
- Built-in triangle and noise wave generation.
I tested identical piece of code on STM32H747 and it just worked without any issues.
The DMA is configured properly, as memory to peripheral, source address increment enabled, destination address increment disabled. Circular mode. Data length: half word. Request DMA1_OUT1.
When I start output with:
auto status = HAL_DAC_Start_DMA(m_dac, m_channel, (const uint32_t*)m_test, 64, DAC_ALIGN_12B_R);
I get the status HAL_OK.
However, 0V on oscilloscope. So I registered an error handler with:
HAL_DAC_RegisterCallback(dac, HAL_DAC_CH1_ERROR_ID, dacError);
And this function is indeed called right after `HAL_DAC_Start_DMA` is called.
The buffer contains 64 uint16_t samples, of altering 0 and 4095 values. It is aligned to 32-bit address properly, memory is accessible when the DMA function is called.
The DAC is triggered from TIM1 that generates the update event 22050 times per second. It works, when I switch from DMA playback to wave generation I see the wave is generated with the frequency matched to the TIM1 changing the output value by 1. I also tried to configure ISR for TIM1 and it is executed exactly as expected.
I tried to change almost every setting possible in both DMA and DAC configuration - including obviously invalid or pointless values just to see what happens.
And what I got is nothing. Constant 0V. Error callback called every time I try to send anything to the DAC via DMA.
I'm giving up for now. I see in U5 DAC just doesn't work, maybe the HAL driver has a bug? As for the DMA controller GPDMA device - it works with everything else. I use DMA to send data to UART. Works perfectly. I use DMA to read ADC samples. Works perfectly. But with DAC - no joy.
Here's some of my configuration:
void MX_DAC1_Init(void)
{
/* USER CODE BEGIN DAC1_Init 0 */
/* USER CODE END DAC1_Init 0 */
DAC_ChannelConfTypeDef sConfig = {0};
DAC_AutonomousModeConfTypeDef sAutonomousMode = {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_T1_TRGO;
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();
}
/** Configure Autonomous Mode
*/
sAutonomousMode.AutonomousModeState = DAC_AUTONOMOUS_MODE_DISABLE;
if (HAL_DACEx_SetConfigAutonomousMode(&hdac1, &sAutonomousMode) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DAC1_Init 2 */
/* USER CODE END DAC1_Init 2 */
}
void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
DMA_NodeConfTypeDef NodeConfig= {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
if(dacHandle->Instance==DAC1)
{
/* USER CODE BEGIN DAC1_MspInit 0 */
/* USER CODE END DAC1_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC|RCC_PERIPHCLK_DAC1;
PeriphClkInit.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_MSIK;
PeriphClkInit.Dac1ClockSelection = RCC_DAC1CLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/* DAC1 clock enable */
__HAL_RCC_DAC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**DAC1 GPIO Configuration
PA4 ------> DAC1_OUT1
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* DAC1 DMA Init */
/* GPDMA1_REQUEST_DAC1_CH1 Init */
NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
NodeConfig.Init.Request = GPDMA1_REQUEST_DAC1_CH1;
NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
NodeConfig.Init.DestInc = DMA_DINC_FIXED;
NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
NodeConfig.Init.SrcBurstLength = 64;
NodeConfig.Init.DestBurstLength = 64;
NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT1;
NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
NodeConfig.Init.Mode = DMA_NORMAL;
NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel2) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel2, NULL, &Node_GPDMA1_Channel2) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel2) != HAL_OK)
{
Error_Handler();
}
handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;
handle_GPDMA1_Channel2.InitLinkedList.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
handle_GPDMA1_Channel2.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel2.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel2.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel2.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel2) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel2, &List_GPDMA1_Channel2) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(dacHandle, DMA_Handle1, handle_GPDMA1_Channel2);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* DAC1 interrupt Init */
HAL_NVIC_SetPriority(DAC1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DAC1_IRQn);
/* USER CODE BEGIN DAC1_MspInit 1 */
/* USER CODE END DAC1_MspInit 1 */
}
}
Solved! Go to Solution.
2024-11-29 02:40 PM
I was running into the same problem. I had code from an L4 to timer trigger a sine wave via DMA into the DAC. But with the U5 it wouldn't work. The issue was the DMA transfer size. On the U5 the DMA destination transfer size must be word sized instead of half word sized.
Note that the source can still be half word sized, so you don't need to waste memory for your waveform table.
On the both the L4 and U5, all the DAC registers must be accessed by words according to the reference manuals. But interestingly, it still worked on the L4 with half word transfers. I loaded my L4 code and confirmed the transfers were set to half word in the registers, and it works, even though the manual says it must be word access.
2024-06-12 09:28 AM
Hello,
Could you please check that example from Cube HAL package and inspire from it?
2024-10-11 07:29 AM
Hello, any luck with DAC so far?
2024-10-11 08:00 AM
Hello @kirilb and welcome to the community,
Please open a new thread and describe your issue/your request and review these tips on how to post a thread on this community.
Thank you.
2024-10-11 01:29 PM
As soon as you giving uint_32t to DAC, try using word instead of half word in Data length in DMA settings.
2024-11-29 02:40 PM
I was running into the same problem. I had code from an L4 to timer trigger a sine wave via DMA into the DAC. But with the U5 it wouldn't work. The issue was the DMA transfer size. On the U5 the DMA destination transfer size must be word sized instead of half word sized.
Note that the source can still be half word sized, so you don't need to waste memory for your waveform table.
On the both the L4 and U5, all the DAC registers must be accessed by words according to the reference manuals. But interestingly, it still worked on the L4 with half word transfers. I loaded my L4 code and confirmed the transfers were set to half word in the registers, and it works, even though the manual says it must be word access.