赞
踩
在平常的开发调试过程中,最常用的方式就是使用串口输出打印进行调试。相较于其他的通信方式,串口通信更加方便,快捷。
下面先介绍一些关于串口通信的基础知识。
之前说过,通信的就是模块之间进行数据传输。传输的数据就是逻辑0和逻辑1。那么如何定义逻辑0和1呢。
常用的有TTL、RS485、RS422、RS232。在不同的应用环境下,选择不同的电平标准。
需要注意的是,RS422、RS232、RS485他们三个需要经过电平转化芯片,把自己变成TTL电平才可以与芯片连接。也就是说不管是什么类型的通信,最终都是TTL电平与芯片相连,只不过是TTL的电平值的高低有所不同。
通常TTL适合近距离通信,一般只有几厘米;RS422、RS232、RS485可以用于远距离通信。
1、波特率:之前介绍过说波特率是系统在单位时间内传输的码元的个数。码元就是通信线上的电平。波特率的简单理解就是每秒传输的bit的个数,比如每秒传输9600个bit,那么波特率就是9600。
2、起始位:表示传输数据的开始,是一帧数据的第一位,表示从这一位以后开始,后面的数据就是要传输的数据。
3、数据位:实际想要传输的数据,在起始位之后,校验位和停止位之前。
4、校验位:用来校验数据传送的正确性。分为奇校验和偶较远。校验位是可选的,可以使用。
5、停止位:它是一帧数据的结束标志,表示这一帧数据结束了。
1、收发双方约定好波特率、数据格式(数据位个数、停止位个数、是否使用校验位、奇校验还是偶校验),假设数据位是8,停止位是1,校验位是1;
2、初始电平为逻辑1;
3、发送方输出逻辑0(起始位信号),并保持1位的时间;接收方检测到逻辑0,就知道对方准备发送数据了;
4、发送方根据数据的bit是0还是1,设置引脚电平,并保持1位的时间;接收方读取引脚电平,得到bit0或1;
5、重复第4步,直到所有的数据发送完毕(这里以8位数据为例)。
6、发送方计算出校验值,设置引脚,并保持1位的时间;接收方读取引脚电平,得到校验值;这步可以省略;
7、发送方输出逻辑1(停止位信号),并保持1位的时间;接收方读取引脚电平,直到数据传输结束;
在嵌入式领域串口一般用来:调试、打印信息;连接外部模块,传输数据;连接工控设备。
STM32F103有3个通用同步异步收发器(Universal synchronous asynchronous receiver transmitter,USART),2个通用异步收发器(Universal asynchronous receiver transmitter,UART)。USART和UART的主要区别在于,USART支持同步通信,该模式有一根时钟线提供时钟。
STM32F103一共有5个串口。3个USART和2个UART。他们支持的功能如下图所示。
根据USART的框图大体可以把USART的结构分为4部分。
1:引脚部分
最主要的引脚是TX:数据发送和 RX:数据接收这两个引脚。
CK:在同步模式时,用于输出时钟;
SW_RX、RTS、CTS这三个引脚不常用。
2:数据收发寄存器
这里面有两种寄存器,分别负责数据的发送和接收。
其中的两个移位寄存器,负责将数据进行准换,实现数据在传输时,是一位一位的发送和接收。
在UART中,只有一个寄存器USART_DR,是个双向寄存器,若CPU对这个寄存器进行读操作,就是读数据RDR;若CPU对USART_DR进行写操作,就是写数据TDR。
3、控制管理单元
这部分是对串口进行设置的。
CR1、2、3是控制寄存器,负责各种功能的使能。CR1主要用于配置USART的数据位、校验位和中断使能,CR2用于配置USART的停止位和SCLK时钟控制,CR3用于CTS硬件流控制、DMA多缓冲控制等。
SR是状态寄存器,表明UART的各种状态。
GTPR是SmartCard和IrDA模式下专用的寄存器。
4、波特率发生器
这部分有一个USART_BRR寄存器,用于设置串口通信数据传输速率也就是波特率。
B
a
u
d
r
a
t
e
=
f
P
C
L
K
16
∗
U
S
A
R
T
D
I
V
Baudrate = \frac {f_{PCLK}}{16*USARTDIV}
Baudrate=16∗USARTDIVfPCLK
设波特率为115200,当前USART时钟为72MHz,则USARTDIV=72000000/(115200*16)=39.0625。
USART_BRR寄存器使用高12位[15:4]存放整数部分,低4位[3:0]存放小数部分,小数部分每一位对应1/(2^4)=0.0625。因此,整数39对应16进制为0x27,左移4位为0x270,小数0.0625,对应0x1,USART_BRR=0x271。
USART1挂载APB1上,USART2、3和USART4、5挂载APB2上。
1、选择使用的串口,得到地址。
2、操作BRR寄存器,设置波特率。
3、根据需求操作各种寄存器,设置停止位、校验位、同步时钟使能位。
4、使能发送和接收位。
5、根据需求使能发送和接收的中断位。
6、使能具体的时钟。
7、使能串口。
8、读USART_DR寄存器接收数据;写USART_TR寄存器发送数据。
HAL库初始化USART的流程很简单。
1、定义结构体。
2、设置结构体成员。
3、使能时钟、设置引脚复用模式。
注意:printf()和scanf()这两个函数一般只用于调试打印信息使用,在具体发布的工程中不宜使用,因为他们消耗时间比较久。
用到的结构体和常用函数如下:
结构体成员:
uint32_t BaudRate;
设置波特率
uint32_t WordLength;
设置数据位长度,在HAL库中只有8位和9位两种长度。
uint32_t StopBits;
停止位,可选0.5、1、1.5、2四种。
uint32_t Parity;
校验位,有奇校验、偶校验和不使用校验。
uint32_t Mode;
工作模式,可以选择只发送、只接受和发送接收都可以。
uint32_t HwFlowCtl;
设置是否支持硬件流控制。
uint32_t OverSampling;
设置过采样。
一般来说后两种成员用不到。
在设置完结构体成员之后,调用HAL_UART_Init(UART_HandleTypeDef *huart)
初始化即可。
注意1:HAL_UART_Init(UART_HandleTypeDef *huart)
这个初始化函数的参数不是上面的结构体UART_InitTypeDef
,而是UART_HandleTypeDef
这个结构体。成员配置的结构体UART_InitTypeDef
是UART_HandleTypeDef
其中的一个成员。
在配置串口的一些寄存器的时候,要先失能串口才能进行配置,配置完成之后再使能串口。HAL库中已经实现,用户不用担心,知道这个情况即可。
串口发送函数:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
串口接收函数:
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
参数:huart
是具体的串口;pData是发送或接受数据的首地址;Size是发送或接受数据的个数;Timeout是一个超时时间,若在发送或接受函数中,Timeout是超时时间,代表某次执行函数,最多占用串口的时间,单位是毫秒。简单来说,在本次数据发送完之前,不能发送别的数据。所以,调用函数的时候要指明参数,本次发送占用多长时间,在此期间,由于串口资源被独占,不能成功调用发送函数。如果在规定的时间内,数据发送完毕,那就释放占用的串口资源;如果到了时间,即便数据还没有发送完毕,(比如数据量很大),仍需要归还串口资源的控制权,让别人来用。此时函数返回值是HAL_TIMEOUT
。
printf
和scanf
在内部会调用fgetc
和fputc
,所以重新编写fgetc
和fputc
就可以在printf
和scanf
内部加入串口发送和接收功能了。
串口接收到“kaishi”。
接收数据放在receivenum中,可以看到在开始的时候数值为0,然后接收到数据5(ASCII格式)。
在使用HAL_UART_Transmit
和HAL_UART_Receive
这两个函数的时候,第一个参数huart不是具体的串口,而是UART_HandleTypeDef
类型的串口句柄。在初始化的时候,UART_HandleTypeDef
中的第一个成员Instance
才是具体的串口,husart.Instance = USARTA;
也就是调用这两个函数的时候:
HAL_UART_Transmit(&USARTA, “Hello Word”, 10, 1000);
错误
UART_HandleTypeDef *huart
HAL_UART_Transmit(&huart, “Hello Word”, 10, 1000);
正确
在用HAL_UART_Init(UART_HandleTypeDef *huart)
初始化的时候,参数也是UART_HandleTypeDef
类型的,而不是具体的串口类型USART_TypeDef
USART1
。
配置串口就两部分,一个是配置引脚,一个是配置串口模式。注意配置引脚要在配置串口之前,因为在串口初始化函数HAL_UART_Init
中,会调用HAL_UART_MspInit
这个函数对引脚进行配置,要是先初始化串口,此时在内部串口的引脚就初始化了,但是实际上串口的引脚没有配置,然后在外部对串口的引脚配置好了,这时候配置好的引脚和串口连接不上,所以串口无法使用。所以要先配置引脚在配置串口。或者重定位HAL_UART_MspInit
这个函数,在这个函数内部进行引脚配置,在串口初始化的时候会自动调用这个函数,完成串口和引脚的关联。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。