STM32学习笔记 二、基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发

系列文章目录

一、基于STM32F103C8T6最小系统板和STM32CubeMX实现LED灯循环闪烁
二、基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发



前言

这次我用的板子是一个用STM32F103C8T6作为主控芯片的一个数据采集卡,两个LED灯连接的引脚是PB3与PB4,TX与RX引脚分别是PA9和PA10。

完整工程下载链接:
基于STM32F103C8T6(HAL库)和CubeMX实现UART串口通信数据收发.rar

相关基础概念:
基于STM32之UART串口通信协议(一)详解

在这里插入图片描述
在这里插入图片描述


一、配置CubeMX

1、新建工程;
2、配置时钟源,在RCC里面的HSE配置的是晶振时钟;
3、配置程序烧录引脚SYS为SWD模式;
4、配置GPIO口,配置一个LED灯(我的板子是PB3),起到串口成功接收到数据时的指示作用;
5、配置串口收发引脚

此处我们采用的通信方式为UART通信(异步全双工串口通信),PA9作为TX,PA10作为RX使用。直接在可视框图里点击相应的引脚进行配置。

配置到目前,效果图为:
在这里插入图片描述
可以发现,TX和RX两个引脚配置完之后是黄色的,代表还没有配置完毕,下面继续进行配置。

6、在左边的"Connectivity”里面的USART1模式里选择异步全双工通信模式asynchronous
⮚点击USATR1
⮚设置MODE为异步通信(Asynchronous)
⮚基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1,接收和发送都使能
⮚GPIO引脚设置 USART1_RX/USART_TX
⮚NVIC Settings 一栏使能接收中断

这里简单扩展一下:
STM32F103系列单片机共有5个串口,其中1-3是通用同步/异步串行接口USART(Universal Synchronous/Asynchronous Receiver/Transmitter),4,、5是通用异步串行接口UART(Universal Asynchronous Receiver/Transmitter)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7、配置时钟树,我还是开到最高的72MHz
8、进行项目设置,最后生成代码,CubeMX部分就大功告成了

二、HAL库UART相关函数简介

2.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 UART的别名 如 : 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 UART的别名
⮚ *pData 接收到的数据存放地址
⮚ Size 接收的字节数
举例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1); //中断接收一个字符,存储到value中

2.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的别名
举例: 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;
  }

2.3 串口查询函数

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

举例:

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

三、逻辑代码部分

3.1 UART接收中断

因为中断接收函数只能触发一次接收中断,所以我们需要在中断回调函数中再调用一次中断接收函数。

具体流程:

1、初始化串口

2、在main中第一次调用接收中断函数

3、进入接收中断,接收完数据 进入中断回调函数

4、修改HAL_UART_RxCpltCallback中断回调函数,处理接收的数据,

5、回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断

函数流程:

①HAL_UART_Receive_IT (中断接收函数) ->
②USART2_IRQHandler(void) (中断服务函数) ->
③HAL_UART_IRQHandler(UART_HandleTypeDef *huart) (中断处理函数) ->
④UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) ->
⑤HAL_UART_RxCpltCallback(huart) (中断回调函数)

HAL_UART_RxCpltCallback函数就是用户要重写在main.c里的回调函数。

代码实现:

在main.c中添加下列定义:

#include <string.h>
 
#define RxBuffer_MaxSize  256     //最大接收字节数
char RxBuffer[RxBuffer_MaxSize];   //接收数据
uint8_t aRxBuffer;			//接收中断缓冲
uint8_t UART1_Rx_Cnt = 0;		//接收缓冲计数

在main()主函数中,调用一次接收中断函数

/* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
/* USER CODE END 2 */

在main.c下方添加中断回调函数

/* USER CODE BEGIN 4 */
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    

  UNUSED(huart);

 	if(UART1_Rx_Cnt >= 255)  //溢出判断
	{
    
    
		UART1_Rx_Cnt = 0;
		memset(RxBuffer,0x00,sizeof(RxBuffer));
		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
  }
	else
	{
    
    
		RxBuffer[UART1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
			if((RxBuffer[UART1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[UART1_Rx_Cnt-2] == 0x0D)) //判断结束位
			//此处条件可以改写为if(RxBuffer[UART1_Rx_Cnt-1] == '\n') 因为上面的条件就是表示回车
			{
    
    
					HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, UART1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
          while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
				  HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_3);//LED指示灯状态翻转
					UART1_Rx_Cnt = 0;
					memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
			}
	}
		HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}
/* USER CODE END 4 */

现象:

使用USB转TTL(CH340)进行数据发送,XCOM V2.6串口助手进行调试,发送“1”(这里要注意波特率等参数要设置的和之前配置的相同
在这里插入图片描述
在debug模式下,将缓冲计数值UART1_Rx_Cnt加入监视器,串口接收到发来的数据后,计数值+1.
在这里插入图片描述
同时也可以看到LED灯状态翻转

3.2 UART发送

重新定义printf函数

stm32f1xx_hal.c中包含#include <stdio.h>

#include "stm32f1xx_hal.h"
#include <stdio.h>
extern UART_HandleTypeDef huart1;   //声明串口

在 stm32f1xx_hal.c 中重写fget和fput函数

/**
  * 函数功能: 重定向c库函数printf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fputc(int ch, FILE *f)
{
    
    
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
    
    
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}

在main.c中添加

 #define RxBuffer_MaxSize  256
char RxBuffer[RxBuffer_MaxSize]; 
  while (1)
  {
    
    
    /* USER CODE END WHILE */
			printf("你好,世界\r\n");//这里博主只写\n的时候无法换行,百度了一下用\r\n就可以了,估计是编译系统的问题
			HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

现象:

在这里插入图片描述
注意:
重定义printf后,必须在target里面勾选上MicroLIB,调用一下这个微型库,不然一直卡在里面。

在这里插入图片描述

参考博文:【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解

猜你喜欢

转载自blog.csdn.net/h568630659/article/details/121427218