STM32 (四)串口通信

        今天我们学习STM32CubeMX串口的操作,以及HAL库串口的配置,我们会详细的讲解各个模块的使用和具体功能,并且基于HAL库实现Printf函数功能重定向,UART中断接收,本章不讲解串口的原理,在之前的51单片机里面就有一章讲过串口通信的原理了,感兴趣的可以去前面看看。

     

一、原理简介

STM32串口简介

        USART-通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个UART(Universal Asynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能(时钟同步),只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。

1.物理层


1)RS232标准

很多单片机内部例如我们所用的STM32,以及一些传感器一般都是TTL电平。
RS232是一种串行数据传输形式,称其为串行连接,最经典的标志就是 9 针孔的 DB9 电缆RS232电压表示逻辑 1 ,0的范围大极大的增强了容错率,主要用于工业设备直接通信。

 两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯。

 

 

2)USB转串口(重点)

至于为什么是重点因为这是我实验用的方式重点介绍:

USB转串口:主要用于设备(STM32)与电脑通信

 原理图:一定要搞懂下面这张图

 

2.协议层

串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致(一样的起始位 数据 校验位 停止位)才能正常收发数据

 1)通讯的起始和停止信号

串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示

1个停止位:停止位位数的默认值。
2个停止位:可用于常规USART模式、单线模式以及调制解调器模式。
0.5个停止位:在智能卡模式下接收数据时使用。
1.5个停止位:在智能卡模式下发送和接收数据时使用。
2)有效数据

在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长
3)数据校验

    偶校验:校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为偶数。
    例如:数据=00110101,有4个’1’,如果选择偶校验(在USART_CR1中的PS=0),校验位将是’0’,最后数据检验如果数据有偶数个1则数据传输没有出错(但不是绝对的,如果同时两个数据为发送错误(0变成1)则还是偶数个1)

  • 奇校验:此校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为奇数。
    例如:数据=00110101,有4个’1’,如果选择奇校验(在USART_CR1中的PS=1),校验位将是’1’,最后数据检验如果数据有奇数个1则数据传输没有出错,但同样不是绝对的(同时两个1变成0)

 

二、寄存器讲解

1.控制寄存器

 传输模式:如果USART_CR1的PCE位被置位,如果奇偶校验失败USART_SR寄存器中的PE标志被置’1’,并且如果USART_CR1寄存器的PEIE在被预先设置的话,中断产生(我们可以在相应的中断服务函数中,写处理校验失败的代码)。

2.状态寄存器

 

3.USART 功能框图(超级重要)

1)功能引脚:

 

2)数据寄存器(重点)

在这里插入图片描述

 

 

 下面这张图也非常重要理解理解!!
在这里插入图片描述

 3)控制单元(重点)

<1>发送器

发送器根据M位的状态发送8位或9位的数据字。当发送使能位(TE)被设置时,发送移位寄存器中的数据在TX脚上输出,相应的时钟脉冲在CK脚上输出。

一个字符帧发送需要三个部分:起始位+数据帧(可能有校验位)+停止位。每个字符(一个数据帧)之前都有一个低电平的起始位,之后跟着的停止位,其数目可配置,数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的,停止位是一定时间周期的高电平。

配置步骤:

1.通过在USART_CR1寄存器上置位UE位来激活USART

 2.编程USART_CR1的M位来定义字长。

 3.在USART_CR2中编程停止位的位数。

 4.如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中的描述配置DMA寄存器,关于DMA下期再详细讲解。
在这里插入图片描述

 5.利用USART_BRR寄存器选择要求的波特率。

 

发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。

 

 

6.设置USART_CR1中的TE位,发送一个空闲帧帧(一个数据帧长度的高电平)作为第一次数据发送。

 7.把要发送的数据写进USART_DR寄存器(此动作清除TXE位)。在只有一个缓冲器的情况下,对每个待发送的数据重复步骤7。

 8.在USART_DR寄存器中写入最后一个数据字后,要等待TC=1,它表示最后一个数据帧的传输结束(移位寄存器中的数据全部发送完毕)。当需要关闭USART或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。

深入理解TXE位与TC位

清零TXE位总是通过对数据寄存器的写操作(CPU 或 DMA)来完成的,当TXE位已经被硬件置1它表明:

● 数据已经从TDR移送到移位寄存器,数据发送已经开始(发送移位寄存器正在一位一位向外传输数据)

● TDR寄存器被清空

