0

我正在尝试使用 usart2 直接配置相关寄存器来传输 char 数组:RCC、GPIO、USART2 并且在使用示波器测量时获得了不好的波特率(预期 9600 时约为 8k 波特。)

我正在使用 atollic True Studio 9.0.1 和一个“新嵌入式 C”项目对其进行编码,选择正确的 MCU,stm32F401RE,并将除调试探针之外的所有内容保留为默认值。

我只有一个包括:#include "stm32f4xx.h"

令我惊讶的是,当我使用 stm32CubeMX 创建项目并生成最小代码,然后用我的“裸机”项目代码替换 main.c 内容时,uart 的时基在示波器上看起来几乎完美(9571 波特)。

是不是很有趣?可能会发生什么?

这是我的代码:

包括和主循环:

#include "stm32f4xx.h"

void UART2_Init(void);
void UART2_Test_TX(void);

int main(void)
{
  int i = 0;

  UART2_Init();

  UART2_Test_TX();

  while (1)
  {
    i++;
  }
}

一个一直发送“U”的简单测试函数:

void UART2_Test_TX(void)
{
    USART_TypeDef * pUSART2;
    pUSART2 = USART2;

    char data[] = "U";


    while(1)
    {
        while(!(pUSART2->SR && (1<<7)))// TXE transmit data register empty
                {
                }
                pUSART2->DR = (uint16_t)data[0];
    }

}

初始化函数:

