STM32-NUCLEO-F411RE-USART_串口

STM32-NUCLEO-F411RE-USART_串口

    • 串口基本原理
      • 异步串行通信的特点
      • 异步串行通信的两个关键点
      • 异步串行通信的数据接收过程
      • 串行内部寄存器结构框图
      • 串行状态标志位
      • 串囗初始化过程
    • 一、配置时钟
    • 二、配置调试模式
    • 三、串口参数配置
    • 四、生成 Keil 工程
    • 代码部分
      • UART结构体定义
      • UART函数
        • 1、串口发送/接收函数
        • 2、串口中断函数
        • 3、串口查询函数
      • 主程序
    • 七、实验效果
    • 八、重定向 printf

使用STM32CubeMX生产初始化代码,底板硬件使用NUCLEO-F411RE开发板

串口基本原理

异步串行通信的特点

STM32-NUCLEO-F411RE-USART_串口_第1张图片

特点:数据传输以单个字符为单位,字符和字符之间的间隙任意,字符内部每一位持续的时间相同。收发双方没有专门的时钟信号,而是依靠事先约定的字符格式和通信速率来完成通信。

STM32-NUCLEO-F411RE-USART_串口_第2张图片
在这里插入图片描述

常用字符格式:1位起始位 8位数据位 无奇偶校验 1位停止位

波特率:每秒钟传送二进制数码的位数,以bit/s(bps)为单位
常用的波特率有:9600、19200、38400、57600和115200;

波特率为115200,表示每秒传输115200位,且每一位数据在数,据线上持续时间为Tbit=1/115200=8.68us。 波特率为9600时起始位长度为104.2us。

异步串行通信的两个关键点

STM32-NUCLEO-F411RE-USART_串口_第3张图片

异步串行通信的数据接收过程

STM32-NUCLEO-F411RE-USART_串口_第4张图片

串行内部寄存器结构框图

STM32-NUCLEO-F411RE-USART_串口_第5张图片

STM32-NUCLEO-F411RE-USART_串口_第6张图片

串行状态标志位

STM32-NUCLEO-F411RE-USART_串口_第7张图片

串囗初始化过程

STM32-NUCLEO-F411RE-USART_串口_第8张图片

STM32-NUCLEO-F411RE-USART_串口_第9张图片

一、配置时钟

开发板焊接了外部晶振,所以我 RCC(Reset and Cock Control) 配置选择了 Crystal/Ceramic Resonator(石英/陶瓷谐振器),配置完成后,右边的 Pinout view 里相关引脚就会被标绿。
STM32-NUCLEO-F411RE-USART_串口_第10张图片

外部高速时钟配置完成后,进入 Clock Configuration 选项,根据实际情况,将系统时钟配置为 96 MHz,配置步骤如下,最后按下回车,软件会自动调整分频和倍频参数。
STM32-NUCLEO-F411RE-USART_串口_第11张图片

二、配置调试模式

ST-Link 就是 Serial Wire 调试模式,一定要设置!!!
以前使用 M0 的芯片,不配置这个模式没出现问题,但现在这个型号,如果不配置 Serial Wire 模式,程序一旦通过 ST-Link 烧录到芯片中,芯片就再也不能被ST-Link 识别了。(后来我是通过 STMISP 工具烧录程序/擦除后才恢复正常的)
STM32-NUCLEO-F411RE-USART_串口_第12张图片

三、串口参数配置

串口为 USART1,按照下面步骤配置 USART1,将其设置为异步模式(Asynchronous),波特率 115200(默认),字长 8 Bits(默认),无校验(默认),停止位 1(默认),使能发送和接收(默认):
STM32-NUCLEO-F411RE-USART_串口_第13张图片
使能中断
STM32-NUCLEO-F411RE-USART_串口_第14张图片

四、生成 Keil 工程

设置 IDE 和 工程目录及名称:
STM32-NUCLEO-F411RE-USART_串口_第15张图片
将每种外设的代码存放到不同的 .c /.h 文件中,便于管理(不然都会被放到 main.c 中)。
STM32-NUCLEO-F411RE-USART_串口_第16张图片

代码部分

UART结构体定义

typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                 *Instance;        /*!< 串口寄存器的基地址定义		   */
  UART_InitTypeDef              Init;             /*!< 串口初始化数据类型			   */
  uint8_t                       *pTxBuffPtr;      /*!< 串口发送缓冲区首地址 			   */
  uint16_t                      TxXferSize;       /*!< 串口待发送数据个数			   */
  __IO uint16_t                 TxXferCount;      /*!< 串口发送数据计数器               */
  uint8_t                       *pRxBuffPtr;      /*!< 串口接收缓冲区首地址			   */
  uint16_t                      RxXferSize;       /*!< 串口待接收数据个数     	       */
  __IO uint16_t                 RxXferCount;      /*!< 串口接收数据计数器     	       */
  __IO HAL_UART_RxTypeTypeDef ReceptionType;      /*!< 持续接收类型     				   */
  DMA_HandleTypeDef             *hdmatx;          /*!< 串口发送的DMA通道句柄定义    	   */
  DMA_HandleTypeDef             *hdmarx;          /*!< 串口接收的DMA通道句柄定义  	   */
  HAL_LockTypeDef               Lock;             /*!< 保护锁类型定义                   */
  __IO HAL_UART_StateTypeDef    gState;           /*!< 串口全局状态和发送状态信息*/
  __IO HAL_UART_StateTypeDef    RxState;          /*!< 串口接收状态信息*/
  __IO uint32_t                 ErrorCode;        /*!< 串口错误代码                 	  */
} UART_HandleTypeDef;

UART函数

1、串口发送/接收函数

HAL_UART_Transmit();串口发送数据,使用超时管理机制
HAL_UART_Receive();串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();串口中断模式发送
HAL_UART_Receive_IT();串口中断模式接收
HAL_UART_Transmit_DMA();串口DMA模式发送
HAL_UART_Transmit_DMA();串口DMA模式接收
这几个函数的参数基本都是一样的,我们挑两个讲解一下

串口发送数据:

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

功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。

参数:

UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要发送的数据
Size 发送的字节数
Timeout 最大发送时间,发送数据超过该时间退出发送

举例:   HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff);   //串口发送三个字节数据,最大传输时间0xffff

中断接收数据:

HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

功能:串口中断接收,以中断方式接收指定长度数据。
大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)

参数:

UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 接收到的数据存放地址
Size 接收的字节数

举例:    HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1);   //中断接收一个字符,存储到value中

2、串口中断函数

HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数

串口接收中断回调函数:

HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);  

功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码。

串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改

参数:

UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1

举例:   HAL_UART_RxCpltCallback(&huart1){           //用户设定的代码               }

串口中断处理函数

HAL_UART_IRQHandler(UART_HandleTypeDef *huart);  

功能:对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用

如果接收数据,则会进行接收中断处理函数

 /* UART in mode Receiver ---------------------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    UART_Receive_IT(huart);
  }

如果发送数据,则会进行发送中断处理函数

  /* UART in mode Transmitter ------------------------------------------------*/
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }

3、串口查询函数

HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌

举例:

while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX)   //检测UART发送结束

主程序

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"


uint8_t RxBuffer;
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* 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 ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

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

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, 1, 10);  // 把收到的字节发送出去,10ms超时时间
		HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);  // 开启下一次串口中断
	}
}

/* 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();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);  // 开启下一次串口中断
	
	HAL_UART_Transmit(&huart1, "HAL_UART_Transmit test\r\n", 24, 0xffff);  //发送字符串

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		HAL_UART_Transmit(&huart1, "hello world\r\n", 13, 0xffff);
		HAL_Delay(500);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 96;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

七、实验效果

STM32-NUCLEO-F411RE-USART_串口_第17张图片

八、重定向 printf

在工程中添加下面代码,重定义 fputc

#include "usart.h"
#include 

/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
/* USER CODE END 0 */

需要使用C语言函数
STM32-NUCLEO-F411RE-USART_串口_第18张图片

将前文示例代码中的 HAL_UART_Transmit() 替换成 printf() 将实现同样的功能
STM32-NUCLEO-F411RE-USART_串口_第19张图片

你可能感兴趣的:(STM32开发,stm32,单片机,arm)