STM32串口相关知识的了解与学习

本文详细介绍了STM32串口通信的基础知识,包括串口数据传输方式、奇偶校验、STM32串口的特点及工作原理,并通过一个具体的实验案例说明了如何配置STM32的串口进行数据的发送与接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录

    • 串口简介
    • 串口的数据传输
    • 奇偶校验
    • STM32的串口特点
    • STM32串口工作原理
    • STM32的串口中断
    • 串口1和串口6互相通信(实验)
      • 配置外设
      • 代码编写
    • 时钟树
    • 参考资料

串口简介

  • 通用异步收发传输器
  • Universal Asynchronous Receiver/Transmitter,UART
  • 具有TX和RX俩个信号线
  • 可以将数据串行地发送给接收方
    • 串口的数据是按照每一位依次发送的,同时只有一条信号线用于发送
    • 大多数通信协议都是串口的
  • 串口没有时钟线,接收方根据波特率确定采样时间(异步通信)
  • 波特率:每秒串口发送的位速率
  • 常用115200bps,等效的数据传输速率一般为11.25KB/s

串口的数据传输

在这里插入图片描述

  • TX接RX,RX接TX,两条数据线上的信号格式相同但是方向不同
  • 可以同时发送和接收数据(全双工)
  • 一个数据包由起始位,结束位,数据位,和校验位(校验位可有可无)组成
  • 每一位的时长由波特率决定,停止位可以是1~2个时钟
  • 串口空闲时(即没有数据传输时)保持高电平
  • 数据可以是5~9位,最低位(LSB)先发送,最高位(MSB)后发送

在这里插入图片描述

奇偶校验

  • 串口的最后一个数据位和结束位之间可以插入校验位
  • 校验位将这一字节数据中1的个数补齐至偶数个
    • 例如,原来8位中数据有5个1,如果是奇校验则第9位校验位为0,偶校验则校验位为1
  • 如果通信中有一位发生错误校验会不通过,数据就被舍弃

STM32的串口特点

  • UART,USART和LPUART(三种)

    • UART,和USART基本相同

    • 但USART可以当SPI主机用(不推荐)

    • LPUART可以使用32KHz时钟,这时功耗很低,常用于一些低功耗芯片

  • 可以自由设置波特率

  • 具有多种高级功能(很少用)

    • 支持全双工,单线半双工模式
    • 具有硬件RS232流控
    • 第二代串口支持硬件RS485流控,自动波特率等
    • 第三代USART增加了对SPI从机的支持

STM32串口工作原理

  • 发送:数据被写入串口外设,然后串口按照波特率依次发送数据并添加起始位和停止位
  • 接收:串口外设可被配置为以波特率的8倍或16倍的频率采样RX引脚上的电平,然后串口会自动分析数据并将接收到的数据提取出来
  • 串口波特率的最大值取决于串口外设的时钟和接收采样倍数

STM32的串口中断

  • 每个串口都有一个中断向量
  • 当串口发生特定事件即可触发中断,例如
    • 一个数据包的数据发送完成
    • 一个数据包的数据接收完成
    • 串口通信错误(如奇偶校验错误)
    • 串口处于空闲状态(连续一个字节的时间RX处于高电平)
  • 软件需判定触发中断的具体事件

串口1和串口6互相通信(实验)

  • UART1使用中断发送数据
  • UART6使用中断接收数据
  • 使用按键触发发送
  • 尝试同时收发数据

配置外设

打开Cube,首先把PA0设置为GPIO-EXTI0在Connectivity中把串口1和串口6打开(即USART1和USART6),在mode里选择Asynchronous即打开异步模式

STM32的所有外设都可以被配置在不同引脚上(可以切换引脚)

按住ctrl键后点击引脚就可以显示可以被切换的引脚位置

按住ctrl键不动,即可以把原来设置好的引脚拖到可以替换的引脚位置

在USART1的Parameter Settings中的Baud Rate(比特率),Word Length(一个数据包有多少个数据位,包括校验位),Parity校验选项(不校验,偶校验,奇校验),Stop Bits停止位,Over Sampling超采样(采用频率的倍数,决定了串口波特率的上限,把16Samples改为8Samples可以提高上限)

