Неправильная реальная скорость передачи данных UART на микроконтроллере stm32f4 при программировании «голого металла» - PullRequest
0 голосов
/ 25 сентября 2018

Я пытаюсь передать массив символов с помощью usart2, напрямую настраивая соответствующие регистры: RCC, GPIO, USART2 и я получаю неправильную скорость передачи данных при измерении с помощью осциллографа (около 8 Кбод при ожидании 9600).

Я кодирую его, используя atollic True Studio 9.0.1 и проект «новый встроенный C», выбирая правильный MCU, stm32F401RE и оставляя все, кроме зонда отладки, по умолчанию.

У меня есть только одинinclude: #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


}

1 Ответ

0 голосов
/ 28 сентября 2018

Что ж, после проверки моего кода и двойной проверки справочного руководства для моего микроконтроллера (RM0368 - справочное руководство для микроконтроллеров stm32f401xB / C / D / E), я понимаю и решаю проблему.Я уточню.

Прежде всего Я не выбрал источник системных часов и предположил, что это был HSI (высокоскоростные внутренние часы), и по какой-то причине это было не такэто был ВШЭ, то есть внешний высокоскоростной генератор.Поэтому я решил, что мне нужна функция синхронизации часов до , чтобы выбрать правильный источник синхронизации .

Во-вторых, я полностью забыл о предварительном масштабировании в APB1 (Advanced Peripheral Bus 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;

}

Инициализация USART2 конфигурирует его как UART с скоростью передачи 9600 бод, когда тактовая частота источника - HSI (16 МГц), а прескалер для APB1 установлен на 1Для получения дополнительной информации о значениях, пожалуйста, обратитесь к справочному руководству:

/*
 * 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 файл инициализации.

...