2024-11-27 09:14 AM
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');
}
}
2024-11-27 10:07 AM - edited 2024-11-27 10:08 AM
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
2024-12-02 05:03 AM
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');
}
}
2024-12-02 05:18 AM
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.
2024-12-02 09:40 AM
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.
2024-12-02 10:06 AM
>>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.
2024-12-02 10:10 AM
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.
2024-12-02 10:35 AM - edited 2024-12-02 10:35 AM
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
2024-12-02 11:55 PM - edited 2024-12-03 12:01 AM
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