USART serial port configuration of STM32 -- learning notes

The content of the article is arranged according to the wildfire learning course, just learning records.

Development board: Wildfire STM32F429 Challenger V2
Official firmware library version: stm32f4xx? DSP? Stdperiph? Lib? V1.8.0

1, Select USART

To configure USART, select which USART/UART to configure.
I use STM32F429 chip. From "Table 8. Comparison of USART characteristics" and "table 10. Definition of stm32f427xx and STM32F429xx pins and solder balls" in STM32F4xx Chinese data manual, we can see that:

  • The chip supports four USART s and four UART S.
  • The clocks of USART1 and USART6 are from the APB2 bus clock (the maximum frequency is 90MHz).
  • The clocks of USART2, USART3, UART4, UART5, UART7 and UART8 are from the APB1 bus clock (the maximum frequency is 45MHz).
  • Which GPIO port is the specific USART/UART pin connected to.
    Organize as shown in the following figure (quoted from the tutorial).
    Then it is necessary to see the circuit design of the development board to see which USART/UART is led out through which GPIO port (or directly connect the corresponding GPIO pin with DuPont wire).
    I use USART1 here, mainly to find out which pin TX and RX are connected to.

2, Start coding

1. Declare macro definition (mainly for convenience of modification)

/******************************************************************************/
/*Local macro definition*/
/******************************************************************************/
#Define usart1 ﹐ handle usart1 / * handle to usart1*/
#Define usart1 ﹣ CLK RCC ﹣ apb2periph ﹣ usart1 / * peripheral clock of usart1*/
#Define usart1? Baudrate 115200 / * baudrate of usart1*/

#Define usart1? GPIO? Port gpioa / * port of usart1*/

#Define usart1? TX? Pin GPIO? Pin? 9 / * GPIO send pin of usart1*/
#Define usart1? RX? Pin GPIO? Pin? 10 / * GPIO receive pin of usart1*/
#Define usart1? TX? Source GPIO? Pinsource9 / * GPIO send pin number of usart1*/
#Define usart1? RX? Source GPIO? Pinsource10 / * GPIO receive pin number of usart1*/
#Define usart1 GPIO AF GPIO AF usart1 / * GPIO pin multiplexing function of usart1*/

#Define debug ﹐ USART ﹐ IRQ usart1 ﹐ IRQN / * interrupt number of usart1*/

2. Configure GPIO

(1) Enable GPIO clock

We have known that USART1 is connected to the pin under the GPIOA port, so we need to enable the clock of GPIOA port first.

    /* Initialize GPIO port clock */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
(2) Configure GPIO for RX and TX pins
/**
  * @brief  USART GPIO configuration for
  * @param  nothing
  * @retval nothing
  */
static void gpio_cfg()
{
    /* Configure GPIO for USART1 */
    bsp_gpio_config(USART1_GPIO_PORT, USART1_TX_PIN | USART1_RX_PIN, GPIO_Mode_AF, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_UP);
    
    /* Connect PXx to usartx? TX*/
    GPIO_PinAFConfig(USART1_GPIO_PORT, USART1_RX_SOURCE, USART1_GPIO_AF);
    
    /*  Connect PXx to usartx? RX*/
    GPIO_PinAFConfig(USART1_GPIO_PORT, USART1_TX_SOURCE, USART1_GPIO_AF);
}

Here, I encapsulate the GPIO pin configuration (no more benefits, just habits). The BSP GPIO config function is:

/**
  * @brief  GPIO Configuration function
  * @param  *GPIOx: GPIO port
  * @param  pin:    PIN pin PIN of GPIO to configure
  * @param  mode:   GPIO mode to configure (I / O / multiplexing / analog)
  * @param  speed:  GPIO rate to be configured (2MHz/25MHz/50MHz/100MHz), input mode does not need to be configured with 0
  * @param  otype:  Output type (push pull / open drain), input mode does not need to be configured with 0
  * @param  pupd:   Pin default state (pull up / pull down / float)
  * @retval nothing
  */
