cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F411RE Unable to make UART work.

IRoja.1
Associate

Hello guys.

I've been trying to learn how to program STM32 MCU's using registers.

This time, I've attempted to configure a basic UART programm in which I would like to send whatever I receive via UART back to the source. Basically an echo program.

I'm interfacing a STM32F411RE on a nucleo board with a USB RS232 module towards putty. Nonetheless, no matter what I type on my PC, the MCU does not appear to be sending that data back to the computer

I've tried setting the system clock to 100 MHz by configuring the following parameters for clocking:

*Internal 16MHz oscilator used as clocking source

*PLL M factor = /8

*PLL N factor= x100

*PLL P factor= /2

*PLL Q factor= /4

*AHB Prescaler: 1

*APB1 Prescaler: /2

*APB2 Prescaler: /1

*Calculations are made in order to set baud rate to 9600

I'm including my code here below. I've tried debugging it using Keil's debugging feature. I've noticed that the GPIOA and GPIOB clocks do not seem to be enabled after the MCU runs the following two lines: (In other words, both registers stay at 0 eventhough I'm setting them to 1) 

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

I don't know if the fact I'm modifying clocking has something to do with this or if this is just a stetic issue with the debugger

If you do find something off with my code, please let me know

Thank you very much in advance.

#include "stm32f4xx.h"
/*
 
STM32F411 UART 
 
PINS FOR UART
 
Port A_15 ->>>> UART1_TX
Port B_7  ->>>> UART1_RX
 
*/
 
 
int main(){
	
	
// TODO Configure main clock(?) to run at 100 MHz
	
		//Shut PLL Off
	  RCC->CR &= ~RCC_CR_PLLON;
	
		//Set PLL Source as HSI
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLSRC;
	
		//Set PLL M value to 8 in dec
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_5; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_4; //0
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_3;  //1
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_2; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_1; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLM_0; //0
	
		//Set PLL P Value to 0 in DEC. Datasheet states 0 equals 2
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLP_0;
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLP_0;
		
		
		//SET PLL Q Value to 4 in DEC
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLQ_3;
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLQ_2;
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLQ_1;
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLQ_0;
		
	
		//Set PLL N value to 100 in dec
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_8; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_7; //0
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_6;  //1
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_5;  //1
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_4; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_3; //0
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_2;  //1
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_1; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_0; //0
		
		
		
		//Set APB2 Values TO 2
		RCC->CFGR |= RCC_CFGR_PPRE2_2; //1
		RCC->CFGR &= ~RCC_CFGR_PPRE2_1; //0
		RCC->CFGR &= ~RCC_CFGR_PPRE2_0; //0
		
		//Set APB1 Values TO 1 or No Div
		RCC->CFGR &= ~RCC_CFGR_PPRE1_2; //0
		//RCC->CFGR |= RCC_CFGR_PPRE1_1; //Datasheet marks this as X when div is set to 1
		//RCC->CFGR |= RCC_CFGR_PPRE1_0; //Datasheet marks this as X when div is set to 1
		
		//Set AHB Values to 1 or No Div
		RCC->CFGR &= ~RCC_CFGR_HPRE_3; //0
		//RCC->CFGR |= RCC_CFGR_HPRE_2;  //Datasheet marks this as X when div is set to 1
		//RCC->CFGR |= RCC_CFGR_HPRE_1;  //Datasheet marks this as X when div is set to 1
		//RCC->CFGR |= RCC_CFGR_HPRE_0;  //Datasheet marks this as X when div is set to 1
		
		
		//Set PLL as System Clock. "10" Sets PLL as System Clock
		RCC->CFGR |= RCC_CFGR_SW_1;
		RCC->CFGR &= ~RCC_CFGR_SW_0;
		
		//Enable HSI
		RCC->CR |= RCC_CR_HSION;
		while(!(RCC->CR  &  RCC_CR_HSIRDY));
		
		//Re-enable PLL
		RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR   &   RCC_CR_PLLRDY));
	
			
//Enable clock for ports
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
	
 
//Enable clock for USART
	RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
 
