cancel
Showing results for 
Search instead for 
Did you mean: 

DAC not working in bare metal on STM32F756VGH6

Vilius
Associate III

Hi,

I went through quite some earlier posts on this forum regarding DAC setup, but none of those are relevant to me nor helped me.

I am trying to setup a DAC on a STM32F756VGH6 via registers. I read all chapters about the DAC in the datasheet and it looked like quite a simple peripheral on this mcu, as it there is very little setup needed to be done (I just selected the trigger mode, enabled it and enabled the DAC1 itself on pin PA4.) My goal is just to setup a simple DAC without any continuous waves, DMA etc. Just a constant analog voltage value that I write to a register.

However, my problem is that I do not get anything on the DAC pin (always 0 V). I tried to put an external pulldown resistor, pullup resistor, but none of those worked. I also tried with both enabled and bypassed output buffer modes but that did not change anything either. I also tried to setup the PA4 pin as GPIO to blink some led, to see, if the pin itself was working, and everything was just fine...

It will either be unbelievably simple mistake or there is something fundamentally wrong with my perception of how this mcu DAC module works. In any case, I need some help or advice to get this thing going again. Thank you in advance

The code might look really long and complicated, but you just have to look at DAC_Setup and DAC_execution functions as those are just a few lines each.

 

 

 