void bsp_gpio_config(GPIO_TypeDef* GPIOx, uint32_t pin, GPIOMode_TypeDef mode, GPIOSpeed_TypeDef speed, GPIOOType_TypeDef otype, GPIOPuPd_TypeDef pupd)
{
    GPIO_InitTypeDef GPIO_def;

    GPIO_def.GPIO_Pin = pin; 
    GPIO_def.GPIO_Mode = mode;
    GPIO_def.GPIO_PuPd = pupd;
    GPIO_def.GPIO_Speed = speed;
    GPIO_def.GPIO_OType = otype;
    
    GPIO_Init(GPIOx, &GPIO_def);
}

3. Configure USART

(1) Enable USART clock

From the first part, we can know that the clock of USART1 is from the clock of APB2.
Check the source code of stm32f4xx ﹣ RCC. C and find USART1 to configure the clock through RCC ﹣ apb2periphclockcmd function.

/* USART1_CLK For RCC ﹣ apb2periph ﹣ usart1, defined in the macro definition */
RCC_APB2PeriphClockCmd(USART1_CLK, ENABLE);                             /* Enable USART clock */
(2) Initialize USART configuration (after enabling USART clock, otherwise no response)
    USART_InitTypeDef USART_def;
    
    USART_def.USART_BaudRate = USART1_BAUDRATE;                             /* Configure baud rate */
    USART_def.USART_WordLength = USART_WordLength_8b;                       /* Configuration word length is 8 (data bit + check bit) */
    USART_def.USART_StopBits = USART_StopBits_1;                            /* Configure stop bit as 1 stop bit */
    USART_def.USART_Parity = USART_Parity_No;                               /* Configure check bit not to use check */
    USART_def.USART_HardwareFlowControl = USART_HardwareFlowControl_None;   /* Configure hardware flow control to not use hardware flow */
    USART_def.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                   /* Configure USART mode control to enable both reception and transmission */
    USART_Init(USART1_HANDLE, &USART_def);                                  /* Finish USART initialization configuration */
(3) Enable serial port
/* USART1_HANDLE For USART1, define the */
USART_Cmd(USART1_HANDLE, ENABLE);                                           /* Enable serial port */

4. Enable printf, scanf, getchar and other functions

Here, you can use the above functions by redirecting fputc and fgetc, because the two functions fputc and fgetc are ultimately used for character reading and writing in the above functions. So the final operation can be changed to serial port reading and writing.
You can put it anywhere. I put the code related to USART configuration in a. c file.

/**
  * @brief  Redirect and rewrite fputc function in C function library, and use printf function after resetting
  * @param  ch:Bytes to output
  * @param  *f:useless
  * @retval nothing
  */
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1_HANDLE, (uint8_t)ch);                             /* Send a byte of data to the serial port */
    
    while (USART_GetFlagStatus(USART1_HANDLE, USART_FLAG_TXE) == RESET);    /* Waiting to send */
    
    return (ch);
}

/**
  * @brief  Redirect and rewrite fgetc function in C function library. After rewriting, scanf, getchar and other functions can be used
  * @param  *f:useless
  * @retval A byte read from the serial port
  */
int fgetc(FILE *f)
{
    while (USART_GetFlagStatus(USART1_HANDLE, USART_FLAG_RXNE) == RESET);   /* Waiting for serial port input data */

    return (int)USART_ReceiveData(USART1_HANDLE);
}

5. You can also customize the send function

/**
  * @brief  Send a byte to the serial port
  * @param  *pUSARTx:USART handle to use
  * @param  ch:Bytes to output
  * @retval nothing
  */
void usart_send_byte(USART_TypeDef *pUSARTx, uint8_t ch)
{
    USART_SendData(pUSARTx, ch);                                             /* Send a byte of data to USART */
    
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);          /* Waiting to send data register is empty */
}

/**
  * @brief  Send two bytes to serial port
  * @param  *pUSARTx:USART handle to use
  * @param  ch:Bytes to output
  * @retval nothing
  */
