HAL库:串口

1.前言

        上一篇文章学习了GPIO的输入和输出,这一章引入了串口,实现一个单片机向电脑发送一个数据,然后键盘输入任意一个字符,led进行点亮,同时进行输入字符的回显。

2.查看硬件原理图

        一般来说绝大多数常见的就是两种硬件设计电路,一种就是CH340C转type-c,一种是CH340C转USB,我们可以先不需要了解具体的硬件设计,只需要知道,是串口通过CH340C芯片将串口转给USB或者type-c,这都是中间的一种形式,我们只需要开头和结尾就行,不需要知道中间具体发生了什么。

3.创建工程

3.1配置老三样
3.2配置GPIO
3.3配置USART

        设置为异步通信方式,其他均不用进行操作,相关的引脚CubeMX会自己进行设置。

3.4生成工程

4.代码编写

#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include<stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int UartDrvWrite(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Transmit(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

int UartDrvRead(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Receive(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

struct __FILE{
    int handle;
};

FILE __stdout;

int fputc(int ch,FILE *f)
{
    (void)f;
    UartDrvWrite((unsigned char*)&ch,1);
    return ch;
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  char a = 'a';
  
  printf("%c",a);
  
   if(1 == UartDrvRead((unsigned char*)&a,1))
    {
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
        HAL_Delay(1000);
    }
    
    printf("%c",a);
  /* USER CODE END 2 */
   
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

5.结果展示

        在显示结果时,不是很方便一起录视频,所以只截图一个MobaXterm的界面。相关的软件操作放在文章最后面进行讲解。

6.代码分析

        在学习串口时,我们有一点要进行注意,一定要分清楚,串口的发送方和接收方是谁,我们程序中的发送方其实是stm32单片机,接收方是电脑,不要错认为电脑是发送方,如果在一开始都分不清楚这一点,就很难搞明白串口的工作方式和方向,从而进入学习的误区。

6.1初始思路

        初始思路很残暴,就是直接采用HAL_UART_Transmit和HAL_UART_Receive进行字母a的收和任意字符的收,程序一开始,就将a进行发出,然后程序一直卡在HAL_UART_Receive,等待收字符,进行led点亮,然后在对收到的字符进行回显。我们在进行仿真和正常的逻辑思维双重推理下,发现程序其实很残暴,因为当我们一直收不到字符时,程序就一直卡着不动,进行等待,现实中不会这样进行设计程序,这里只是为了学习而进行学习。

int main(void)
{
  
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  char a = 'a';
  
  if(HAL_OK != HAL_UART_Transmit(&huart1,(uint8_t *)&a,1,10))
  {
        Error_Handler();
  }
  
  if(HAL_OK == HAL_UART_Receive(&huart1,(uint8_t *)&a,1,HAL_MAX_DELAY))
  {
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
        HAL_Delay(1000);
  }
    
  if(HAL_OK != HAL_UART_Transmit(&huart1,(uint8_t *)&a,1,HAL_MAX_DELAY))
  {
        Error_Handler();
  }
  
  while (1)
  {
  }

}
6.2进一步

        接着我们会想着自己封装一个发送和接收函数,这样让程序更加清晰明了。

#include "main.h"


UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

int UartDrvWrite(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Transmit(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

int UartDrvRead(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Receive(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();

  char a = 'a';
  
  if(1 != UartDrvWrite((unsigned char*)&a,1))
  {
      Error_Handler();  
  }  

  if(1 == UartDrvRead((unsigned char*)&a,1))
  {
       HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
       HAL_Delay(1000);
  }
    
  if(1 != UartDrvWrite((unsigned char*)&a,1))
  {
      Error_Handler();  
  }  

  while (1)
  {
  }
}
6.3最终代码

        最后再加入重定向,形成最终代码。加入重定向以后,printf函数就是相当于发送函数,也就是写函数,这里也是要搞明白这个相关逻辑。

#include "main.h"
#include<stdio.h>

UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

int UartDrvWrite(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Transmit(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

int UartDrvRead(unsigned char *buf,unsigned short length)
{
    if(NULL == buf) return -1;
    if(0 == length) return -1;
    
    if(HAL_OK != HAL_UART_Receive(&huart1,buf,1,HAL_MAX_DELAY))
        return -1;
    return length;
}

struct __FILE{
    int handle;
};

FILE __stdout;

int fputc(int ch,FILE *f)
{
    (void)f;
    UartDrvWrite((unsigned char*)&ch,1);
    return ch;
}

int main(void)
{

  HAL_Init();

  SystemClock_Config();


  MX_GPIO_Init();
  MX_USART1_UART_Init();

  char a = 'a';
  
  printf("%c",a);
  
   if(1 == UartDrvRead((unsigned char*)&a,1))
    {
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
        HAL_Delay(1000);
    }
    
    printf("%c",a);

  while (1)
  {
  }
}

7.函数分析

7.1HAL_UART_Receive

判断是否忙-->锁住-->标记接收忙-->获取tick计数-->赋值RxXferCount有多少数据要接收-->每次从DR内获取一个Byte存在pData指向的空间

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* 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->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Init tickstart for timeout managment */
    tickstart = HAL_GetTick();

    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Check the remain data to be received */
    while (huart->RxXferCount > 0U)
    {
      huart->RxXferCount--;
      if (huart->Init.WordLength == UART_WORDLENGTH_9B)
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        tmp = (uint16_t *) pData;
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
          pData += 2U;
        }
        else
        {
          *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
          pData += 1U;
        }

      }
      else
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
        }
        else
        {
          *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
        }

      }
    }

    /* At end of Rx process, restore huart->RxState to Ready */
    huart->RxState = HAL_UART_STATE_READY;

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}
7.2HAL_UART_Transmit
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Init tickstart for timeout managment */
    tickstart = HAL_GetTick();

    huart->TxXferSize = Size;
    huart->TxXferCount = Size;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    while (huart->TxXferCount > 0U)
    {
      huart->TxXferCount--;
      if (huart->Init.WordLength == UART_WORDLENGTH_9B)
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        tmp = (uint16_t *) pData;
        huart->Instance->DR = (*tmp & (uint16_t)0x01FF);
        if (huart->Init.Parity == UART_PARITY_NONE)
        {
          pData += 2U;
        }
        else
        {
          pData += 1U;
        }
      }
      else
      {
        if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
        {
          return HAL_TIMEOUT;
        }
        huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
      }
    }

    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

    /* At end of Tx process, restore huart->gState to Ready */
    huart->gState = HAL_UART_STATE_READY;

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值