在STM32里,USART负责进行串口通信。STM32可以通过串口和其他设备进行传输并行数据,是全双工、异步时钟控制,设备之间进行的是点对点的数据传输。对应的STM32引脚分别是RX(接收端)和TX(发送端)。STM32的USART串口资源有USART1、USART2、USART3。

串口有以下几个几个重要的参数:

1、波特率,串口通信的速率。
2、空闲,即没有信号传输的时候的电平位,一般为高电平。
3、起始位,标志一个数据帧的开始,固定为低电平。当数据开始发送时,产生一个下降沿。(空闲位为高电平,当条约到起始位的低电平,会产生一个下降沿触发信号)
4、数据位,发送数据帧,1为高电平,0为低电平。低位先行。
比如 发送数据帧0x0F 在数据帧里就是低位线性 即 1111 0000
5、校验位,用于数据验证,根据数据位的计算得来。有奇校验,偶校验和无校验。

如果是采用奇校验,在传送每一个字节的时候另外附加一位作为校验位,当实际数据中“1”的个数为偶数的时候,这个校验位就是“1”,否则这个校验位就是“0”,这样就可以保证传送数据满足奇校验的要求。在接收方收到数据时,将按照奇校验的要求检测数据中“1”的个数,如果是奇数,表示传送正确,否则表示传送错误。同理偶校验的过程和奇校验的过程一样,只是检测数据中“1”的个数为偶数。

如0100101偶校验码就是10100101,当实际数据中“1”的个数为偶数的时候,这个校验位就是“0”,否则这个校验位就是“1”,这样就可以保证传送数据满足偶校验的要求。

STM32cubemx串口收发_嵌入式硬件


6、停止位,用于数据的间隔,固定为高电平。数据帧发送完成后,产生一个上升沿。(从数据传输的起始低电位变为空闲的高电位,产生一个上升沿触发信号)

 

下方就是一个字节数据的传输过程,从图中可以看出,串口发送的数据一般都是以数据帧的形式进行传输,每个数据帧都由起始位,数据位,停止位组成, 且停止位可变。

STM32cubemx串口收发_stm32_02

UART是通用异步收发器,USART是通用同步异步收发器,一般来说,在单片机上,名为UART的接口只能用于异步串行通信,而名为USART的接口既可用于同步串行通信,也可用于异步串行通信。

接下来介绍相应的代码文件。

首先是uart.c头文件:

#ifndef __USART_H
#define __USART_H

#include "./SYSTEM/sys/sys.h"
#include "./BSP//DELAY/delay.h"
#include "./BSP/LED/led.h"

extern UART_HandleTypeDef g_uart1_handle;
extern uint8_t g_rx_buffer[1];
extern uint8_t g_usart1_rx_flag;

void usart_init(uint32_t baudrate);
void USART1_IRQHandler(void);
void HAL_MspInit(UART_HandleTypeDef *haurt);

#endif

我们要使用extern关键字,声明该变量在.c文件里已经定义过了,在头文件引用,使之能在主函数中使用。

extern UART_HandleTypeDef g_uart1_handle;是对UART串口的初始化结构体。

uint8_t g_rx_buffer[1]是储存我们需要传输的字节。

uint8_t g_usart1_rx_flag检验我们是否发送了数据。
HAL_xx_MspInit:将外设底层的相关资源初始化完成,如时钟、使用到的引脚等。

接下来再编写uart.c文件:

#include "./BSP/UART/uart.h"

UART_HandleTypeDef g_uart1_handle;
uint8_t g_rx_buffer[1];
uint8_t g_usart1_rx_flag;

void usart_init(uint32_t baudrate){

    g_uart1_handle.Instance = USART1;
    g_uart1_handle.Init.Baudrate = baudrate;
    g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;
    g_uart1_handle.Init.StopBits = UART_STOPBITS_1;
    g_uart1_handle.Init.Parity = UART_PARITY_NONE;
    g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    g_uart1_handle.Init.Mode = UART_MODE_TX_RX;

    HAL_UART_Init(&g_uart_handle);
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1);
}

void HAL_UART_MspInit(UART_HandleTypeDef *haurt){

    GPIO_InitTypeDef gpio_init_struct;
    if(haurt->Instance == USART1){
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();

        gpio_init_struct.Pin = GPIO_PIN_9;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);

        gpio_init_struct.Pin = GPIO_PIN_10;
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);

        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);
        HAL_NVIC_EnableIRQ();

    }
}

void USART1_IRQHandler(void){

    HAL_UART_IRQHandler(&g_uart1_handle);
}

UART_HandleTypeDef是usrt串口初始化的结构体,源码如下:

typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                 *Instance;        /*!< UART registers base address        */

  UART_InitTypeDef              Init;             /*!< UART communication parameters      */

  uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */

  uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */

  __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter           */

  uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */

  uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */

  __IO uint16_t                 RxXferCount;      /*!< UART Rx Transfer Counter           */

  DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */

  DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */

  HAL_LockTypeDef               Lock;             /*!< Locking object                     */

  __IO HAL_UART_StateTypeDef    gState;           /*!< UART state information related to global Handle management
                                                       and also related to Tx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */

  __IO HAL_UART_StateTypeDef    RxState;          /*!< UART state information related to Rx operations.*/

} UART_HandleTypeDef;

我们主要关注这几个结构体成员:

1.Instance:选择的USART串口号;

2.Init.Baudrate:波特率,串口通信的速率,这里我们在初始化函数中会传入相应的波特率参数。

3.Init.WordLength = UART_WORDLENGTH_8B:传输数据大小,我们这里选用8位长的数据传输大小,也就是说我们一次只传输一个char大小的数据。

4.Init.StopBits=UART_STOPBITS_1:停止位,有无/1位/2位,这里我们采用一位停止位。

5.Init.Parity:奇偶校验位,这里我们不采用校验位。

6.Init.HwFlowCtl:设置硬件流控是否使能或禁能,这里我们关闭。

7.Init.Mode=UART_MODE_TX_RX:UART采用的模式,我们这里采用输入输出模式。

这里我们需要共用PA9,PA10两个串口,初始化GPIOA后再设置中断优先级。

HAL_UART_Receive_IT是串口接收中断函数。它用于在接收到数据时进行中断处理。在函数中包含了接收数据的读取和相关的数据处理。

最后我们再编写主函数main.c:

#include "./SYSTEM/sys/sys.h"
#include "./BSP/UART/uart.h"

int main(){
    HAL_Init();
    sys_stm32_clock_init(RCC_PLL_MUL9);
    delay_init(72);
    led_init();
    usart_init(115200);

    while(1){
        if(g_usart1_rx_flag == 1){
            HAL_UART_Transmit(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1, 1000);
            while(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_TC) != 1){
                printf("Message received");
                g_uart1_ex_flag = 0;
            }
        }
        else{
            delay_ms(50);
        }
    }
}

于是我们的实验就完成了