1. 简介
串口全称:Universal synchronous asynchronous receiver transmitter,中文名称:通用同步异步串行接收发送器。串口可用于接收和发送数据,可以用作跟其他设备通讯,如蓝牙模块、GSM模块、GPS模块等。
串口中最常见的是RS-232,共有9根线用于通讯,但常用的仅3根线:RXD、TXD、GND。其中RXD用于接收,TXD用于发送,GND为信号地线,两个通讯设备之间的收发信号(RXD与TXD)应交叉相连。
2. 基本概念
- 波特率:串口异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为4800、9600、115200 等。波特率越高传输速度越快,但出错的概率要更大。
- 通讯的起始和停止信号:串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5或 2个逻辑 1的数据位表示,只要双方约定一致即可。
- 有效数据:在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7或 8位长。
- 数据校验:在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无校验(noparity)。奇校验要求有效数据和校验位中“1”的个数为奇数,偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,0校验是不管有效数据中的内容是什么,校验位总为“0”,1校验是校验位总为“1”。
- 常用的设置:波特率9600,8位数据位,1位停止位,无校验位,无硬件流控。
串口USART分为两部分,初始化和处理。
1. 初始化分三步:GPIO、通用中断、USART。
1.1. GPIO:时钟、引脚、输入输出模式、端口初始化
- 时钟:需要同时使能GPIO时钟
- 引脚:需要选择指定的引脚
- 输入输出模式:TX引脚选择复用推挽模式,RX引脚选择浮空输入模式
- 端口初始化:初始化指定的端口
1.2. 通用中断:优先级分组、中断源、优先级、使能
- 优先级分组:设定合适的优先级分组
- 中断源:选择指定的中断源:USART1_IRQn
- 优先级:设定合适的优先级
- 使能:调用库函数即可,可选择使能多个中断,一般情况下只使能接收中断。
1.3. USART:时钟、波特率、数据字长、停止位、校验位、硬件流控制、工作模式、初始化、使能。
结构体
typedef struct {
uint32_t USART_BaudRate;
uint16_t USART_WordLength;
uint16_t USART_StopBits;
uint16_t USART_Parity;
uint16_t USART_Mode;
uint16_t USART_HardwareFlowControl;
} USART_InitTypeDef;
- 时钟:需要使能USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
- 波特率:一般可选的设置项包括4800、9600、115200等,常用的波特率为9600。
USART_InitStructure.USART_BaudRate = 9600;
- 数据字长:可选8位或9位,常用的是8位。
USART_InitStructure.USART_BaudRate = 9600;
- 停止位:可选0.5、1、1.5、2个,常用的是1个停止位。
USART_InitStructure.USART_BaudRate = 9600;
- 奇偶校验:可选无校验、奇校验和偶校验,常用的是无校验。
USART_InitStructure.USART_Parity = USART_Parity_No;
- 模式:可选发送、接收,或者同时启用,一般都是同时启用。
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
- 硬件流控:可选有使能RTS、使能CTS、同时使能、不使能,一般选择不使能。
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
- 初始化配置
USART_Init(USART1, &USART_InitStructure);
- 使能
USART_Cmd(USART1, ENABLE);
2. 处理
处理分为接收和发送。
接收处理通常放在中断中,中断服务函数名称为USART1_IRQHandler(),调用库函数USART_ReceiveData()得到接收的数据,并自动清除接收中断标志位;
发送直接调用库函数USART_SendData()即可,发送完成之后会调用函数来获取相应标志来实现发送完成等待功能,即USART_SendData()函数返回时,相应的数据已经确保发送完成,并可发送下一个数据,发送不同类型的数据可以封装成不同的函数,但最终都是调用USART_SendData()函数。
接收:
// 串口中断服务函数
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(USART1);
}
}
发送:
//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch)
{
//发送一个字节数据到USART
USART_SendData(pUSARTx,ch);
//等待发送数据寄存器为空,即发送完成
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
//发送一个数组
void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
//发送字符串
void Usart_SendString(USART_TypeDef * pUSARTx, char *str)
完整代码(仅自己编写的部分)
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void USART_Config(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 打开串口外设的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = baudrate;
// 配置 帧数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
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(USART1, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(USART1, ENABLE);
}
//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch)
{
//发送一个字节数据到USART
USART_SendData(pUSARTx,ch);
//等待发送数据寄存器为空,即发送完成
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
//发送数组
void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i = 0; i < num; i++)
{
/* 发送一个字节数据到USART */
Usart_SendByte(pUSARTx, array[i]);
}
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}
//发送字符串
void Usart_SendString(USART_TypeDef * pUSARTx, char *str)
{
uint32_t k = 0;
do
{
Usart_SendByte(pUSARTx, *(str + k));
k++;
} while(*(str + k)!='0');
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
//发送16位数
void Usart_SendHalfWord(USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
/* 取出高八位 */
temp_h = (ch & 0xFF00) >> 8;
/* 取出低八位 */
temp_l = ch & 0xFF;
/* 发送高八位 */
USART_SendData(pUSARTx, temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低八位 */
USART_SendData(pUSARTx, temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 串口中断服务函数
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ucTemp = USART_ReceiveData(USART1);
USART_SendData(USART1, ucTemp);
}
}
int main(void)
{
USART_Config(9600);
Usart_SendString(USART1, "Hello World!n");
while(1)
{
}
}
仿真结果
1. 发送
编译完成后,点击
开始仿真,点击
,打开串口监视串口(点击右侧的箭头可以选择第几个串口),然后运行程序,可以在串口监视窗口看到串口发送的信息。
2. 接收
如果想要仿真串口接收,需要使用虚拟串口软件vspd,下载安装完成后,运行该软件:
可以看到左侧有COM1-5共 5个物理串口,点击右侧的添加端口,可以添加COM6和COM7两个虚拟串口,并自动连接这两个串口。
此时可以打开2个串口工具,分别设置为COM6和COM7,并分别发送数据,看看对方是否能够正常接收,如果可以,表示虚拟串口添加并连接成功,此时在串口工具中关闭COM6。
运行KEIL,并开始仿真后,在COMMAND串口中输入
MODE COM6 9600,0,8,1
ASSIGN COM6 <S1IN> S1OUT
第一条命令设置COM6波特率9600,无校验位,8个数据位,1个停止位,注意这里的设置应该与程序中串口的设置保持一致。
第二条命令将COM6和单片机的第一个串口进行绑定,即单片机第一个串口的收发数据通过计算机的COM6进行收发。
此时在串口工具打开COM7,并通过串口工具发送数据,程序即可接收到发送的数据。
从上图可以看到从COM7发送了一个A给单片机,单片机接收到了A,并将A又发送出去,COM7接收到了A。
因此,通过虚拟串口软件,即可在无开发板、无串口线、无串口设备的情况下,通过串口工具即可调试串口接收及发送程序,实现纯软件环境调试。