手把手教你开发stm32——USART(基于hal库)

1.串口通信的基本概念

关于串口通信的基本概念,我在原来的博客中有写到过,这里就不再赘述,需要了解的,可以看下面这篇博客。
链接: https://blog.csdn.net/nbbskk/article/details/127852125

stm32中串口介绍:

  • usart是stm32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧格式,从TX引脚发送出去,也可自动接收RX引脚的数据帧格式,拼接为一个字节数据,存放在数据寄存器里。
  • 自带波特率发生器,最高达4.5Mbits/s
  • 可配置数据位长度(8/9)停止位长度(0.5/1/1.5/2)
  • 支持同步模式、硬件流控制、DMA、智能卡、IRDA、LIN

在stm32中最长用的模式就是使用usart的异步收发功能。

  • 其中的同步模式是指usart中可以引出引脚来使两个设备处于同步通信的模式,但是这种方法一般不用。
  • 硬件流控制就是指有时候接收设备来不及接收数据,但是发送设备在源源不断发送数据,这样就容易导致数据的丢失,为了防止这一现象的产生,就会通过两个引脚RTS和CTS来进行硬件流控制,当接收设备未准备好接收数据的时候,硬件流线上就会置高电平,当接收设备准备好接收数据的时候,硬件流线上就会置低电平,通知发送设备可以进行数据的发送,这种方式一般也不常见。
  • DMA的功能是进行数据的搬运,这个功能我们以后会详细介绍。
  • 而向智能卡、IRDA、LIN等功能我们都基本不会用到,所以不需要掌握。

2.串口的基本结构

2.1.串口的硬件结构

在这里插入图片描述
串口的基本结构其实主要包含三个部分。

  • 第一部分是接受部分,主要包含接收移位寄存器、接收数据寄存器等寄存器组成。
  • 第二部分是发送部分,主要包含发送移位寄存器、发送数据寄存器等寄存器组成。
  • 第三部分是波特率发生器部分,主要是用来产生波特率,由波特率发生寄存器组成。
  • 其余部分就包括一些控制寄存器,使能位等等。
    在这里插入图片描述
    现在分析上图中的具体部分
  1. 这个部分是串口的引脚部分,其中最重要的是TX引脚和RX引脚,上面方块的引脚其他引脚都是和IRDA、LIN等有关的,我们平时一般用不到;然后下面块中的nRTS和nCTS主要就是上面讲的硬件流控制有关,用来通知发送设备是否可以发送数据。

  2. 第二个部分就是发送控制块,这个控制块主要是由发送数据寄存器和发送移位寄存器构成,用于数据的发送。通过发送数据寄存器和发送移位寄存器来配合,使数据发送到TX数据线上。

    • 首先CPU会将数据传输到发送数据寄存器,当检测到发送移位寄存器将所有的数据都移出去以后,TDR就会将数据一次性发送到移位寄存器,这时候,标志位TXE(TX Empty)会置位,表示TDR为空,CPU可以将新的数据传送到TDR中。
    • 然后移位寄存器会将数据一位一位的移动到数据发送端TX上,是从低位开始进行移动的,当数据移位寄存器将数据都移动出去以后,就会将TC这个标志位置位,表示数据移动完成。
    • 这样就通过数据发送寄存器和数据移位寄存器的配合,就能高效实现数据的发送操作。
  3. 第三个部分就是接收控制块,接收控制块的工作机制是和发送控制块相反的操作,但是需要注意的是,其实TDR和RDR在硬件上是同一个寄存器,只是,在串口配置成发送模式的时候就变成了TDR,当配置成接收模式的时候就变成了RDR。这一部分也是通过RDR和接收移位寄存器进行配合来实现数据的接收。过程和发送过程相反,但是当数据从RX引脚移动到接收移位寄存器的时候,是没有标志位的,但是当从接收移位寄存器移动到RDR的时候,就会使得标志位RXNE(RX Not Empty)置位,通知CPU可以读取数据。

  4. 第四个部分就是波特率发生器,波特率就是通过这个地方的一个计算公式来计算出波特率的,但是,由于计算公式比较麻烦,然后我们平常配置波特率用库函数的方式来配置是十分方便的,所以我们不需要了解太多这部分的知识。

  5. 第五个部分就是具体的每个寄存器的配置,其中包括串口使能,各个标志位使能,中断发生等内容,等后续中具体的寄存器介绍的时候我们再具体介绍。

这五个部分就构成了串口的硬件结构,这也是串口能够正常运行的保障。

2.2.串口的数据帧