void UART2_Init(void)
{
    RCC_TypeDef * pRCC;
    pRCC = RCC;

    GPIO_TypeDef * pGPIOA;
    pGPIOA = GPIOA;

    USART_TypeDef * pUSART2;
    pUSART2 = USART2;



    //1. Enable the peripheral clock
    /*
     * The USART2 is connected to the APB1 bus so we have to check here
     * the Reset and Clock Configuration Enable register for APB1APB1_ENR
     *
     * */
    pRCC->APB1ENR |= (1 << 17); // Set the USART2EN bit to enable the clock (RCC_APB1ENR_USART2EN)

    //2. Configure the GPIO PINS related to UART TX and RX
    /*
     *
     * To do this we need to find the alternate function of the pins in a reference table. That is located in the Section4, table 8 of the Data sheet:
     * USART2_RX *PA3, PD6
     * USART2_TX *PA2, PD5
     * Also in the user manual of the board (UM1724) the RX and TX pins accessible from the PC are located in port A. We have a winner.
     *
     * That's good but not enough. PINs MAY HAVE UP TO 16 DIFFERENT FUNCTIONALITIES so we need another table and register to select it (in datasheet table 9)
     *
     * 2.1 So, enable the RCC clock for GPIOA AHB1
     * 2.2 Configure the PINs as alternate function
     * 2.3 Configure or not Internal Pull-up resistor
     * 2.4 select the alternate function Table 9 of datasheet + GPIOA_AFRL, the low pins registers, from 0 to 7
     *
     * */
    pRCC->AHB1ENR |= 1<<0;//RCC_AHB1ENR_GPIOAEN; // 2.1 Enable the source clock for GPIOA

    // configuring pin 2 TX
    pGPIOA->MODER &= ~(0b11<<4); // 2.2 Clear previous configuration in PIN2

    pGPIOA->MODER |= (0b10<<4); // 2.2 Configure PIN2 as alternate function GPIO_Mode_AF

    pGPIOA->AFR[0] &= ~(0b1111<<8); // 2.4 clear the bits in the register ;

    pGPIOA->AFR[0] |= (0b0111 <<8); // 2.4 AF7 for TX pin

    // FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors

    pGPIOA->PUPDR &= ~(0b11<<4);

    pGPIOA->PUPDR |= (0b01 <<4);

    // configuring pin 3 RX
    pGPIOA->MODER &= ~(0b11<<6); // 2.2 Clear previous configuration in PIN3

    pGPIOA->MODER |= (0b10<<6); // 2.2 Configure PIN3 as alternate function GPIO_Mode_AF

    pGPIOA->AFR[0] &= ~(0b1111<<12); // 2.4 clear the bits in the register;

    pGPIOA->AFR[0] |= (0b0111 <<12); // 2.4 AF7 for RX pin

    // FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors

//  pGPIOA->PUPDR &= ~(0b11<<6);

//  pGPIOA->PUPDR |= (0b01 <<6);

    // Note: I would be more efficient to configure all the pins at the same time but we did this way for clarity


    //3. Configure the UART parameters: baudrate, data width, parity, number of stop bits etc
    /*
     * OVERSAMPLE 16
     * Baudrate 115200
     *
     * OVER8 sampling divider
     * 19.3.4 BaudRate = Fck/(16 * USARTDIV) .
     *      USARTDIV = DIV_Mantisa +(DIV_Fraction/ 8 x (2- OVER8))
     *      Fck = 16Mhz (default HSI)
     *
     * data width 8
     * parity None
     * stopbits 1
     * */


    // Configuring baudrate: 115200, real baudrate 115107.913669065. Error 0.08%
    pUSART2->CR1 &= ~(1<<15); // O: Oversample 16 OK1

    pUSART2->BRR &= ~(0xFFFF); // Clear the mantisa and fraction
    pUSART2->BRR |= (104<<4); // Mantisa
    pUSART2->BRR |= (3<<0); //  Fraction
    uint32_t cBRR = pUSART2->BRR;
    //pUSART2->BRR |= (0x9B); // Mantisa and Fraction as Hex OK1

    /*
    pUSART2->CR1 &= ~(1<<12); // 8 bits OK1
    pUSART2->CR1 &= ~(1<<10); // Parity control disable  OK1
    pUSART2->CR2 &= ~(0b11<<12); // 1 stop bits OK1
*/

    //4. Enable the TX engine of UART2 (do we need RX or we can save power?)
    /*
     * UE bit USART enable
     * TE bit Transmit enable
     * TDR Register to output the data
     * */
    pUSART2->CR1 |= (1<<3); // O: Transmit enable


    //5. ENABLE THE USART peripheral Always at the end
    /*
     * Section 19.3.2
     * USART_CR1.UE enable the usart
     * USART_CR1.M number of bits 8,9
     * USART_CR2 number of stops
     * DMA enable...
     *
     * */

    pUSART2->CR1 |= (1<<13); // O: USART enable

    // Here is ready


}
4

1 回答 1

3

好吧,在检查了我的代码并仔细检查了我的微控制器的参考手册(RM0368 是 stm32f401xB/C/D/E MCU 的参考手册)之后,我理解并解决了这个问题。我会详细说明。

首先,我没有选择系统时钟源并假设它是 HSI(高速内部时钟),由于某种原因,它不是 HSE,即外部高速振荡器。所以我决定我需要一个时钟初始化函数选择正确的时钟源

其次,我完全忘记了 APB1(高级外围总线 1)中的预分频器。它将频率除以 4。因此,在时钟初始化函数中,我还将APB1 预分频器配置为已知值,在示例中,除以一。

/* Includes */
#include "stm32f4xx.h"

/* Private macro */
/* Private variables */
/* Private function prototypes */

void CLOCK_Init(void);

void UART2_Init(void);

void UART2_Test_TX(void);

/* Private functions */

/**
**===========================================================================
**
**  Abstract: main program
**
**===========================================================================
*/
int main(void)
{
  int i = 0;

  /* Initialization */

  CLOCK_Init();

  UART2_Init();

/* Test */
  UART2_Test_TX();

  /* Infinite loop */
  while (1)
  {
    i++;
  }
}

时钟的初始化函数将主时钟和 APB1 时钟设置为已知值。所以后面的配置是连贯的:

/* CLOCK_Init
 * System clock source HSI
 * APB1 prescaler 1
 *
 * Register affected: RCC_CFGR
 *
 */