#include "main.h" #include <stdio.h> uint8_t received_character; void Core_Clock_Setup (void){ RCC->CR |= RCC_CR_HSEON; //Set the clock source to external crystal/resonator (HSE) while (!(RCC->CR & RCC_CR_HSEON)); //Wait until clock gets stable RCC->APB1ENR |= RCC_APB1ENR_PWREN; //Enable power interface clock PWR->CR1 &= ~(1U << 14); PWR->CR1 &= ~(1U << 15); //Set internal voltage regulator to is reset value (scale 1) FLASH->ACR &= ~FLASH_ACR_ARTEN; //Disable ART accelerator FLASH->ACR &= ~FLASH_ACR_ARTRST; //Reset ART accelerator FLASH->ACR |= FLASH_ACR_PRFTEN; //Enable prefetch FLASH->ACR |= FLASH_ACR_LATENCY_6WS; //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock) //@ 25 MHz crystal, 200 MHz core clock configuration down below (200 MHZ verified as output on MCO2) RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4); RCC->CFGR &= ~(1 << 4); //Core clock division by 1 (core clock is not devided) RCC->CFGR &= ~(1 << 5); RCC->CFGR &= ~(1 << 6); RCC->CFGR &= ~(1 << 7); RCC->CFGR &= ~(1 << 29); RCC->CFGR &= ~(1 << 30); //Set MCO2 as SYSCLK, no division RCC->CFGR &= ~(1 << 31); RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry RCC->PLLCFGR &= ~(1 << 16); //PLLP Setting corresponding PLL prescalers (division by 2) RCC->PLLCFGR &= ~(1 << 17); RCC->PLLCFGR &= ~((1 << 6) - 1); RCC->PLLCFGR |= (16 & ((1 << 6) - 1)); //PLLM Setting corresponding PLL prescalers (division by 16) //RCC->PLLCFGR |= (16 << 0); RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6); RCC->PLLCFGR |= (256 << 6); //PLLN Setting corresponding PLL prescalers ( multiplication by 256) RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz) RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz) RCC->CR |= RCC_CR_PLLON; //Enable PLL while (!(RCC->CR & RCC_CR_PLLRDY)); //Wait until PLL gets stable RCC->CFGR |= RCC_CFGR_SW_PLL; //PLL is set to be core clock //RCC->CFGR |= RCC_CFGR_SW_HSE; //HSE is set to be core clock while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source } //---------------------------------------------------------------------------------------------------------------------------------------------------------------------- void Timer1_Setup(void){ //16 bit advanced timer RCC->DCKCFGR1 &= ~ (1 << 24); //TIMxCLK = 2xPCLKx //When TIMPRE bit of the RCC_DCKCFGR1 register is reset, if APBx prescaler is 1, then TIMxCLK = PCLKx, otherwise //TIMxCLK = 2x PCLKx. // When TIMPRE bit in the RCC_DCKCFGR1 register is set, if APBx prescaler is 1,2 or 4, then TIMxCLK = HCLK, otherwise //TIMxCLK = 4x PCLKx. //TIM1 CLK is HCLK in this case RCC->APB2ENR |= (1 << 0); //Enable Timer 1 clock TIM1->PSC = 99; //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ TIM1->ARR = 0xFFFF; //Auto reload at 100 ticks -> around 100 micro seconds at 100 MHz timer clock TIM1->CR1 |= (1 << 0); //Enable Timer 1 counter while(!(TIM1->SR & (1<<0))); //Wait until timer update bit is set } void delay_ms (uint32_t ms){ SysTick->LOAD = (200000-1); // Configure for milisecond delay SysTick->CTRL |= (1<<2); // Select Processor Clock for blocking delay function SysTick->CTRL |= (1<<0); // Counnter Enable SysTick->VAL = 0; for(int i =0; i<ms ; i++) { while ((SysTick->CTRL & (1<<16)) == 0); } SysTick->CTRL &= ~(1<<0); // Counnter Disable } void delay_us (uint32_t us){ SysTick->LOAD = (200-1); // Configure for milisecond delay SysTick->CTRL |= (1<<2); // Select Processor Clock for blocking delay function SysTick->CTRL |= (1<<0); // Counnter Enable SysTick->VAL = 0; for(int i =0; i<us ; i++) { while ((SysTick->CTRL & (1<<16)) == 0); } SysTick->CTRL &= ~(1<<0); // Counnter Disable } //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void GPIO_Setup(void){ RCC->AHB1ENR |= (1 << 2); //Enable clock for GPIO bank C RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E delay_ms(1); //SYSCLK Output at PC9 GPIOC->AFR[1] |= (0b0 << 4); //Select alternate function as MCO2 on pin PC9 GPIOC->OSPEEDR |= (0b11 << 18); //PC9 very high GPIO speed GPIOC->MODER |= (0b10 << 18); //PC19 Assign alternate function //PC14 OUTPUT GPIOC->MODER |= (0b01 << 28); //PC14 General purpose output mode GPIOC->OTYPER &= ~ (1 << 14); //PC14 Output push-pull (reset state) GPIOC->OSPEEDR |= (0b11 << 28); //PC14 very high GPIO speed GPIOC->PUPDR |= (0b10 << 28); //PC14 pull down resistors //PC15 OUTPUT GPIOC->MODER |= (0b01 << 30); //PC15 General purpose output mode GPIOC->OTYPER &= ~ (1 << 15); //PC15 Output push-pull (reset state) GPIOC->OSPEEDR |= (0b11 << 30); //PC15 very high GPIO speed GPIOC->PUPDR |= (0b10 << 30); //PC15 pull down resistors //PE4 OUTPUT GPIOE->MODER |= (0b01 << 8); //PE4 General purpose output mode GPIOE->OTYPER &= ~ (1 << 4); //PE4 Output push-pull (reset state) GPIOE->OSPEEDR |= (0b11 << 8); //PE4 very high GPIO speed GPIOE->PUPDR |= (0b10 << 8); //PE4 pull down resistors //PE0 OUTPUT GPIOE->MODER |= (0b01 << 0); //PE0 General purpose output mode GPIOE->OTYPER &= ~ (1 << 0); //PE0 Output push-pull (reset state) GPIOE->OSPEEDR |= (0b11 << 0); //PE0 very high GPIO speed GPIOE->PUPDR |= (0b10 << 0); //PE0 pull down resistors //----------------------------------------------------------------------------------- //PE5 INPUT ALL BUTTONS EXTERNALLY PULLED UP GPIOE->MODER |= (0b00 << 10); //PE5 General purpose input mode //PE6 INPUT GPIOE->MODER |= (0b00 << 12); //PE6 General purpose input mode } void UART1_Setup(){ RCC->AHB1ENR |= (1 << 0); //Enable clock for GPIO bank A RCC->APB2ENR |= (1 << 4); //USART1 clock enabled //TX1 at pin PA9 and RX1 at pin PA10 GPIOA->AFR[1] |= (0b0111 << 4); //Select alternate function as UART on pin PA9 GPIOA->AFR[1] |= (0b0111 << 8); //Select alternate function as UART on pin PA10 GPIOA->OSPEEDR |= (0b11 << 18); //Highest speed at pin PA9 GPIOA->OSPEEDR |= (0b11 << 20); //Highest speed at pin PA10 GPIOA->PUPDR |= (0b01 << 18); //Pulldown resistor on PA9 GPIOA->PUPDR |= (0b01 << 20); //Pulldown resistor on PA10 GPIOA->MODER |= (0b10 << 18); //Assign alternate function of UART to pin PA9 GPIOA->MODER |= (0b10 << 20); //Assign alternate function of UART to pin PA10 //UART configuration, CR1 and CR2 registers are all zero on reset /* USART1->CR1 = 0x00; //Reset register just in case USART1->CR1 &= ~ (1 << 28); //M[1:0] = 00: 1 Start bit, 8 data bits, USART1->CR1 &= ~ (1 << 12); //M[1:0] = 00: 1 Start bit, 8 data bits, USART1->CR2 &= ~ (1 << 13); //1 Stop bit USART1->CR2 &= ~ (1 << 12); //1 Stop bit USART1->CR1 &= ~ (1 << 10); //Parity control disabled USART1->CR1 &= ~ (1 << 15); //Oversampling by 16 */ USART1->BRR = (USART1->BRR & ~0xFFFF) | (0x364 & 0xFFFF); //fclk/baud rate*2 200MHz / 115200*2 USART1->CR1 |= (1 << 3); //Transmitter is enabled USART1->CR1 |= (1 << 2); //Receiver is enabled USART1->CR1 |= (1 << 0); //USART enable //Receive data interrupt setup RCC->APB2ENR |= (1 << 14); //System configuration controller clock enabled USART1->CR1 |= (1 << 5); //UART1 Receiver buffer not empty interrupt enable NVIC_SetPriority(USART1_IRQn, 0); NVIC_EnableIRQ (USART1_IRQn); } void UART1_Send(uint8_t character){ USART1->TDR = character; //Load data to transmit register while(!(USART1->ISR & (1 << 6))); //Wait until transmission is executed } uint8_t UART1_Receive (void){ uint8_t character; while(!(USART1->ISR & (1 << 5))); //Wait receive buffer fills up character = USART1->RDR; //Read the receive register return character; } void ADC_Setup (void){ //PA1 setup as analog pin GPIOA->MODER |= (0b11 << 2); //Assign analog function to pin PA1 //Clock for GPIOA is already enabled RCC->APB2ENR |= (1 << 8); //ADC1 clock enabled ADC->CCR = (0b01 << 16); //Set ADC clock prescaler (APB2CLK/4) ADC1->CR1 = (0b00 << 24); //Set 12-bit ADC resolution ADC1->CR2 = (1 << 10); //The EOC bit is set at the end of each regular conversion ADC1->SMPR2 = (0b111 << 3); //Sample time set to 480 cycles at 25 MHz ADC1->SQR1 = (0b0000 << 20); //Using only one conversion ADC1->CR2 |= (1 << 0); //Enable ADC module delay_ms(1); } uint16_t AD_Conversion (void){ ADC1->SQR3 = 0; //Reset ADC1->SQR3 |= (1<<0); //First channel conversion on regular sequence ADC1->SR = 0; //Clear status register ADC1->CR2 |= (1 << 30); //Start the conversion while(!(ADC1->SR & (1 << 1))); //Waiting for conversion to execute return (ADC1->DR & 0xFFFF); } void DAC_Setup (void){ //PA4 setup as analog pin for DAC_OUT1 //RCC->AHB1ENR = (1 << 0); //Enable clock for GPIO bank A //Clock for GPIO A is already enabled in the UART setup function GPIOA->MODER |= (0b11 << 8); //Assign analog function to pin PA4 /*Setup down bellow was used to test this pin as a GPIO, and it worked fine GPIOA->MODER |= (0b01 << 8); // General purpose output mode GPIOA->OTYPER &= ~ (1 << 4); // Output push-pull (reset state) GPIOA->OSPEEDR |= (0b11 << 8); // very high GPIO speed GPIOA->PUPDR |= (0b10 << 8); // pull down resistors */ DAC->CR = 0x0; //Reset whole register just for safety //Since whole CR is reset, wave generation is off, output buffer is enabled DAC->CR |= (1 << 2); //Enable trigger DAC->CR |= (0b111 << 3); //Select software as trigger source DAC->CR |= (1 << 0); //Enable DAC1 } void DAC_execution (uint16_t value){ DAC->DHR12R1 = value; //Enter DAC value DAC->SWTRIGR = (1 << 0); //Software trigger is set } void USART1_IRQHandler(void){ //GPIOC->BSRR |= (1 << 14); //uint8_t received_character; //KEYBOARD ECHO code if (USART1->ISR & (1 << 5)){ while(!(USART1->ISR & (1 << 5))); //Wait receive buffer fills up received_character = USART1->RDR; //Read the receive register UART1_Send(received_character); //Echo received data } //delay_ms(10); //GPIOC->BSRR |= (1 << 30); } int main (void){ Core_Clock_Setup(); Timer1_Setup(); GPIO_Setup(); UART1_Setup(); ADC_Setup (); DAC_Setup (); //GPIOC->BSRR = 0; //Initial value (important for background led toggling) while(1){ //Just some random patterns to generate voltages for (int i=0; i<3600; i++){ DAC_execution(i); delay_ms(i); } for (int i=4096; i>260; i--){ DAC_execution(i); delay_ms(i); } for (int i=700; i>1900; i++){ DAC_execution(i); delay_ms(i); } } }
View more

 

 

 