串口的数据帧格式在发送和接收时是相同的,我们以发送时为例,下图就是显示的发送时的数据帧格式。
在这里插入图片描述
这个数据帧格式就是用来配置发送时的帧格式,选择8位或者9位字长中,都是包含校验位的。当选择9位字长的时候,最好加上校验位,当选择8位字长的时候最好不加校验位,这样就能使发送的数据和接收的数据都变成一个字节,方便接收和发送。其中下面的空闲帧和断开帧都是和局域网协议有关的,串口中用不到,所以就不过多介绍。

2.3.接收器中的起始帧侦测

在这里插入图片描述
在一个数据位中有16个采样时钟。在空闲状态下,信号线置高电平,如果信号线突然检测到有低电平存在,接收器就会进行起始帧检测,在检测到下降沿以后,会在之后的第3、5、7次采样点再次进行第一次采样,在第8、9、10次采样点再次进行第二次采样。

  • 如果三次采样中信号全都是为0,那表明进入起始帧,之后数据接收全部都在第8/9/10次来完成(这样可以保证采样是在信号正中间来进行的)。
  • 如果两次采样中有两个‘0’,一个‘1’,那么也会表示起始位有效,但是会将噪声标志位NE置‘1’,提醒用户有噪声。
  • 如果两次采样有大于一个‘1’存在,那么就会重新回到起始帧侦测的状态。

2.4.接收器中的信号接收

当进行完起始帧侦测后,就会进行信号的采集,具体采集的方法如下。
在这里插入图片描述
每个位也会有16个采样值,一般采样值都是在第8/9/10次进行采样,这样可以保证采样信号正好在正中间,一般情况下,采样信号会保证全‘0’或者全‘1’,但是,当出现噪声的时候,噪声标志位NE也会置‘1’。具体的格式如下图所示:
在这里插入图片描述

3.USART发送代码实现

3.1.cubemx配置

在这里插入图片描述
其他的配置都和原来相同。

3.2.串口的具体代码实现

在这里插入图片描述
我们平常主要就是用到的这四个函数,首先我们先来测试一下串口发送函数
在这里插入图片描述

接下来我们测试接收函数,由于接收函数没有办法在单片机上清晰的显示,所以我们接收到函数以后,立马进行发送出来,然后再显示在串口调试助手上。
在这里插入图片描述
这个接收中断和发送中断我们具体在第四章讲。

3.3.串口重定向(printf)

我们在C语言中打印信息的时候一般都是使用printf来输出信息,C库中使用的<stdio.h>是标准输入输出库,默认输入设备为键盘,默认输出设备为电脑屏幕,因此我们在C库中使用printf的时候不需要考虑其他的因素,只需要调用printf函数语句就可以输出相关信息了。
那么我们能否在stm32的串口中使用printf来打印相关的调试信息呢,答案是肯定的,而一般的做法是通过重定向的方法来讲printf的输出定向到串口中。
keil5中有C库的裁剪库microLib库,可以支持使用printf函数,但是我们需要进行重定向的配置。
首先需要勾选microLib库,
在这里插入图片描述

然后我们需要重写fputc函数,从而将printf的输出输出到串口中
首先我们需要在头文件出引入标准输入输出库<stdio.h>
在这里插入图片描述
然后编写fputc函数进行输出重定向,具体的格式如下:
在这里插入图片描述
最后我们需要对我们的代码进行测试:
在这里插入图片描述
我们可以看出现象,printf函数可以正常使用。

4.USART的中断发送与接收

在使用USART的时候,如果我们要是使能相关中断寄存器的话,那么USART就可以在发送结束或者接收结束的时候产生中断,这样就可以进行一系列的操作了。所有关于中断的函数一般都是在stm32f1xx_it.c这个c文件中包含,所以我们只需要在这个c文件中找一下就好了。.但是我们开启串口中断以后,在这个c文件中也只会表示为一个中断入口,我们需要进入这个函数来寻找相关的发送中断和接收中断函数。

4.1.USART中断发送

在这里插入图片描述
我们可以在stm32f1xx_it.c顺利找到串口1中断入口,然后我们追进串口中断中寻找。
在这里插入图片描述
我们往下找,可以发现一个EndTransmit_IT的函数,这个函数就是发送中断完成的函数。
然后我们继续往下追
在这里插入图片描述
可以发现下面有个回调函数,这个回调函数就是实现中断函数最后的功能了,我们需要重写这个回调函数。
在这里插入图片描述
我在usart.c中重写了这个回调函数,并用printf输出打印调试信息,别忘了这个c库也要包含stdio.h这个头文件
下面是具体的现象
在这里插入图片描述
当我们发送完成一串数据以后,会立即进入中断函数中,然后打印出我们设定好的内容。

