2020-04-25 09:26 PM
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));
}
}
}
2020-04-25 09:50 PM
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
2020-04-26 12:17 AM
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
2020-04-26 02:04 AM
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.
2020-04-26 10:24 AM
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
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... ;)