STM32之USART串口
本文介绍如何使用STM32标准外设库驱动USART串口,本例程使用串口1接收及发送一些数据。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。
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)
完整代码(仅自己编写的部分)
1 void NVIC_Configuration(void) 2 { 3 NVIC_InitTypeDef NVIC_InitStructure; 4 5 /* 嵌套向量中断控制器组选择 */ 6 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 7 8 /* 配置USART为中断源 */ 9 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 10 /* 抢断优先级*/ 11 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 12 /* 子优先级 */ 13 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 14 /* 使能中断 */ 15 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 16 /* 初始化配置NVIC */ 17 NVIC_Init(&NVIC_InitStructure); 18 } 19 20 void USART_Config(uint32_t baudrate) 21 { 22 GPIO_InitTypeDef GPIO_InitStructure; 23 USART_InitTypeDef USART_InitStructure; 24 25 // 打开串口GPIO的时钟 26 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 27 28 // 打开串口外设的时钟 29 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 30 31 // 将USART Tx的GPIO配置为推挽复用模式 32 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 33 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 34 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 35 GPIO_Init(GPIOA, &GPIO_InitStructure); 36 37 // 将USART Rx的GPIO配置为浮空输入模式 38 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 39 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 40 GPIO_Init(GPIOA, &GPIO_InitStructure); 41 42 // 配置串口的工作参数 43 // 配置波特率 44 USART_InitStructure.USART_BaudRate = baudrate; 45 // 配置 帧数据字长 46 USART_InitStructure.USART_WordLength = USART_WordLength_8b; 47 // 配置停止位 48 USART_InitStructure.USART_StopBits = USART_StopBits_1; 49 // 配置校验位 50 USART_InitStructure.USART_Parity = USART_Parity_No ; 51 // 配置硬件流控制 52 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 53 // 配置工作模式,收发一起 54 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 55 // 完成串口的初始化配置 56 USART_Init(USART1, &USART_InitStructure); 57 58 // 串口中断优先级配置 59 NVIC_Configuration(); 60 61 // 使能串口接收中断 62 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 63 64 // 使能串口 65 USART_Cmd(USART1, ENABLE); 66 } 67 68 //发送一个字节 69 void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch) 70 { 71 //发送一个字节数据到USART 72 USART_SendData(pUSARTx,ch); 73 //等待发送数据寄存器为空,即发送完成 74 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); 75 } 76 77 //发送数组 78 void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num) 79 { 80 uint8_t i; 81 82 for(i = 0; i < num; i++) 83 { 84 /* 发送一个字节数据到USART */ 85 Usart_SendByte(pUSARTx, array[i]); 86 } 87 /* 等待发送完成 */ 88 while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET); 89 } 90 91 //发送字符串 92 void Usart_SendString(USART_TypeDef * pUSARTx, char *str) 93 { 94 uint32_t k = 0; 95 96 do 97 { 98 Usart_SendByte(pUSARTx, *(str + k)); 99 k++; 100 } while(*(str + k)!='\0'); 101 102 /* 等待发送完成 */ 103 while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); 104 } 105 106 //发送16位数 107 void Usart_SendHalfWord(USART_TypeDef * pUSARTx, uint16_t ch) 108 { 109 uint8_t temp_h, temp_l; 110 111 /* 取出高八位 */ 112 temp_h = (ch & 0xFF00) >> 8; 113 /* 取出低八位 */ 114 temp_l = ch & 0xFF; 115 116 /* 发送高八位 */ 117 USART_SendData(pUSARTx, temp_h); 118 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); 119 120 /* 发送低八位 */ 121 USART_SendData(pUSARTx, temp_l); 122 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); 123 } 124 125 // 串口中断服务函数 126 void USART1_IRQHandler(void) 127 { 128 uint8_t ucTemp; 129 130 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 131 { 132 ucTemp = USART_ReceiveData(USART1); 133 USART_SendData(USART1, ucTemp); 134 } 135 } 136 137 int main(void) 138 { 139 USART_Config(9600); 140 141 Usart_SendString(USART1, "Hello World!\n"); 142 143 while(1) 144 { 145 } 146 }
仿真结果
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。
因此,通过虚拟串口软件,即可在无开发板、无串口线、无串口设备的情况下,通过串口工具即可调试串口接收及发送程序,实现纯软件环境调试。