void CLOCK_Init(void)
{

    RCC_TypeDef * pRCC;
    pRCC = RCC;
    RCC_ClocksTypeDef clocks;

    uint32_t cpCFGR;

    uint32_t mask;

    //Setting HSI as system clock
    cpCFGR = pRCC->CFGR;


    // clear the SW1 SW0: Clock Source HSI
    mask = ~((uint32_t)0b011);

    cpCFGR &= mask;

    pRCC->CFGR = cpCFGR;

    // wait till SWS is 00, that is the clock source is HSI
    while(1)
    {
        cpCFGR = pRCC->CFGR>>2;
        if((~(cpCFGR) & (uint32_t)0b010) == (uint32_t)0b010) break;
    }


    /* Since the UART2 is connected to APB1 lets configure
     * the prescaler in a known value, lets say 1
     * */
    //change the preescaler of APB1 from 4 to 1

    cpCFGR = pRCC->CFGR;

    mask = ~((uint32_t)0x00001C00); // RCC_CFGR_PPRE1

    cpCFGR &= mask;

    pRCC->CFGR = cpCFGR;

}

当源时钟为 HSI (16MHz) 且 APB1 的预分频器设置为 1 时,USART2 初始化将其配置为波特率为 9600 波特的 UART。有关这些值的更多信息,请参阅参考手册:

/*
 * UART2_Init
 * Asumming that the source clock is HSI and the APB1 prescaler is 1
 *
 * Register modified:   RCC_APB1ENR,
 *                      GIPIOA_MODER, GPIOA_AFR, GPIOA_PUPDR,
 *                      USART2_CR1, USART2_BRR
 *
 */