void usart_send_half_word(USART_TypeDef *pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;
    
    temp_h = (ch & 0XFF00) >> 8;                                            /* Take out the high octet */
    temp_l = ch & 0XFF;                                                     /* Take out the lower octet */
    
    USART_SendData(pUSARTx, temp_h);                                        /* Send high octet */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    
    USART_SendData(pUSARTx, temp_l);                                        /* Send low octet */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/**
  * @brief  Send a string to the serial port
  * @param  *pUSARTx:USART handle to use
  * @param  ch:String to output
  * @retval nothing
  */
void usart_send_string(USART_TypeDef *pUSARTx, char *str)
{
    unsigned int k = 0;
    do 
    {
        usart_send_byte(pUSARTx, *(str + k));
        k++;
    } while(*(str + k) != '\0');
    
    while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);               /* Wait for sending to complete */
}

At this point, the USART configuration is basically completed, and the functions that can be realized are as follows

  • Output string to serial port through printf.
  • Read the contents of serial port input through getchar and scanf.

5. Configure interrupt for USART.

(1) Attention points
  • After opening the interrupt, you must rewrite the terminal service function, otherwise it will enter the dead cycle.
  • After the interrupt is enabled, getchar and scanf functions are not normal and cannot be used normally. The reason for this is still unclear.
(2) Enable USART receive interrupt
/* USART1_HANDLE For USART1, define the */
USART_ITConfig(USART1_HANDLE, USART_IT_RXNE, ENABLE);                       /* Enable serial port receive interrupt */
(3) Configure NVIC (nested vector interrupt controller)

Select nested vector interrupt controller group first (do not repeat if it has been configured)

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                             /* Nested vector interrupt controller group selection */

Configure interrupt for USART1

/* USART1_IRQ For usart1 ﹐ IRQN, the */
/* USART1_IRQn Defined in stm32f4xx.h */
nvic_config(USART1_IRQ, 1, 1, ENABLE);                             /* Interrupt configuration */

Functions encapsulated by NVIC config

/**
  * @brief  NVIC Configuration function
  * @param  channel:    Interrupt source
  * @param  preemption: Preemption priority
  * @param  sub:        Sub priority
  * @param  cmd:        Enabling state
  * @retval nothing
**/
void nvic_config(uint8_t channel, uint8_t preemption, uint8_t sub, FunctionalState cmd)
{
    NVIC_InitTypeDef nvic_def;
    
    nvic_def.NVIC_IRQChannel = channel;                                         /* Configure USART as interrupt source */
    nvic_def.NVIC_IRQChannelPreemptionPriority = preemption;                    /* Preemption priority is 1 */
    nvic_def.NVIC_IRQChannelSubPriority = sub;                                  /* Sub priority is 1 */
    nvic_def.NVIC_IRQChannelCmd = cmd;                                          /* Enable interruption */
    
    NVIC_Init(&nvic_def);                                                       /* Initialize configuration NVIC */
}
(4) Override interrupt service function

The interrupt service function must be overridden after the interrupt is turned on, otherwise it will jump into a dead loop.
The interrupt service function must have the same name as the interrupt service function in the startup file (startup ﹣ stm32f429 ﹣ 439xx. S), otherwise it will jump into a dead cycle.

Here we only realize the content of receiving data and sending it again. If we don't use getchar, scanf and other functions, we can also customize buff to receive data processing.

/**
  * @brief  Rewrite the interrupt function of USART1, which must be the same as the function name defined in the vector table in startup ﹣ stm32f429 ﹣ 439xx. S
  * @param  nothing
  * @retval nothing
  */
void USART1_IRQHandler(void)
{
    uint16_t ucTemp;
    
    if(USART_GetITStatus(USART1_HANDLE, USART_IT_RXNE) != RESET) {
        ucTemp = USART_ReceiveData(USART1_HANDLE);
        USART_SendData(USART1_HANDLE, ucTemp);    
    }
}
Published 64 original articles, won praise 106, visited 220000+
Private letter follow

Posted on Tue, 04 Feb 2020 03:14:48 -0500 by kath421