4 REPLIES 4
Saket_Om
ST Employee

Hello @Vilius 

Please refer to the LL DAC examples Projects/STM32F767ZI-Nucleo/Examples_LL/DAC available on STM32Cube firmware F7 and update your DAC functions accordingly.

If your question is answered, please close this topic by clicking "Accept as Solution".

Thanks
Omar
LCE
Principal II

For a simple DC output without DMA, the F7 DAC is quite simple to use.

So check if the DAC peripheral clock is set and enabled, if the GPIO is set correctly.

I suggest to use some UART IO to check registers and change DAC level.

 

void DAC_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* Peripheral clock enable */ __HAL_RCC_DAC_CLK_ENABLE(); /**DAC GPIO Configuration PA4 ------> DAC_OUT1 PA5 ------> DAC_OUT2 */ GPIO_InitStruct.Pin = DAC1_OUTPUT_Pin | DAC2_OUTPUT_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(DAC12_OUTPUT_Port, &GPIO_InitStruct); /* DAC direct register settings */ DAC->CR = 0; /* channel 1, enable only, DC output, no trigger, no DMA */ DAC->CR |= DAC_CR_EN1; } /* DAC 1 set DC value */ uint8_t DAC1_set_DC(uint16_t u16DcVal) { /* check input value */ if( u16DcVal > DAC_VAL_INT_MAX ) { return 1; } /* write data to right aligned 12 bit register */ DAC->DHR12R1 = (uint32_t)u16DcVal; return 0; }
View more

 

>>The code might look really long and complicated..

More sadly, for bare metal, it looks woefully inefficient

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

As @LCE said above, you need to set DAC clock in RCC first. 

Style: Don't use "magic numbers", use symbols from the CMSIS mandated device header. Don't repeatedly RMW registers, calculate a value (by ORing together all fields you want to set) and perform a single write to the register. 

JW