2020-07-15 11:07 PM
Hi, I am currently working on small project which I need to generate some arbitrary signal using DAC inside STM32G431KB.
Code is shown below.
#include "main.h"
#include "SysClockConfig.h"
uint16_t Output_Buffer[10] = {2048,2048,2048,2048,2048,2048,2048,2048,2048,2048};
void Initialization()
{
//----------Clock Config----------//
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; //Enable Timer 2 Clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN; //Enable DMA
RCC->AHB2ENR |= RCC_AHB2ENR_DAC3EN | RCC_AHB2ENR_GPIOAEN; //Enable DAC3 and GPIOA
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Enable SysCfg for OPAMP
//----------GPIO Config----------//
GPIOA->MODER |= GPIO_MODER_MODE2; //Analog Mode
GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD2); //No PU-PD
//----------OPAMP Config----------//
OPAMP1->CSR &= ~(OPAMP_CSR_OPAMPxEN); //Disable OPAMP
OPAMP1->CSR |= OPAMP_CSR_VPSEL; //Connect DAC3_CH1 to VINP
OPAMP1->CSR |= OPAMP_CSR_VMSEL; // Follower Mode
OPAMP1->CSR |= OPAMP_CSR_HIGHSPEEDEN; // Enable High-Speed Mode
OPAMP1->CSR &= ~(OPAMP_CSR_OPAMPINTEN); //Output connected to pin
OPAMP1->CSR |= OPAMP_CSR_OPAMPxEN; //Enable OPAMP
//----------Timer Config----------//
TIM2->CR1 &= ~(TIM_CR1_CEN); //Counter disable
TIM2->PSC = 149; //
TIM2->ARR = 2000000; //
TIM2->CR1 |= TIM_CR1_URS; //Only counter overflow/underflow generates an update
TIM2->CR2 |= (0b010<<TIM_CR2_MMS_Pos); //The update event is selected as a trigger output
TIM2->EGR |= TIM_EGR_UG; //Update generation
//----------DAC Config-----------//
DAC3->CR &= ~(DAC_CR_EN1); //Disable DAC
DAC3->CR |= DAC_CR_DMAEN1; //Enable DMA on DAC Ch1
DAC3->CR &= ~(DAC_CR_WAVE1); //Wave Generation Disabled
DAC3->CR |= (4 << DAC_CR_TSEL1_Pos); //Trigger Channel 4 = Timer TRGO 2
DAC3->MCR |= (0b011 << DAC_MCR_MODE1_Pos);//Normal Mode DAC Channel 1 is connected to on chip peripherals with Buffer disabled.
DAC3->MCR |= (0b01 << DAC_MCR_HFSEL_Pos); //High frequency interface mode compatible to AHB>80 MHz enabled
DAC3->CR |= DAC_CR_TEN1; //Channel Trigger Enable
DAC3->CR |= DAC_CR_EN1; //Enable DAC
while(!(DAC3->SR & DAC_SR_DAC1RDY)); //Wait Until DAC is ready
//----------DMA1 Channel1 Configuration----------//
DMA1_Channel1->CNDTR = 10; //Set number of data to be sent
DMA1_Channel1->CPAR = (uint32_t) &DAC3->DHR12R1; //Peripheral address
DMA1_Channel1->CMAR = (uint32_t) Output_Buffer; //Data Buffer address
// VHigh Priority 16bit Data size 16bit Data size Memory Inc. Circular Mode Memory2Perip
DMA1_Channel1->CCR |= (0b11 << DMA_CCR_PL_Pos) | (0b01 << DMA_CCR_MSIZE_Pos) | (0b01 << DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_DIR ;
DMAMUX1_Channel0->CCR |= (102 << DMAMUX_CxCR_DMAREQ_ID_Pos); //Connect DAC3_CH1 to DMA channel 1
DMA1_Channel1->CCR |= DMA_CCR_EN; //Enable Channel
}
int main(void)
{
HAL_Init();
SystemClock_Config();
Initialization();
//DAC3->DHR12R1 = 2048;
TIM2->CR1 |= TIM_CR1_CEN; //Counter enable
while (1)
{
}
}
void Error_Handler(void)
{
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;
RCC_OscInitStruct.PLL.PLLN = 75;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
{
Error_Handler();
}
SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);
}
The code works if I don't use DMA. I can see the expected Output on PA2. However, when I tried to use DMA the output stays at 0V and I get DMA underrun bit set in DAC_SR register.
My timer is really slow 150MHz/150 = 1MHz/2000000= 2seconds. I can't see why this happens since underrun caused when DMA can't keep up with the DAC trigger speed.
I suspect that DMA do not work at start and thus giving underrun error at beginning.
Has anyone encountered something like this?
Thank you.
Solved! Go to Solution.
2020-07-16 02:05 AM
Check the DMA registers - you'll probably see it's in Transfer Error. You have to set DMA on peripheral side to WORD size transfers, as DAC in 'G4 is directly on AHB and DAC registers allow only 32-bit access.
I'd also set up DMA first, only after then I'd enable DAC's DMAEN.
I can't comment about the analog performance - the performance you mention being given in datasheet, is it given for the DAC-opamp combo? MSPS does not necessarily imply analog bandwidth, too.
JW
2020-07-15 11:56 PM
Also I tried using timer interrupt to send data into "DAC3->DHR12R1" register. I set timer to 2.5MHz and used internal OPAMP(follower) to route DAC3 output to external pin. Code works fine but it seems like output is limited to 1Msps even though in datasheet it says that DAC3 can output at 15Msps. Is this normal?
2020-07-16 02:05 AM
Check the DMA registers - you'll probably see it's in Transfer Error. You have to set DMA on peripheral side to WORD size transfers, as DAC in 'G4 is directly on AHB and DAC registers allow only 32-bit access.
I'd also set up DMA first, only after then I'd enable DAC's DMAEN.
I can't comment about the analog performance - the performance you mention being given in datasheet, is it given for the DAC-opamp combo? MSPS does not necessarily imply analog bandwidth, too.
JW
2020-07-16 03:32 AM
Thank you! After changing only peripheral transfer size to WORD DMA is working now. Also it can go beyond 1Msps now. Maybe there is bandwidth limitation in interrupt method because I can sample 2.5Msps with DMA.