在NVIC中把EXTI0的中断勾选上

在GPIO中把GPIO设置为上升沿或者下降沿触发(下降沿触发即按下按键后触发中断)

然后生成代码

代码编写

在main.c文件中有提供系统运行时间的函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM7) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

我们来看一下里面调用的HAL_IncTick()函数有什么作用

__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

在这个函数下面有一个获得系统运行时间的函数

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

该函数的作用就是获取系统运行时间

首先我们传输数据就需要建立一个发送缓冲区和接收缓冲区(是数组,即一个内存空间,用来存放发送和接收的数据),在 USER CODE BEGIN PV/ END PV里写两个数组充当发送缓冲区以及接收缓冲区,代码如下

/* USER CODE BEGIN PV */
uint8_t txDateBuffer[4];
uint8_t rxDateBuffer[4];
/* USER CODE END PV */

要使串口1传送给串口6,我们需要先设置串口1的中断,在 USER CODE BEGIN 4 / END 4 之间写外部中断回调函数的代码

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
{
 uint32_t temp=HAL_GetTick;
 *((uint32_t *)txDateBuffer)=temp;//把txDateBuffer的地址强制类型转换为一个uint32_t *的指针
    //由于txDateBuffer是uint8_t类型,因此我们需要借助指针来进行强制类型转换为uint32_t类型
  if (GPIO_PIN==GPIO_PIN_0)
  {
    HAL_UART_Transmit_IT(&huart1,txDateBuffer,sizeof(txDateBuffer));//其中的IT代表中断
  }
}
/* USER CODE END 4 */

这样我们就成功地发送了数据,接下来就需要在 USER CODE BEGIN 2 / END 2 之间写一个接收数据的函数

  /* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart6,rxDateBuffer,sizeof(rxDateBuffer));
  /* USER CODE END 2 */

然后编写接收的回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
  if (huart==&huart6)//判断条件也可以写成huart->Instance==USART6
  {
    HAL_UART_Receive_IT(&huart6,rxDateBuffer,sizeof(rxDateBuffer));//这样就可以执行一个继续接收的程序
      //由于串口接收到的数据是uint32_t存放在rxDateBuffer(但是它是一个uint8_t的数组)
  }
}

由于这样在rxDateBuffer中的数据不方便观察,因此我们需要在BEGIN PV/END PV之间再定义一个uint32_t的数组rxDate,更改后代码如下:

/* USER CODE BEGIN PV */
uint8_t txDateBuffer[4];
uint8_t rxDateBuffer[4];
uint32_t rxDate;
/* USER CODE END PV */

然后用这个rxDate去接收rxDateBuffer强制类型转换后的数据

然后把它写到接收函数的前面

void HAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
  if (huart==&huart6)//(huart->Instance==USART6)
  {
    rxDate=*((uint32_t*)rxDateBuffer);
    HAL_UART_Receive_IT(&huart6,rxDateBuffer,sizeof(rxDateBuffer));
  }
}

然后我们考虑到如果按键产生中断速度过快,就可能使得上一次发送数据后串口还没接收完毕,这时再次传送数据过去,会由于接收串口正在被占用导致下一次传送的数据出现错误,因此需要写一个回调函数来判断上一次传输是否已经完成

void HAL_UART_TxCpltCallback(UART_HandleTypeDef*huart)

或者我们可以在按键回调函数里写一个循环,让它判断是否传输完毕,如果未完成就将程序锁死

while (huart1.gState!=HAL_UART_STATE_READY);

这样就完成了串口传输的功能。

时钟树

  • STM32的所有外设都是可以配置时钟的
  • 大部分外设挂载在APB总线上,它们的时钟频率等于总线频率
  • 部分外设可以单独配置时钟频率

参考资料

CH3.1 UART 第1讲 串口基础【南工骁鹰嵌入式软件培训】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

书阁下

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

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

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

打赏作者

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

抵扣说明:

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

余额充值