Skip to main content
Visitor II
November 27, 2024
Question

Problem setting up UART on STM32F756VGH6

  • November 27, 2024
  • 3 replies
  • 1903 views

Hi,

I am learning and exploring the STM32F756VGH6. My goal is to setup a simple UART and send some data to the PC using USB-UART serial converter and  puTTy terminal on PC. I am using register based coding approach as it makes the code more efficient. However, the code provided down bellow (that I have written) is faulty. I strongly suspect the problem lies somewhere in my UART setup function (but I could not figure out where exactly), because if I omit it, main loops gets executed (it does not even jam in the UART_Send function even though the UART has not been initialized) and I see the background leds blinking. If I try to execute the UART setup function, the led turns on and it stays like that indefinitely, needless to say I do not get any signals on my TX/RX lines, those always stay at high level (I have checked it with a logic analyzer). Both TX/RX lines have a 5.1k pullups to +3.3V. I have written some simple codes for my current setup and it worked fine, so hardware issue is very unlikely. Any ideas to get this code moving? Thank you in advance.

 

#include "main.h"

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

	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->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 (uint16_t ms){
	TIM1->CR1 = (1 << 0);
	for(uint16_t i = 0; i<ms; i++)
	{
		TIM1->CNT = 0; //Reset counter
		while (TIM1->CNT < 2000); //Wait until counter reaches desired value
	}
	TIM1->CR1 &= ~(1 << 0);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){

	//PC14 OUTPUT //LEDS

	RCC->AHB1ENR |= (1 << 2);				 //Enable clock for GPIO bank C
	RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E

	delay_ms(1);

	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->APB2ENR = (1 << 4); 		 //USART1 clock enabled
	//TX1 at pin PA9 and RX1 at pin 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

	GPIOA->OSPEEDR = (0b11 << 18);		 	 //Highest speed at pin PA9
	GPIOA->OSPEEDR = (0b11 << 20);			 //Highest speed at pin PA10

	GPIOA->PUPDR = (0b00 << 18);		 	 //No pullup, no pulldown resistor on PA9
	GPIOA->PUPDR = (0b00 << 20); //No pullup, no pulldown resistor on 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

	RCC->AHB1ENR |= (1 << 0); //Enable clock for GPIO bank A

	USART1->CR1 = 0x00; //Reset register just in case
	USART1->CR1 = (0b00 << 28); //M[1:0] = 00: 1 Start bit, 8 data bits, n stop bits
	USART1->CR1 &= ~ (1 << 15); //Oversampling by 16

	USART1->BRR = 0x6C8; //fclk/baud rate 200MHz / 115200

	USART1->CR1 = (1 << 3); //Transmitter is enabled
	USART1->CR1 = (1 << 2); //Receiver is enabled

	RCC->APB2ENR = (1 << 4); 		 //USART1 clock enabled
	USART1->CR1 = (1 << 0); //USART enable
	}

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;

}

