摘要:通讯的分类、UART的工作原理及其使用方法、HAL库的代码解读、利用USART回显
通讯的三个分类以及各分类详解
————你了解通讯吗???
通讯的三个分类:
串行通讯、并行通讯
全双工、半双工、单工通讯
同步通讯、异步通讯
串行通讯、并行通讯:
串行通讯是值设备之间通过少量的数据线(8根以下),地线以及控制信号线,按数据位的形式一位一位地传输的通讯方式。抗干扰能力较强,速率低,成本低、传输距离较远
并行通讯一般指使用8、16、32、64以及更大数据线传输的方式。一次可以发送大量的数据,抗干扰能力较弱,速率高,成本高、传输距离较近
全双工、半双工、单工通讯:
全双工:同一时刻两个设备可以同时收发数据
半双工:两个设备之间可以收发数据,但不能同一时间进行
单工:在任何时刻,都只能进行一个方向的通讯,即一个固定为发送设备,另外一个固定为接收设备。
同步通讯、异步通讯:
一般可以根据通讯过程中,是否有使用到时钟信号进行简单的区分,
同步通信方式:有共同的时钟信号,设备A与设备B之间有两条线:时钟线和数据线。在时钟信号的驱动下设备A与设备B才能进行通讯。双方会规定在时钟信号的上升沿或者下降沿同时进行采样(双方规定),同步通信传输的数据大部分是有效数据。
异步通信方式:只用一条线:数据线。通过通讯起始位、校验位、停止位、以及所需要传输的数据打包,以数据帧的格式传输。双方需要约定好传输速率:波特率。异步通信传输的数据包含了起始位、校验位、停止位等无效数据。
三个率的概念
通讯速率:在单位时间内(通常为一秒)传输的比特数。
比特率(Bitrate):即每秒传输的二进制位数,单位为比特每秒(bit/s)
波特率:表示每秒中传输了多少个码元,单位是baud/s,一个码元可以由多个二进制表示。
当波特和比特等价的时候,默认为一个码元用一个bit表示:即1波特=1比特
USART
————你了解UART吗???
USART是一个STM32的一个外设。通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),简称UATRT。STM32上的USART外设可以实现同步传输功能,所以外设名为USART,比UART多了一个S,即为同步:synchronous, Universal synchronous Asynchronous Receiver/Transmitter。在STM32中,USART配置成异步通信的时候是与UART是相等的,以下都以USART进行描述。
UART的电平标准
UART的器件主要是用来产生相关接口的协议信号,如RS232/RS485等串行接口标准规范和总线标准规范,要使用传输数据的这些接口,就要按照接口规定的协议信号发送数据。UART器件广泛应用于串口通信协议中,扮演着传送器的角色。
RS-232:
RS-232标准接口(又称EIA RS-232)是常用的串行通信接口标准之一,它是由美国电子工业协会(Electronic Industry Association,EIA)联合贝尔系统公司、调制解调器厂家及计算机终端生产厂家于1970年共同制定,其全名是“数据终端设备( DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。
RS-232标准规定了连接电缆和机械、电气特性、信号功能及其发送过程。
TTL:
TTL(transistor transistor logic)即晶体管-晶体管逻辑电平。TTL电平信号规定,+5V等价于逻辑“1”,0 V等价于逻辑“0”(采用二进制来表示数据时)。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统,这是计算机处理器控制的设备内部各部分之间通信的标准技术。一般的电子设备用的多是TTL电平,但是它的驱动能力和抗干扰能力很差,不适合作为外部的通信标准
TTL标准与RS-232
通讯标准 | 电平标准(发送端) |
5v TTL | 逻辑1:2.4V~5V 逻辑0:0V~0.5V |
RS-232 | 逻辑1:-15V~-3V 逻辑0:+3V~15V |
控制器一般使用的是TTL电平标准,使用常常会使用MAX3232芯片对TTL及RS-232电平信号进行转换,RS-232提高了传输时的抗干扰能力。
RXD:接收数据 。
TXD:发送数据。
GND:信号地
串口数据包的格式
在串口通讯协议层中,规定了数据包的内容,它有起始位、主体数据、检验位、停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,不然就牛头不对马尾啦!!两者需要约定好波特率,常见的波特率为4800、9600、115200等。
起始位一般为一个逻辑0的数据位表示,停止位可以用0.5、1、1.5、2个逻辑1的数据位表示。有效数据通常被约定位5、6、7、8、位长。数据校验在有效数据之后,有一个可选的数据校验位,用赖校验数据是否出现偏差,校验方法有奇检验、偶校验、0校验、1校验以及无校验。如果校验发现错误,那么这个数据包就会被丢弃。
拿STM32F10xx来看,他的数据包的基本格式如下:
USART的框图和工作原理
分析一些USART实现功能的底层逻辑,下图为STM32F10xx的参考手册中关于USART的框图,
我们重点看TX(发送)和RX(接收)功能的实现,其实他们两个功能是实现的方式是一样的。当IrDASIR编解码模块接收数据的时候,将接收到的数据,一位一位填进去移位寄存器中,当其填满的时候,接收数据寄存器RDR就会一次性的读取接收移位寄存器中的数据,从而交给DATA总线(可以通过CPU或者DMA的方式来读取)。当要发送数据的时候,将要发送的数据写入发送数据寄存器TDR中,当TDR满了之后,会一次性的把数据交给发送移位寄存器,然后发送数据移位寄存器通过IrDASIR编解码模块发送数据
还要一种常用的转串口的方式:USB转串口,他是通过CH340G来完成的,拿STM32103开发板来看,其USB转串口的原理图如下:
USART的CubeMX的配置
————你会运用USART吗??
CubeMX的配置步骤
首先我们打开CubeMX,设置常规时钟,然后看到USART的部分。解释都在图中,
然后我们生成工程。
HAL库USART的代码解读
———你理解HAL库关于USART的代码吗?
生成完工程之后,我们会看到CubeMX会调用这4个初始化函数:HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();
HAL_Init()的定义
我们首先去看看HAL_Init()的定义:
根据搜索我们可以找到HAL库调用的HAL_Init();我们可以直接看看HAL库对这个函数的介绍:
- 这个函数用于初始化HAL库;它必须是主程序中执行的第一条指令(在调用任何其他HAL函数之前)
- 它执行以下操作:配置Flash预取。
- 将SysTick配置为每1毫秒产生一次中断,由HSI进行时钟处理(在此阶段,时钟尚未配置,因此系统从内部HSI运行,频率为16 MHz)。
- 设置“NVIC组优先级”为“4”。
- 调用用户文件“stm32f1xx_hal_msp.c”中定义的HAL_MspInit()回调函数来执行全局低级硬件初始化。
- SysTick被用作HAL_Delay()函数的时间基础,应用程序需要确保SysTick的时间基础始终设置为1毫秒才能正确地进行HAL操作。
SystemClock_Config();MX_GPIO_Init();在这就不多赘述,感兴趣的小伙伴可以直接去研究。
MX_USART1_UART_Init()的实现流程梳理
我们再来看看USART是如何被初始化的MX_USART1_UART_Init();梳理一下MX_USART1_UART_Init()的实现流程。
找到MX_USART1_UART_Init()的定义:
可以看见CubeMX已经将基础的usart的环境配置好了,是基于我们上文对Usart的配置来配置的。让我们去看看HAL库是如何驱动USART的吧,这需要去阅读stm32f1xx_hal_usart.c文件的HOW TO USE THIS DRIVER。
HOW TO USE THIS DRIVER驱动解释
1.UART_HandleTypeDef
定义一个UART_HandleTypeDef结构体句柄声明UART_HandleTypeDef句柄结构(例如UART_HandleTypeDef huart)
那么CubeMX配置的UART_HandleTypeDef句柄结构是这样的,对UART_HandleTypeDef的成员分析如下:
对UART_InitTypeDef这个初始化成员分析如下:具体的分析步骤在前几个文章也有说,大家可以去看看前面几篇文章,在此不多赘述了。
对成员Mode查看他有几种模式可以配置,
发现他有3种模式:分别是接收、发送、接收和发送。
2.HAL_UART_MspInit()
通过实现HAL_UART_MspInit()API初始化UART低级别资源,HAL_MspInit广泛存在每一个外设中
HAL库驱动调用流程:HAL_XXX_Init---调用HAL_MspInit---初始化对应外设使用的GPIO引脚、特殊功能、时钟使能等。
HAL_MspInit对于每一个外设都是不一样的,如这里用到的UART外设,对于串口而言就会有HAL_UART_MspInit,通过HAL_UART_MspInit函数来实现串口外设的底层初始化,要做下列几个功能:
(1)使能UATRT外设时钟
(2)配置UART使用的引脚模式
(3)如果要用中断就配置中断
(4)如果要用DMA,就配置DMA
3:弱定义
通过前面定义的结构体句柄,来配置串口的波特率、数据字长、停止位、奇偶检验位、硬件流的控制。
通过搜索发现HAL_UART_MspInit()函数是一个弱定义!!对于弱定义的理解就是:如果你自己定义的话就是强定义,那么强定义的功能就会替代这个弱定义的功能,如果你自己没有定义,那么他就默认的会使用这个弱定义来做UNUSED(huart);
4:HAL_UART_Init()
通过调用HAL_UART_Init()函数,来将串口配置为异步模式。HAL_UART_Init(&huart1)传入&huart1这个句柄,那让我们看看这个函数做了什么。
感兴趣的小伙伴可以再看看UART_SetConfig(huart),也就是HAL库是如何从寄存器入手具体实现配置寄存器内容的。其实HAL库的实现机制不是特别的复杂,但就是绕来绕去,有时候会绕的头疼,所以我们一步步的来梳理HAL库,就可以对未来程序的移植等工作有所帮助!
5:…………其他API不常用,如果使用到再详细看
6:配置中断
利用USART做串口回显上位机的实验
———你会运用UART吗?
到这里MX_USART1_UART_Init();的初始化函数就完全梳理一遍了,那么接下来我们就来实现一下串口最简单的功能,也就是通过串口来回显一些字符到上位机(串口调试助手)来显示。
重定向printf()和Scanf
在这里有一个重点,在PC端上用我们可以用《C语言程序设计》中的格式化输出函数Printf()将我们想要输出的字符或者程序的运行结果发送到显示屏中显示,也可以用输入函数Scanf()读取键盘输入信息,但是HAL库提供的串口收发函数功能太过简单,不能进行c语言格式化的输入与输出[Scanf()、Printf()],所以我们如果要实现类似于C语言中的Printf()和Scanf(),我们就必须要将他们重新定向到串口。
Prinf是指向fputc然后指向真正的串口来发送,如果不重新定义fputc的话就不可能用串口来输出。在此谨记,我们用到printf的时候,需要勾选上使用微库。不然所做的一起都是徒劳!!!!亲身经历!!!!!!
HAL库实现解读
原理:调用HAL库串口发送的一个函数HAL_UART_Transmit(),我们为什么要调用HAL_UART_Transmit()来发送呢,这就要去看HOW TO USE THIS DRIVER
发送一定量的数据用HAL_UART_Transmit()
接收一定量的数据用HAL_UART_Receive()
也可以用中断和DMA的方式进行传输
HAL_UART_Transmit()和HAL_UART_Receive()的4个参数
HAL_UART_Transmit()和HAL_UART_Receive()都有4个参数:
(需要用哪个串口发送也就是其串口的句柄,要发送的数据,发送数据的数量,等待时间)。
重定向c库函数printf到串口huart1,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口DEBUG_USART */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
int ch;
HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
然后直接在main.c中调用printf()即可输出到上位机,printf("如果你喜欢这篇文章,可以点赞收藏+关注哟,谢谢你的支持\n"); 输出结果如下:
本文就到这里啦,希望大家通过本文可以对通信和串口USART有深入的了解,毕竟知道外设原理和代码的实现才能更好的去使用外设,如果有错大家可以指出哟,感兴趣或者有不明白的地方的小伙伴可以私信问我!!如果喜欢可以点赞+关注哟!!!