Skip to content

USART

设置为启动位一个, 必须为0, 8位有效数据, 没有校验位, 1位字长, 一个停止位

HAL库外设初始化MSP回调机制

在HAL库里面有很多的HAL_PPP_Init()的初始化函数, 用户需要使用这一个函数进行初始化, 之后这个函数会调用一个HAL_PPP_MspInit()函数, 一般是被__weak进行修饰, 里面实际上什么都没有干, 需要用户自己实现

这个函数需要用户自己进行配置使用到的硬件, 比如说GPIO, NVIC, CLOCK等

当多个PPP外设同时使用MSP函数的时候, 比如说串口1和串口3都使用者一个函数的时候, 需要通过判断外设寄存器的基地址进行区分实际上初始化的是哪一个外设的硬件

当多个外设都是用到这一个函数的时候, 文件不好管理, 会使得代码比较乱

image-20231128202820168

官方把所有的msp回调函数放在stm32f1xx_hal_msp.c文件里面, 但是不方便管理

回调函数

image-20231128203359561

image-20231128203417044

image-20231128212511376

实际使用

配置串口的工作参数, HAL_UART_Init()

串口底层的初始化HAL_UART_MspInit()

开启串口的异步接收中断HAL_UART_Receive_IT()

设置优先级以及使能中断HAL_NVIC_SetPriority(), HAL_NVIC_EnableIRQ()

编写中断服务函数 USARTx_IRQHandler() UARTx_IRQHandler()

串口数据发送: UART_DR寄存器, HAL_UART_Transmit()

函数介绍

image-20231128214358535

c
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
c
void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;//设置句柄
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}
c
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;//判断参数是否正确
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pRxBuffPtr = pData;	//设置几个参数, 会在中断的处理函数里面用到
    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);	//初始化接受非空中断

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

使用中断的方式接受指定字节的数据

c
/* Check that a Rx process is ongoing */
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {
        /*使用9bit模式的处理*/
    }
    else
    {
        pdata8bits = (uint8_t *) huart->pRxBuffPtr;//获取数据存储区
        pdata16bits  = NULL;

        if ((huart->Init.WordLength == UART_WORDLENGTH_9B) || ((huart->Init.WordLength == UART_WORDLENGTH_8B) && (huart->Init.Parity == UART_PARITY_NONE)))
        {
            *pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
        }
        else
        {
            *pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
        }
        huart->pRxBuffPtr += 1U;//存储区移位
    }

    if (--huart->RxXferCount == 0U)
    {//没有数据了关闭中断
        /* Disable the UART Data Register not empty Interrupt */
        __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

        /* Disable the UART Parity Error Interrupt */
        __HAL_UART_DISABLE_IT(huart, UART_IT_PE);

        /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
        __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

        /* Rx process is completed, restore huart->RxState to Ready */
        huart->RxState = HAL_UART_STATE_READY;

        #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx complete callback*/
        huart->RxCpltCallback(huart);
        #else
        /*Call legacy weak Rx complete callback*/
        HAL_UART_RxCpltCallback(huart);
        #endif /* USE_HAL_UART_REGISTER_CALLBACKS */

        return HAL_OK;
    }
    return HAL_OK;
}
else
{
    return HAL_BUSY;
}
c
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

接收数据用一个死等的方式

c
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

发送数据

c
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
c
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

原理图

image-20231129084955262

image-20231129085250966

c
uint8_t g_rx_buffer[1];          /* HAL库使用的串口接收数据缓冲区 */
uint8_t g_usart1_rx_flag = 0;    /* 串口接收到数据标志 */

UART_HandleTypeDef g_uart1_handle;  /* UART句柄 */

/* 串口1初始化函数 */
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_uart1_handle);
    
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1);	//在这里直接使用中断去收数据
}

/* 串口MSP回调函数 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;
    if(huart->Instance == USART1)                /* 如果是串口1,进行串口1 MSP初始化 */
    {
        __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.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);            /* 初始化串口1的TX引脚 */
        
        gpio_init_struct.Pin = GPIO_PIN_10;
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;         /* 输入 */
        gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);            /* 初始化串口1的RX引脚 */
        
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
    }
}

/* 串口1中断服务函数 */
void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&g_uart1_handle);
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1);
}

/* 串口数据接收完成回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        g_usart1_rx_flag = 1;
    }
}
c
int main(void)
{
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                             /* 初始化延时函数 */
    led_init();                                 /* 初始化LED */
    usart_init(115200);                         /* 波特率设为115200 */
    
    printf("请输入一个英文字符:\r\n\r\n");
    while(1)
    {
        if(g_usart1_rx_flag == 1)
        {
            printf("您输入的字符为:\r\n");
            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("\r\n");
            g_usart1_rx_flag = 0;
        }
        else
        {
            delay_ms(10);
        }
    }
}

使用printf的原因

当波特率是115200的时候, 一秒钟发送的数据的115200/10bit的数据(有起始位结束位), 每一秒钟发送的数据的大概是11.25K数据, 发送一个数据大约需要89us, 发送11个数据就是接近1ms了

在实际的产品里面尽量不要使用这个函数