int main (void){

	Core_Clock_Setup();
	Timer1_Setup();
	GPIO_Setup();
	UART1_Setup();

	GPIOC->BSRR = 0x4000;
	GPIOC->BSRR = (1 << 30);

	while(1){

		GPIOC->BSRR = (1 << 15);
		delay_ms(750);
		UART1_Send('S');
		GPIOC->BSRR = (1 << 31);
		delay_ms(750);
		UART1_Send('S');
	}

}

 

    This topic has been closed for replies.

    3 replies

    Super User
    November 27, 2024

    Enable UART and GPIO clock in RCC *before* you write into its registers. 

    Also, you are writing several times into the same register, overwriting is perhaps content. 

    Observing content of registers (carefully, e.g. reading UART DR clears the RXNE flag) is helpful in understanding what happens in the hardware. 

    JW

    ViliusAuthor
    Visitor II
    December 2, 2024

    This is my latest code down bellow (like you said, I enabled the clocks earlier). Sadly it did not change anything (background led is still not blinking). I tried to comment the UART setup function line by line and flash the code each time to track down the problem. This wasn successful, because problems start to occur wtih enabling the clock for GPIO A bank. Needless to mention UART isnt working either... Any ideas?

    #include "main.h"
    
    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
    
    	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->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 (uint16_t ms){
    	TIM1->CR1 = (1 << 0);
    	for(uint16_t i = 0; i<ms; i++)
    	{
    		TIM1->CNT = 0; //Reset counter
    		while (TIM1->CNT < 2000); //Wait until counter reaches desired value
    	}
    	TIM1->CR1 &= ~(1 << 0);
    }
    //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    void GPIO_Setup(void){
    
    	//PC14 OUTPUT //LEDS
    
    	RCC->AHB1ENR |= (1 << 2);				 //Enable clock for GPIO bank C
    	RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E
    
    	delay_ms(1);
    
    	//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
    
    	delay_ms(1);
    
    	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->OTYPER &= ~ (1 << 9);			 //Open drain
    	GPIOA->OTYPER &= ~ (1 << 10);			 //Open drain
    
    	GPIOA->OSPEEDR = (0b11 << 18);		 	 //Highest speed at pin PA9
    	GPIOA->OSPEEDR = (0b11 << 20);			 //Highest speed at pin PA10
    
    	GPIOA->PUPDR = (0b00 << 18);		 	 //No pullup, no pulldown resistor on PA9
    	GPIOA->PUPDR = (0b00 << 20); //No pullup, no 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
    	//--------------------------
    	USART1->CR1 = 0x00; //Reset register just in case
    	USART1->CR1 = (0b00 << 28); //M[1:0] = 00: 1 Start bit, 8 data bits, n stop bits
    	USART1->CR1 &= ~ (1 << 15); //Oversampling by 16
    
    	USART1->BRR = 0x6C8; //fclk/baud rate 200MHz / 115200
    
    	USART1->CR1 = (1 << 3); //Transmitter is enabled
    	USART1->CR1 = (1 << 2); //Receiver is enabled
    
    	USART1->CR1 = (1 << 0); //USART enable
    }
    
    
    
    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;
    
    }
    */
    int main (void){
    
    	Core_Clock_Setup();
    	Timer1_Setup();
    	GPIO_Setup();
    	UART1_Setup();
    
    	while(1){
    
    		GPIOC->BSRR |= (1 << 15);
    		delay_ms(100);
    		UART1_Send('S');
    		GPIOC->BSRR |= (1 << 31);
    		delay_ms(100);
    		UART1_Send('H');
    	}
    
    }
    

     

    Graduate
    December 2, 2024

    1. Use bit names defined in MCU header file instead of magic numbers.

    2. The main problem is here:

    GPIOA->MODER = (0b10 << 18);		 	 //Assign alternate function of UART to pin PA9
    	GPIOA->MODER = (0b10 << 20);			 //Assign alternate function of UART to pin PA10

    The second line sets TX to GPIO input, disabling its UART function. BTW this also disables debug interface, so you won't be able to connect the debugger without driving the reset input.

    3. In your code there are many unnecessary logic operation on registers, but you are missing the few necessary ones.

    Do not perform logic operations on BSRR & BRR - use assignments for these.

    Graduate II
    December 2, 2024

    >>I am using register based coding approach as it makes the code more efficient. 

    Yeah, No..

    Doing repetitive, unfoldable, RMW actions on the registers in a Load-Store architecture is NOT EFFICIENT

    Hold the data in processor registers, apply all the logic to those, and then store them in the peripheral registers, in the least number of moves to meet the sequence/ordering requirements.

    You also need to enable the peripheral clocks before the peripheral registers work, so do that FIRST, and some multi-cycle dwell on the slower busses so the peripheral is ready to accept configuration.

     

    Would suggest starting by getting the GPIO and UART working at the default speed the MCU starts off with on the HSI, and then migrate to HSE/PLL onces you have the IO working.

    Super User
    January 21, 2025

    @Vilius wrote:

    I am learning and exploring the STM32F756VGH6 ... I am using register based coding approach as it makes the code more efficient. 


    Apart from the fallacy already noted by @Tesla DeLorean, trying to optimise while learning is probably not a great approach.

    As the famous saying goes,

    "premature optimisation is a root of all kinds of evil"