cancel
Showing results for 
Search instead for 
Did you mean: 

Problem setting up UART on STM32F756VGH6

Vilius
Associate III

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

}

 

8 REPLIES 8

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

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

}

 

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.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
Vilius
Associate III

1.png

I did not quite understand your main observation. What is the correct way to set up UART pins then? Could you provide a few lines of exact code (regarding only the relevant part about UART setup)? Thank you once again.

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

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

Enable CLOCKs first, otherwise the peripheral registers will read zero and not be modified.

Generally don't assume initial content and settings. Just saves a lot of headaches and bemusement later.

Use the Debugger, understand what your code is/should be doing, confirm that by inspecting the registers and stepping it.

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

Assuming that this is after reset, with reset values given in what you've quoted from the RM:

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

i.e. only one RMW (and in this case, not direct write which would clear other fields in the same register). Alternatively, you'd not even RMW, but perform *one* direct write, explicitly setting also fields for the SWD pins:

GPIOA->MODER = 0
| (0b10 << (2 * 9)) //Assign alternate function of UART to pin PA9
| (0b10 << (2 * 10)) //Assign alternate function of UART to pin PA10

  | (0b10 << (2 * 13)) //need to preserve AF also to SWDIO
| (0b10 << (2 * 14)) //and SWCLK
 // | (0b10 << (2 * 15)) // unlikely that you need also JTDI
;

 

JW

Ok, looks like the problem is solved so far. The problem was incorrect writing to APB2ENR register that messed up timer1 clock, that is the source of all delays to leds