4.2.USART中断接收

usart的中断接收和中断发送都是差不多的,因此我们不再进行赘述。

5.有关USART的相关寄存器(有关USART的相关寄存器只需了解即可)

stm32有关USART的相关寄存器如下:

  • 状态寄存器(USART_SR)
  • 数据寄存器(USART_DR)
  • 波特比率寄存器(USART_BRR)
  • 控制寄存器1(USART_CR1)
  • 控制寄存器2(USART_CR2)
  • 控制寄存器3(USART_CR3)
  • 保护时间和预分频寄存器(USART_GTPR)
  1. 状态寄存器
    状态寄存器主要就是用来显示串口的相关状态,主要是通过各种标志位来进行显示的,其中比较常用的标志位有TXE、TC、RXNE等标志位来用于发送和接收时状态的表示。
    在这里插入图片描述
    在这里插入图片描述
    可以通过读取状态寄存器里面的各个状态来使USART进入中断或者执行其他功能。

  2. 数据寄存器
    数据寄存器就是接收和发送过程中的TDR和RDR这两个寄存器,这两个寄存器在物理地址上其实是一个寄存器,当配置为发送模式的时候就变成了TDR,当配置为接收模式的时候就变成了RDR。这个寄存器的主要作用就是用来存放发送和接收的数据的。由于数据可以配置为8位或者9位,所以这个寄存器就只有位0到位8有效。
    在这里插入图片描述

  3. 波特比率寄存器
    波特比率寄存器就是用来产生波特率的,一般我们用库函数配置的时候系统直接默认帮我们配置好了波特率,因此我们不需要直接使用波特比率寄存器来直接配置波特率,所以这个寄存器我们大概率不需要了解。
    在这里插入图片描述

  4. 控制寄存器1
    控制寄存器1是串口寄存器中非常重要的控制器,包括对串口的使能,对字长的设置等内容,其中还有一些中断的设置,这就是控制寄存器主要的作用。
    在这里插入图片描述
    其中最重要的就是位13、位12用于使能USART并且定义数据字的长度。

  5. 控制寄存器2、控制寄存器3和保护时间和预分频寄存器
    这三个寄存器主要和USART的其他功能有关,和DMA、LIN、IREN、智能卡等功能有关,这里就不过多介绍了,感兴趣的可以去看看stm32中文参考手册第25.6节。

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
对于STM32F103C8T6微控制器上的USART2串口,你可以使用STM32HAL库来进行配置和使用。HAL库(Hardware Abstraction Layer)是STMicroelectronics提供的一种高级抽象层,用于简化STM32微控制器的外设配置和操作。 以下是使用HAL库配置和使用USART2串口的基本步骤: 1. 初始化串口外设:在使用之前,需要初始化USART2外设并设置相应的时钟。这可以通过调用`HAL_UART_Init()`函数来完成。 2. 配置串口参数:设置串口的波特率、数据位、停止位、校验位等参数。可以使用`UART_HandleTypeDef`结构体中的成员变量来配置这些参数。 3. 使能串口:通过调用`HAL_UART_Receive_IT()`或`HAL_UART_Transmit_IT()`函数来使能接收和发送中断。 4. 实现中断处理函数:在接收或发送数据时,需要实现相应的中断处理函数。例如,对于接收中断,可以在`USART2_IRQHandler()`函数中处理接收到的数据。 下面是一个简单的示例代码,演示如何使用HAL库配置和使用USART2串口: ```c #include "stm32f1xx_hal.h" UART_HandleTypeDef huart2; void USART2_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart2); } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if (uartHandle->Instance == USART2) { __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART2_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_NVIC_SetPriority(USART2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART2_IRQn); } } void USART2_IRQHandler(void) { HAL_UART_IRQHandler(&huart2); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef* uartHandle) { // 处理接收到的数据 } int main(void) { HAL_Init(); USART2_Init(); // 启动接收中断 HAL_UART_Receive_IT(&huart2, &receivedData, 1); while (1) { // 发送数据示例 uint8_t dataToSend = 'A'; HAL_UART_Transmit(&huart2, &dataToSend, 1, HAL_MAX_DELAY); } return 0; } ``` 以上是一个简单的使用HAL库配置和使用USART2串口的示例,你可以根据自己的需求进行修改和扩展。 希望这对你有所帮助!如果你还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式进阶之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值