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);
				}

	}

}

 

 

 




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

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;
}

 

>>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