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。
因此,通过虚拟串口软件,即可在无开发板、无串口线、无串口设备的情况下,通过串口工具即可调试串口接收及发送程序,实现纯软件环境调试。

 

源码下载:(不包括工程文件和库文件)

https://files.cnblogs.com/files/greatpumpkin/Usart.rar

posted @ 2020-07-21 17:44  心灵航帆  阅读(915)  评论(0编辑  收藏  举报