//Define what alternate function will be used
/*	
	AF7 = UART
	0111: AF7
	Port A_15 ->>>> UART1_TX
  Port B_7  ->>>> UART1_RX
	
	For each GPIO pins 0 through 7 are set in AFR[0] which is called GPIOx_AFRL 
 For GPIO pins 8 through 15 set the corresponding 4 bits for the desired pin in AFR[1] which is called GPIOx_AFRH on the next page in the RM.
	
*/	
	GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH7_3;
	GPIOA->AFR[1] |= GPIO_AFRH_AFRH7_2;
	GPIOA->AFR[1] |= GPIO_AFRH_AFRH7_1;
	GPIOA->AFR[1] |= GPIO_AFRH_AFRH7_0;
	
	
	
	GPIOB->AFR[0] &= ~GPIO_AFRL_AFRL7_3;
	GPIOB->AFR[0] |= GPIO_AFRL_AFRL7_2;
	GPIOB->AFR[0] |= GPIO_AFRL_AFRL7_1;
	GPIOB->AFR[0] |= GPIO_AFRL_AFRL7_0;
	
	
	
 
	
	
//Define pins as alternate function pins
	GPIOA->MODER |= GPIO_MODER_MODE15_1;
	GPIOA->MODER &= ~GPIO_MODER_MODE15_0;
	
	GPIOB->MODER |= GPIO_MODER_MODE7_1;
	GPIOB->MODER &= ~GPIO_MODER_MODE7_0;
	
  // TODO Define Baud Rate
	 //Considering a system clock of 100 MHz
	USART1->BRR = 0x28B1;
 
 
	//Enable RX, TX and UART itself
	USART1->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
	
	
	
		//Check if anything has been received using the SR RXNE flag
		while(1)
		{
			if(USART1->SR & USART_SR_RXNE)
				{
					//Take what we got
					char payload = USART1->DR;
					
					//Send it back
					USART1->DR = payload;
					
					//Wait until data is sent
					while(!(USART1->SR & USART_SR_TC));
				}
		}
}
 
 

4 REPLIES 4
IRoja.1
Associate

Just a quick edit. The program works fine if I comment out all the clock config stuff and leave the MCU running at 16 MHz by default and change the USARTDIV value to 0x683

You need to set FLASH waitstates before you switch to a higher clock, in FLASH_ACR.LATENCY. Read the Relation between CPU clock frequency and Flash memory read time chapter in RM.

JW

Nikita91
Lead II

If you are new to STM32 it is good to read some examples.

There is a feature of CubeMX which is very useful: the generation of the SystemClock_Config () function. You will see the subtleties concerning the Flash and the bus frequency changes.

In CubeMX create a project for your MCU. In RCC possibly validate HSE. Then configure your clock graphically with "Clock Configuration". Then select LL for the RCC (in Project Manager / Advanced Settings). Once the project generated you will see a function very close to what you wrote, and most of the time without error.

Piranha
Chief II

Coming up with the idea of defining separate single bits of register fields probably is the best proof of the fact that ST's software developers are dumb code monkeys - people, who are able of typing long useless texts, but not capable of thinking.

		//Set PLL N value to 100 in dec
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_8; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_7; //0
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_6;  //1
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_5;  //1
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_4; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_3; //0
		RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_2;  //1
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_1; //0
		RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLN_0; //0

Don't use that stupidity. Instead use this with a macro from ARM:

RCC->PLLCFGR = (RCC->PLLCFGR & ~RCC_PLLCFGR_PLLN) | _VAL2FLD(RCC_PLLCFGR_PLLN, 100);

https://www.keil.com/pack/doc/cmsis/Core/html/group__peripheral__gr.html

https://github.com/STMicroelectronics/STM32CubeF4/blob/5d01400afd60410f6e049cbd19179a67d44d53fd/Drivers/CMSIS/Include/core_cm4.h#L1527

Or here is even more elegant bonus feature from me:

#define REG_MOD_FIELD(rReg, xFld, xVal)    ( (rReg) = ((uint32_t)(rReg) & ~xFld##_Msk) | (((uint32_t)(xVal) << xFld##_Pos) & xFld##_Msk) )
#define REG_MOD_BITS(rReg, fClr, fSet, iPos)    ( (rReg) = ((uint32_t)(rReg) & ~((uint32_t)(fClr) << (iPos))) | ((uint32_t)(fSet) << (iPos)) )
 
REG_MOD_FIELD(RCC->PLLCFGR, RCC_PLLCFGR_PLLN, 100);

I'll leave sorting these out it to you... 😉