目录
0x01、什么是串口通信
0x02、异步通信与同步通信
0x0001、异步通信
0x0002、同步通信
0x03、电平标准
0x04、串口通讯参数
0x05、库函数
0x0001、复位
0x0002、初始化
0x0003、发送数据
0x0004、接收数据
0x0005、读取串口状态
0x0006、串口使能
0x0007、开启串口响应中断
0x0008、获取相应中断的状态
0x06、自行编写的相关函数
0x01、什么是串口通信
串行接口是一种可以将接收来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接收的串行数据流转换为并行的数据字符供给CPU的器件。一般完成这种功能的电路,我们称为串行接口电路。
串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节的通信方式。
0x02、异步通信与同步通信
0x0001、异步通信
异步通信以一个字节为传输单位,通信中两个字符之间的时间间隔不固定,可以是任意长的,而在同一个字符中的两个相邻位的时间间隔是固定的,接收时钟和发送时钟只要相近就可以。通信双方必须使用约定的相同的一些规则(通信协议)。常见的传送1个字符的信息格式规定有起始位、数据位、奇偶校验位、停止位等。
1、起始位: 先发出一个逻辑”0”信号,表示传输字符的开始。
2、数据位:紧接着起始位之后。数据位的个数可以是 5、6、7、8 等,构成一个字符。一般采用扩展的 ASCII 码,范围是 0~255,使用 8 位表示。首先传送最低位。
3、奇偶校验位(不是必须):奇偶校验是串口通信中一种简单的检错方式,当然没有校验位也是可以的。数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
例如,如果数据是 01100000,那么对于偶校验,校验位为 0。
4、停止位:它是一个字符数据的结束标志。可以是 1 位、1.5 位、2 位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。
因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
5、空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送
0x0002、同步通信
同步通信是指数据传送是以一个帧(数据块或一组字符)为传输单位,每个帧中包含有多个字符。在通信过程中,字符与字符之间、字符内部的位与位之间都同步,每个字符间的时间间隔是相等的而且每个字符中各相邻位代码间的时间间隔也是固定的。
同步通信的特点可以概括为:
1、以数据块为单位传送信息。
2、在一个数据块(信息帧)内,字符与字符之间无间隔
3、接收时钟与发送时钟严格同步
同步串行通信方式中一次连续传输一块数据,开始前使用同步信号作为同步的依据。同步字符的插入可以使单同步字符方式或双同步字符方式,均由同步字符、数据字符和校验字符CRC等三部分组成。
1、同步字符位于帧结构开头,用于确定数据字符的开始
2、数据字符在同步字符之后,字符个数不受限制,由所需传输的数据块长度决定。
3、校验字符有1到2个,位于帧结构结尾,用于接收端对接收到的数据字符的正确性的校验
由于连续传输一个数据块,故收发双方时钟必须相当一致,否则时钟漂移会造成接收方数据辨认错误。这种方式下旺旺是发送方在发送数据的同时也通过一根专门的时钟信号线同时发送时钟信息,接收方使用发送方的时钟来接收数据。同步串行通信方式传输效率高,但对硬件要求高,电路结构复杂。
0x03、电平标准
1、TTL
+5V等价于逻辑“1”,0V等价于逻辑“0”(采用二进制来表示数据时)。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。
2、RS232
逻辑“1”的电平为-5V~-15 V,逻辑“0”的电平为+5 V~+15 V。选用该电气标准的目的在于提高抗干扰能力,增大通信距离。RS -232的噪声容限为2V,接收器将能识别高至+3V的信号作为逻辑“0”,将低到-3 V的信号作为逻辑“1”。
常用的TTL转RS232芯片有MAX232等
3、RS485
采用差分信号负逻辑,逻辑"1”以两线间的电压差为-(2~6)V表示;逻辑"0"以两线间的电压差为+(2~6)V表示。接口信号电平比RS232降低了,不易损坏接口电路的芯片, 且该电平与TTL电平兼容,可方便与TTL电路连接。RS485的数据最高传输速率为10Mbps。
常用的TTL转RS485芯片如SP3485等
0x04、串口通讯参数
1、波特率:这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。一般调制速率大于波特率,比如曼彻斯特编码)。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
2、数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
3、停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
4、奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶检验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
0x05、库函数
由于串口相关的寄存器众多,而且介绍复杂,这里我们就跳过寄存器的介绍,直接介绍通过官方标准库函数和操作串口的一些流程
串口设置的一般步骤可以总结为以下几步:
1、串口时钟使能,GPIO时钟使能
2、串口复位
3、GPIO端口模式设置
4、串口参数初始化
5、开启串口中断并初始化NVIC(如果需要开启中断才需要这个步骤)
6、使能串口
7、编写串口中断处理函数
0x0001、复位
void USART_DeInit(USART_TypeDef* USARTx)
函数功能:将USARTx外围寄存器初始化为其默认重置值。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
返回值:无
0x0002、初始化
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
函数功能:根据USART_InitStruct中指定的参数初始化USARTx外围设备。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
参数2:USART_InitTypeDef* USART_InitStruct —> 指向包含指定USART外围设备的配置信息的USART_InitTypeDef结构体的指针。
返回值:无
0x0003、发送数据
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
函数功能:指定串口向外发送16位数据
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
参数2:uint16_t Data —> 要发送的16位数据
返回值:无
0x0004、接收数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
函数功能:从指定串口接收发送16位数据
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
返回值:接收到的16位数据
0x0005、读取串口状态
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)
函数功能:检查是否设置了指定的USART标志。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
参数2:uint16_t USART_FLAG —> 指定要检查的标志。
返回值:串口的状态FlagStatus
参数2取值的相关宏定义
#define USART_FLAG_CTS ((uint16_t)0x0200)
#define USART_FLAG_LBD ((uint16_t)0x0100)
#define USART_FLAG_TXE ((uint16_t)0x0080)
#define USART_FLAG_TC ((uint16_t)0x0040)
#define USART_FLAG_RXNE ((uint16_t)0x0020)
#define USART_FLAG_IDLE ((uint16_t)0x0010)
#define USART_FLAG_ORE ((uint16_t)0x0008)
#define USART_FLAG_NE ((uint16_t)0x0004)
#define USART_FLAG_FE ((uint16_t)0x0002)
#define USART_FLAG_PE ((uint16_t)0x0001)
0x0006、串口使能
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
函数功能:启用或禁用指定的USART外围设备。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
取值:ENABLE or DISABLE.
返回值:无
0x0007、开启串口响应中断
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
函数功能:启用或禁用指定的USART中断。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
参数2:uint16_t USART_IT —> 指定要启用或禁用的USART中断源。
取值:ENABLE or DISABLE.
返回值:无
参数2取值的相关宏定义:
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
0x0008、获取相应中断的状态
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
函数功能:检查指定的USART中断是否已发生。
参数1:USART_TypeDef* USARTx —> 选择USART或UART外围设备。
参数2:uint16_t USART_IT —> 指定要检查的USART中断源。
返回值:使用的新状态(设置或重置)。
0x06、编写函数
0x001、USART初始化
/**
*@brief USART2初始化
*@param uint32_t BaudRate:串口波特率
*@return 无
*/
void USART2_Init(uint32_t BaudRate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* 使能USART2、GPIOA时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 初始化串口2时钟
/* GPIOA2 USART2_Tx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // TX引脚PA2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA_2
/* GPIOA3 USART2_Rx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // RX引脚PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA_3
/* 设定USART2 中断优先级 */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; // 抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器
/* USART2配置 */
USART_InitStructure.USART_BaudRate = BaudRate; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
USART_Init(USART2, &USART_InitStructure); // 初始化串口2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 开启串口2接受中断
USART_Cmd(USART2, ENABLE); // 使能串口2
}
0x002、串口发送1个字节
/**
*@brief 串口发送1个字节
*@param USART_TypeDef* USARTx:使用的串口号
uint8_t byte :要发送的字节
*@return 无
*/
void USART_Send_Byte(USART_TypeDef* USARTx, uint8_t byte)
{
USART_SendData(USARTx, byte); // 通过库函数 发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)!= SET); // 等待发送完成。 检测 USART_FLAG_TC 是否置1; 见库函数 P359 介绍
}
0x003、串口发送字符串
/**
*@brief 串口发送字符串
*@param USART_TypeDef* USARTx:使用的串口号
char *DataString :字符串
*@return 无
*/
void USART_SendString(USART_TypeDef* USARTx, char *DataString)
{
int i = 0;
USART_ClearFlag(USARTx,USART_FLAG_TC); // 发送字符前清空标志位(否则缺失字符串的第一个字符)
while(DataString[i] != '\0') // 字符串结束符
{
USART_SendData(USARTx, DataString[i]); // 每次发送字符串的一个字符
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == 0); // 等待数据发送成功
USART_ClearFlag(USARTx, USART_FLAG_TC); // 发送字符后清空标志位
i++;
}
}
0x004、串口发送字节数组
/**
*@brief 串口发送字节数组
*@param USART_TypeDef* USARTx:使用的串口号
uint8_t send_array[]:要发送的数组
uint8_t num:数组长度,1-255
*@return 无
*/
void USART_Send_Array(USART_TypeDef* USARTx, uint8_t send_array[], uint8_t num)
{
uint8_t i = 0; // 定义一个局部变量 用来 发送字符串 ++运算
while(i < num)
{
USART_SendData(USARTx, send_array[i]); // 通过库函数发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)!= SET); // 等待发送完成。 检测 USART_FLAG_TC 是否置1; 见库函数 P359 介绍
i++; // 值加一
}
}