首页 > 解决方案 > How to properly configure the USART_BRR register in STM32L476RG uC?

问题描述

I'm trying to write my own driver for USART_TX on an STM32L476RG Nucleo Board. Here the datasheet and the reference manual.

I'm using Keil uVision 5 and I set in the Manage dialog:

I want to create a single character transmitter. According to the manual instructions in Sec. 40 p 1332 I wrote this code:

// APB1 connects USART2
// The USART2 EN bit on APB1ENR1 is the 17th
// See alternate functions pins and label for USART2_TX! PA2 is the pin and AF7 (AFRL register) is the function to be set


#include "stm32l4xx.h"                  // Device header

#define MASK(x) ((uint32_t) (1<<(x)));

void USART2_Init(void);
void USART2_Wr(int ch);
void delayMs(int delay);

int main(void){
    USART2_Init();
    

    while(1){
    USART2_Wr('A');
    delayMs(100);
        
        
    }
}

void USART2_Init(void){
    RCC->APB1ENR1 |= MASK(17); // Enable USART2 on APB1
    // we know that the pin that permits the USART2_TX is the PA2, so...
    RCC->AHB2ENR |= MASK(0); // enable GPIOA
    
    // Now, in GPIOA 2 put the AF7, which can be set by placing AF7=0111 in AFSEL2 (pin2 selected)
    // AFR[0] refers to GPIOA_AFRL register
    // Remember: each pin asks for 4 bits to define the alternate functions. see pg. 87 
    // of the datasheet
    GPIOA->AFR[0] |= 0x700; 
    GPIOA->MODER &= ~MASK(4);// now ... we set the PA2 directly with moder as alternate function "10"
    
    // USART Features -----------
    
    //USART2->CR1 |=MASK(15); //OVER8=1
    USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?
    //USART2->BRR = 0x1A1; //This one works!!!
    USART2->CR1 |=MASK(0); //UE
    USART2->CR1 |=MASK(3); //TE
    
}

void USART2_Wr(int ch){
    //wait when TX buffer is empty
    while(!(USART2->ISR & 0x80)) {} //when data is transfered in the register the ISR goes 0x80.
        //then we lock the procedure in a while loop until it happens
        USART2->TDR =(ch & 0xFF); 

}

void delayMs(int delay){
int i;
    for (; delay>0; delay--){
        for (i=0; i<3195; i++);
    }
}

Now, the problem:

The system works, but not properly. I mean: if I use RealTerm at 9600 baud-rate, as configured by 0x683 in USART_BRR reg, it shows me wrong char but if I set 2400 as baud rate on real term it works!

To extract the 0x683 in USART_BRR reg i referred to Sec. 40.5.4 USART baud rate generation and it says that if OVER8=0 the USARTDIV=BRR. In my case, USARTDIV=16MHz/9600=1667d=683h.

I think that the problem lies in this code row:

USART2->BRR = 0x683; //USARTDIV=16Mhz/9600?

because if I replace it as

USART2->BRR = 0x1A1; //USARTDIV=16Mhz/9600?

THe system works at 9600 baud rate.

What's wrong in my code or in the USARTDIV computation understanding?

Thank you in advance for your support.

Sincerely, GM

标签: cembeddedmicrocontrollerstm32bare-metal

解决方案


USART 的默认时钟源是PCLK1(图 15 PCLK1SYSCLK / AHB_PRESC / AHB1_PRESC。如果0x1A1波特率为 9600,则表明PCLK1= 4MHz。

当从内部 MSI RC 振荡器运行时,4MHz 恰好是您的处理器(和PCLK1)启动时的默认频率。所以最可能的解释是您没有配置时钟树,并且没有像您认为的那样从 16MHz HSE 运行。

将时钟树配置为使用 16MHz 源,或对 MSI 频率执行计算。MSI 精度在正常温度范围内几乎足够好,可以保持足够准确的波特率,但并不理想。


推荐阅读