● 下一个数据可以被写进USART_DR寄存器而不会覆盖先前的数据如果TXEIE位被设置,此标志将产生一个中断。

        如果此时USART正在发送数据(发送移位寄存器正在一位一位向外传输数据),对USART_DR寄存器的写操作把数据存进TDR寄存器,并在当前传输结束时把该数据复制进移位寄存器,也就是说移位寄存器里面的数据并不会被覆盖,所以我觉得只要你发送一帧数据等待TXE置1,就算是发送多帧数据时最后也不用等待TC=1。

        如果此时USART没有在发送数据,处于空闲状态,对USART_DR寄存器的写操作直接把数据放进移位寄存器,数据传输开始,TXE位立即被置起。

当一帧发送完成时(停止位发送后)并且设置了TXE位,TC位被置起,如果USART_CR1寄存器中的TCIE位被置起时,则会产生中断

使用下列软件过程清除TC位:
1.读一次USART_SR寄存器;
2.写一次USART_DR寄存器。
TC位也可以通过软件对它写’0’来清除。此清零方式只推荐在多缓冲器通信模式下使用

   <2>接收器

        如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置1,同时如果 USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。

当一字符被接收到时.

  •  RXNE位被置1。它表明移位寄存器的内容被转移到RDR。换句话说,数据已经被接收并且可以被读出。
  • 如果RXNEIE位被设置,产生中断。
  • 在多缓冲器通信时,RXNE在每个字节接收后被置起,并由DMA对数据寄存器的读操作而清零。
  • 在单缓冲器模式里,由软件读USART_DR寄存器完成对RXNE位清除,RXNE标志也可以通过对它写0来清除。RXNE位必须在下一字符接收结束前(接收移位寄存器接收满)被清零(要将数据读出),以避免溢出错误(移位寄存器的数据会被覆盖)。

溢出错误
如果RXNE还没有被复位(还没有读出DR寄存器的数据),又接收到一个字符,则发生溢出错误,数据只有当RXNE位被清零后才能从移位寄存器转移到RDR寄存器。RXNE标记是接收到每个字节后被置位的。如果下一个数据已被收到或先前DMA请求还没被服务时,RXNE标志仍是1,溢出错误产生。

当溢出错误产生时:

  • ORE位被置位。
  • RDR内容将不会丢失。读USART_DR寄存器仍能得到先前的数据。
  • 移位寄存器中以前的内容将被覆盖。随后接收到的数据都将丢失。
  • 如果RXNEIE位被设置或EIE和DMAR位都被设置,中断产生。
  • 顺序执行对USART_SR和USART_DR寄存器的读操作,可复位ORE位


 USART相关中断:

 

 

 

 

三、工程创建

1.设置RCC,开启外部高速晶振。

 2.设置串口

  • 1点击USATR1   
  • 2设置MODE为异步通信(Asynchronous)       
  • 3基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1      接收和发送都使能

  • 4. NVIC Seting使能接收中断。

  •  5.设置时钟

  • 1选择外部时钟HSE 8MHz   
  • 2PLL锁相环倍频72倍
  • 3系统时钟来源选择为PLL
  • 4设置APB1分频器为 /2

3.工程的设置

 

 然后就可以点击工程的创建。

三、HAL函数讲解。

1.UART结构体定义

UART_HandleTypeDef huart1;

UART的名称定义,这个结构体中存放了UART所有用到的功能,后面的别名就是我们所用的uart串口的别名,默认为huart1,可以自行修改。

打开这个结构体:

2.串口的发送/接收函数:

  • HAL_UART_Transmit();串口发送数据,使用超时管理机制 
  • HAL_UART_Receive();串口接收数据,使用超时管理机制
  • HAL_UART_Transmit_IT();串口中断模式发送  
  • HAL_UART_Receive_IT();串口中断模式接收
  • HAL_UART_Transmit_DMA();串口DMA模式发送
  • HAL_UART_Transmit_DMA();串口DMA模式接收

这里的函数参数基本上一样,挑两个来讲解一下。

(1)串口发送数据函数:

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。

(2)串口中断接收数据函数:

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中

3.串口中断函数:

  • 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();串口接收错误函数

(1)串口接收中断回调函数

HAL_UART_RxCpltCallback(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;
  }

4.串口查询函数

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

举例:

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

5.USART的重定义。

重新定义printf函数

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

#include "stm32f4xx_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;
}

记得打开魔术棒:

使用微库。MicoroLIB 是缺省 C 库的备选库,它对标准 C 库进行了高度优化使代码更少,占用更少资源。为使用 printf、 scanf 函数需要
在文件中包含 stdio.h 头文件