void UART2_Init(void)
{
    RCC_TypeDef * pRCC;
    pRCC = RCC;

    GPIO_TypeDef * pGPIOA;
    pGPIOA = GPIOA;

    USART_TypeDef * pUSART2;
    pUSART2 = USART2;


    //1. Enable the peripheral clock
    /*
     * The USART2 is connected to the APB1 bus so we have to check here
     * the Reset and Clock Configuration Enable register for APB1APB1_ENR
     *
     * */

    pRCC->APB1ENR |= (1 << 17); // Set the USART2EN bit to enable the clock (RCC_APB1ENR_USART2EN)


    //2. Configure the GPIO PINS related to UART TX and RX
    /*
     *
     * To do this we need to find the alternate function of the pins in a reference table. That is located in the Section4, table 8 of the Data sheet:
     * USART2_RX *PA3, PD6
     * USART2_TX *PA2, PD5
     * Also in the user manual of the board (UM1724) the RX and TX pins accessible from the PC are located in port A. We have a winner.
     *
     * That's good but not enough. PINs MAY HAVE UP TO 16 DIFFERENT FUNCTIONALITIES so we need another table and register to select it (in datasheet table 9)
     *
     * 2.1 So, enable the RCC clock for GPIOA AHB1
     * 2.2 Configure the PINs as alternate function
     * 2.3 Configure or not Internal Pull-up resistor
     * 2.4 select the alternate function Table 9 of datasheet + GPIOA_AFRL, the low pins registers, from 0 to 7
     *
     * */

    pRCC->AHB1ENR |= 1<<0;//RCC_AHB1ENR_GPIOAEN; // 2.1 Enable the source clock for GPIOA

    // configuring pin 2 TX
    pGPIOA->MODER &= ~(0b11<<4); // 2.2 Clear previous configuration in PIN2

    pGPIOA->MODER |= (0b10<<4); // 2.2 Configure PIN2 as alternate function GPIO_Mode_AF

    pGPIOA->AFR[0] &= ~(0b1111<<8); // 2.4 clear the bits in the register ;

    pGPIOA->AFR[0] |= (0b0111 <<8); // 2.4 AF7 for TX pin

    // FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors

    pGPIOA->PUPDR &= ~(0b11<<4);

    pGPIOA->PUPDR |= (0b01 <<4);

    // configuring pin 3 RX (we are not really going to use this
    pGPIOA->MODER &= ~(0b11<<6); // 2.2 Clear previous configuration in PIN3

    pGPIOA->MODER |= (0b10<<6); // 2.2 Configure PIN3 as alternate function GPIO_Mode_AF

    pGPIOA->AFR[0] &= ~(0b1111<<12); // 2.4 clear the bits in the register;

    pGPIOA->AFR[0] |= (0b0111 <<12); // 2.4 AF7 for RX pin

    // FOR SPI, I2C, UART the lines must be held high. So we need pull_up resistors

    pGPIOA->PUPDR &= ~(0b11<<6);

    pGPIOA->PUPDR |= (0b01 <<6);

    // Note: I would be more efficient to configure all the pins at the same time but I did this way for clarity


    //3. Configure the UART parameters: baudrate, data width, parity, number of stop bits etc
    /*
     * OVERSAMPLE 16
     * Baudrate 9600
     *
     * OVER8 sampling divider
     * 19.3.4 BaudRate = Fck/(16 * USARTDIV) .
     *      USARTDIV = DIV_Mantisa +(DIV_Fraction/ 8 x (2- OVER8))
     *      Fpclk = 16Mhz (default HSI)
     *
     * data width 8
     * parity None
     * stopbits 1
     * */



    // Configuring baudrate: 9600

    pUSART2->CR1 &= ~(1<<15); // O: Oversample 16 OK1

    pUSART2->BRR &= ~(0xFFFF); // Clear the mantisa and fraction

    pUSART2->BRR |= (104<<4); // Mantisa
    pUSART2->BRR |= (3<<0); //  Fraction

/*
    // configuring the baudrate deducting the system clock from the oscilloscope measurement, 13336000Hz
    // The USARTDIV is 86.822916667: 86+13/16
    pUSART2->BRR &= ~(0xFFFF); // Clear the mantisa and fraction
    pUSART2->BRR |= (86<<4); // Mantisa
    pUSART2->BRR |= (13<<0); //  Fraction
    uint32_t cBRR = pUSART2->BRR;
*/

    pUSART2->CR1 &= ~(1<<12); // 8 bits OK1
    pUSART2->CR1 &= ~(1<<10); // Parity control disable  OK1
    pUSART2->CR2 &= ~(0b11<<12); // 1 stop bits OK1


    //4. Enable the TX engine of UART2 (do we need RX or we can save power?)
    /*
     * UE bit USART enable
     * TE bit Transmit enable
     * TDR Register to output the data
     * */
    pUSART2->CR1 |= (1<<3); // O: Transmit enable


    //5. ENABLE THE USART peripheral Always at the end
    /*
     * Section 19.3.2
     * USART_CR1.UE enable the usart
     * USART_CR1.M number of bits 8,9
     * USART_CR2 number of stops
     * DMA enable...
     *
     * */

    pUSART2->CR1 |= (1<<13); // O: USART enable

    // Here is ready.

}

测试功能是相同的,但我结合了old_timer建议来生成串行传输频率一半的方脉冲序列:

/* UART2_Test_TX
 * Sends forever the character U to produce a square train of frec half baudrate
 */
void UART2_Test_TX(void)
{
    USART_TypeDef * pUSART2;

    pUSART2 = USART2;

    char data[] = "U";

    while(1)
    {
        while(!(pUSART2->SR && (1<<7)))// TXE transmit data register empty
        {
            __NOP();
        }

        // Feed the data register with data
        pUSART2->DR = (uint16_t)data[0];
    }

}

我希望这结束了这个问题。如果有人需要进一步澄清,请询问。

我还没有分析时钟配置的由来。我想这与 startup_stm32f40xx.s 初始化文件有关。

于 2018-09-28T01:42:34.633 回答