前言
本文基于STM32F103C8T6,使用cubemx,配置串口中断,并简单介绍了一些相关内容。
目录
一、通讯基础知识
1.全双工、半双工与单工
有部分刚学习的同学,可能还不太清楚单工、双工,究竟是什么东东,没关系,我们一起来了解一下。
所谓单工,即两个通讯方(A与B)单向联系,发送方与接收方固定,举个例子:
我是古代权侵朝野的大臣(A),我养了一批死士(B),只有我能给死士下达指令,死士只能接收和执行我的命令,不能反过来对我发送命令,这就是单向通信,也就是所谓的单工;
所谓双工,又分为全双工和半双工。
半双工,就是我A与B可以互相通信,但是我们不能同时通信,举个例子:
北京和上海之间修了一条铁路,这上面有火车在运行。如果这列火车正在运输北京去上海的乘客,那么想从上海去北京的乘客,就必须等这列火车到达上海后,才能再出发,这就是半双工;
全双工,两个通讯方不但能互相通信,而且可以同时通信,还是铁路的例子:
现在北京和上海之间有两条铁路了,北京去上海的乘客,与上海去北京的乘客,可以同时乘坐两趟不同的高铁前往目的地,这就是全双工;
2.同步与异步
这里直接摘抄了这篇博文https://blog.csdn.net/qq_33738357/article/details/134119159
总而言之,如果想快速判断同步还是异步,就看有没有时钟线就可以了。
3.波特率
波特率,指的是串口通信的速率,也就是通信时每秒钟可以传输多少个二进制位。例如,每秒钟可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600。
二、串口通讯
1.串口通讯简介
串口通信(Serial Communication)是一种常见的数据传输方式,它通过串行通信接口进行数据的发送和接收。在串口通信中,数据是一位接一位地顺序传输的,与之相对的是并行通信,后者可以同时传输多位数据。串口通信因其简单、成本低廉、易于实现等特点,在计算机、嵌入式系统、工业控制等领域得到了广泛的应用。
其主要特点为:
串行传输:数据以位为单位,按顺序一位接一位地通过单一通道进行传输。
点对点通信:串口通信通常是两个设备之间的直接连接,例如计算机与打印机、计算机与调制解调器等。
全双工或半双工:串口通信可以是全双工(同时进行数据发送和接收)或半双工(交替进行数据发送和接收)。
异步或同步:串口通信可以是异步的(不需要时钟信号同步,通过起始位和停止位来标识数据包的开始和结束)或同步的(需要额外的时钟信号来同步数据传输)。
2.物理层
通过上面的内容,我们已经知道,串口通讯是传输二进制位,那么,什么来表示1,什么来表示0呢?这就涉及到标准的问题了,这里我们主要介绍RS232与TTL。(下面内容摘抄野火https://doc.embedfire.com/mcu/stm32/f103/hal_generalzh/latest/doc/chapter20/chapter20.html)
电子电路中常使用TTL的电平标准,理想状态下,使用5V表示二进制逻辑1,使用0V表示逻辑0;RS232为了增加串口通讯的远距离传输及抗干扰能力, 它使用-15V表示逻辑1,+15V表示逻辑0。
在RS232中,两个通讯设备的“DB9接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232标准”传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL标准”的电平信号,才能实现通讯。
3.通讯协议
串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口。在串口通讯的协议层中,规定了数据包的内容, 通常,其由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。
三、使用Cubemx配置串口
通过上面的内容,我们已经对串口通讯有了一个基础的了解,接下来我们进入实战部分,这里用到的是STM32F103C8T6
首先打开cubemx,选择对应的芯片型号。
选择外部晶振,配置时钟树。
然后选择异步,勾选串口中断
波特率设置为115200
然后就可以生成代码啦,起一个你喜欢的名字,选择保存的地址,另外环境选择MDK-ARM
四、串口的发送与接收
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_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库串口的一些常用函数啦,我们先来看一下HAL_UART_Transmit()这个函数,下图是官方对它的一个注释。
首先看它的简介,@brief,在阻塞模式下发送大量数据,也就是说这个函数是用于串口发送数据的,接下来我们再来看看它的参数:
huart是一个指向UART_HandleTypeDef
结构体的指针,该结构体包含了特定UART模块的配置信息;
pdata是一个指向数据缓冲区的指针,该缓冲区包含了要发送的数据。pdata可以指指向u8(8位,即一个字节)或u16(16位,即两个字节)的数据元素,具体取决于UART模块的配置;
size指定了要发送的数据元素的数量。它告诉函数有多少个u8或u16数据元素需要从缓冲区发送。
Timeout定义了在发送操作完成前,函数等待的超时时间。
知道了这些,那我们来为之前生成的代码补充一下,来实现串口数据的发送:
所有的配置都完成之后,就可以进行烧录啦。烧录完成之后,我们打开串口调试助手,查看一下我们设置的消息是否成功发送。
接下来我们测试一下串口的接收,我们还是首先来看一下接收函数HAL_UART_Receive()
同样是官方对它的注释,和上面的发送函数很像啊,那我们话不多说,直接实操。
我们设定当我们接收到某个特定的信息时,返回OK。
因为我们需要用到strlen,所以首先我们要在头文件加上一个string.h
然后烧录看一下效果
这里我们开了HEX显示,所以回的消息不是OK,我们可以关掉再来看看
可以看到我们这边已经成功了。
2.串口数据中断方式收发
这里用到的是这个函数
不过这里我在使用这个函数的时候,发现了一个小坑,如果我只在初始化的时候调用这个函数,那么就只能触发一次中断接收,会产生如下现象:
为了解决这个问题,于是我开始了爬坑之路(此处省略八百字),然后我们上方案:
我们在main.c里面,加上HAL_UART_RxCpltCallback这个函数,并在它里面,再调用一次HAL_UART_Receive_IT,就可以解决这个问题了。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
HAL_UART_IRQHandler(&huart1);
HAL_UART_Receive_IT(&huart1,test,1);
}
}
HAL_UART_RxCpltCallback这个函数在官方的库里面有,它在串口中断接收完成时被调用,是一个弱定义的函数,因此我们可以直接把他复制到main.c文件里面,来对它进行覆盖。
总而言之,问题就这样被解决啦!
接下来,我们还可以对上面的代码进行微调,把发送函数也放到接收中断里面去做,当我接收中断触发后,满足一定条件时,自动发送数据。