四、实验示例

实验一:串口接收中断

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

一、.具体流程:

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 RXBUFFERSIZE  256     //最大接收字节数
char RxBuffer[RXBUFFERSIZE];   //接收数据
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下方添加中断回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == USART1)
  {
	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)) //判断结束位
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
            while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
			Uart1_Rx_Cnt = 0;
			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
  }
}

发送数据被正常返回。

实验二:串口接收字符串数据并且判断数据

如果要实现串口接收数据的判断,如下:

宏定义和变量定义:

const char String1[8] = "kaishi";
const char String2[11] = "chushihua";
const char String3[9] = "xianshi";




#define RXBUFFERSIZE  256     //最大接收字节数
char RxBuffer[RXBUFFERSIZE];   //接收数据
uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数

串口接收中断回调函数:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
         if(huart->Instance == USART1)
    {
		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			Uart1_Rx_Cnt = 0;
			if(strstr((const char *)RxBuffer,String1) != NULL)
			{
                //当接收到数据和String1一样时,需要实现的写在这

			}
			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
		}
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
    }


}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LabVIEW与STM32之间可以进行串口通信串口通信是指通过串口将数据从一个设备传输到另一个设备。在LabVIEW与STM32的通信中,可以通过串口将数据从LabVIEW发送到STM32,或者从STM32发送到LabVIEW。串口通信通常使用异步通信,即发送和接收的数据是以可变的速率进行的,并且每个字符都带有起始位、数据位、校验位和停止位。 在串口通信中,LabVIEW可以作为主设备(Master)通过串口发送指令或数据到STM32,而STM32则作为从设备(Slave)接收并处理这些指令或数据。反之,STM32也可以向LabVIEW发送指令或数据,LabVIEW作为从设备进行接收与处理。 为了实现LabVIEW与STM32之间的串口通信,首先需要在STM32的代码中配置串口接口,以确定串口的参数(如波特率、数据位数、校验位等),并设置接收和发送的中断。然后,通过在LabVIEW中使用串口通信的相关模块和函数,可以与STM32进行通信,包括发送和接收数据。 总而言之,LabVIEW与STM32之间是通过串口进行通信的,通过串口通信可以实现数据的传输和交互。 ### 回答2: LabVIEW和STM32都支持串口通信,但它们实现串口通信的方式略有不同。 LabVIEW是一种基于图形化编程的开发环境,可以用于控制、测量和测试等应用。LabVIEW可以通过VISA(Virtual Instrument Software Architecture)来与外部设备进行通信,包括串口通信。在LabVIEW中,串口通信通常通过使用VISA函数库来实现。VISA提供了一组函数来控制串口的配置、发送和接收数据。 STM32是一系列由意法半导体(STMicroelectronics)推出的32位单片机,具有强大的处理能力和丰富的外设接口。STM32可以通过其内置的UART(Universal Asynchronous Receiver Transmitter)外设来实现串口通信。UART是一种异步串行通信接口,可以实现数据的传输和接收。STM32的UART外设提供了相应的寄存器和配置选项,以方便开发者进行串口通信的设置和控制。 因此,LabVIEW和STM32都可以通过串口实现通信,但它们的具体实现方式不同。LabVIEW通过VISA函数库来控制串口通信,而STM32通过内置的UART外设来实现串口通信。 ### 回答3: LabVIEW与STM32是通过串口进行通信的。 串口通信是一种通过串行方式进行数据传输的通信方式。在串口通信中,数据按照一位一位的顺序进行传输,通过一条线路同时传递数据和控制信号。串口通信相对于并行通信更经济、更简单,适用于较短距离和较低速率的通信。 LabVIEW是一种图形化编程语言和开发环境,广泛用于建立数据采集、控制、仿真和分析系统。通过串口通信,LabVIEW可以与其他设备或硬件进行数据交换,如传感器、执行器等。在串口通信中,LabVIEW可以通过串行端口读取或发送数据,实现与STM32之间的数据交互和通信。 STM32是一系列32位的ARM Cortex-M单片机,具有强大的性能和丰富的外设接口。STM32通过串口与其他设备进行通信,可以使用UART、USART等串行通信接口,通过发送和接收数据来完成与LabVIEW之间的数据交互。 因此,LabVIEW与STM32是通过串口通信进行数据传输和通信的。通过串口通信,LabVIEW可以与STM32进行双向数据传输,实现数据的读取、控制和交互,使两者能够进行有效的协作和信息交换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值