目录
- 一、什么是USART
-
- 1. USART简介
- 2. STM32F4中的USART
-
- 2.1 USART的发送/接收引脚
- 2.2 USART转为USB接口
- 二、常用的串口相关寄存器
- 三、程序编写
-
- 1. 串口配置的一般步骤
- 2. 编写程序
参考正点原子的视频教程,本文我们将编写一段以USART作为通信串口、接收到数据后立即引发中断、并执行中断处理函数将数据发送给MCU的程序。
一、什么是USART
1. USART简介
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)的全称为通用同步/异步串行接收/发送器,它与普通串口UART的不同在于,USART有同步、异步两种工作模式;而UART是经过裁剪后的USART,只有异步工作模式。
2. STM32F4中的USART
2.1 USART的发送/接收引脚
STM32F4中有两个USART(USART1、USART2),其中,我们以USART1为例,它的发送、接收与PA9、PA10引脚相连,从如下的GPIO引脚复用图中可以看出,PA9可以复用为USART1的发送(TX)功能,而PA10可以复用为USART1的接收(RX)功能。
2.2 USART转为USB接口
单片机通常需要与电脑互相传输数据,但是电脑没有USART接口,这该怎么办呢?设计者通常通过一个芯片把USART接口转换为USB接口,这样就可以与电脑通信了。
能实现USART转USB的芯片有很多,STM32F4中使用的是CH340G。
STM32F4中USART1转USB的原理图如下所示:
其中,TXD/RXD 是相对于 CH340G 来说的,也就是 USB 串口的发送和接受脚。而 USART1_RX/USART1_TX 则是相对于 STM32F407ZGT6 来说的。这样,通过对接,就可以实现 USB 串口和 STM32F407ZGT6 的串口通信了。
注:在本文的实验中,就是用USB串口把USB信号转换为串口信号,进而通过PA9和PA10的复用功能来与单片机进行通信。
二、常用的串口相关寄存器
常用的串口相关寄存器有三个:
- USART_SR 状态寄存器,用来记录一些状态,如是否接收到数据,是否要发送数据等
- USART_DR 数据寄存器,用来存储数据,包括要接收的数据和要发送的数据等
- USART_BRR 波特率寄存器,用来调整波特率的大小。
其中,USART_SR和USART_DR的各个位的含义在**《STM32F4xx中文参考手册》的26.6节可以查到, 本文不再赘述。这里只简要介绍一下USART_BRR**波特率寄存器的内部结构:
在上篇博文【STM32F4】四、串口通信1——硬件部分中我们在第三部分列出过,下面我们只把波特率发生器的硬件部分展示在下图中:
首先由USART_BRR寄存器产生初始的时钟信号,假设频率为** f ;输入到分频系数为USARTDIV的分频器后,输出信号频率变为 f / USARTDIV**;在经过采样除法器后,最终输出信号的波特率为** f / USARTDIV / [8 x (2 - OVER8)] **,其中,OVER8可人为设置。
其中,初始频率 f 通常是固定的,OVER8 通常设为0;那么公式就简化为波特率 = f / USARTDIV / 16
。而为了得到最终的波特率,我们要求的其实就是唯一的可变参数USARTDIV
,实际上它也是由USART_BRR寄存器决定的。
但在程序中,我们不需自己计算USART_BRR的配置。我们只要把想要的波特率(如115200)直接写入程序、传给相应函数即可,STM32F4提供的库函数会帮我们计算并对USART_BRR进行配置。
三、程序编写
1. 串口配置的一般步骤
根据正点原子的课程,列出串口配置(带中断响应)的一般过程如下:
① 必要的时钟使能
- 串口时钟使能:RCC_APBxPeriphClockCmd();
- GPIO时钟使能:RCC_AHB1 PeriphClockCmd();
注:要想使用一个外设,必须要对【外设】、以及【连接外设的GPIO引脚】的时钟进行使能。
② 引脚复用映射:GPIO_PinAFConfig():
③ GPIO端口模式设置:GPIO_Init(); //模式设置为GPIO_Mode_AF
④ 串口参数初始化:USART_Init(); //配置波特率等参数
⑤ 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤):
- NVIC_Init();
- USART_ITConfig();
⑥ 使能串口:USART_Cmd();
⑦ 编写中断处理函数:USARTx_IRQHandler();
⑧ 串口数据收发:
- void USART_SendData(); //从DR寄存器中将数据发送出去
- unit16_t USART_ReceiveData(); //从DR寄存器读取接收到的数据
⑨:串口传输状态获取:
- FlagStatus USART_GetFlagStatus();
- void USART_ClearITPendingBit();
下面我们也将按照上述步骤,一一编写程序。
2. 编写程序
下面我们把经过详细注释的代码放上来,全都是按照上述九个步骤来写的:
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
void My_USART1_Init(void) //配置和初始化的程序,除中断处理函数外,其他的配置都在这里面
{
GPIO_InitTypeDef GPIO_InitStructure; //用于GPIO配置的结构体
USART_InitTypeDef* USART_InitStruct; //用于USART配置的结构体
NVIC_InitTypeDef* NVIC_InitStruct; //用于NVIC配置的结构体
//===============================一、串口时钟使能===================================
//使能USART1,由于USART1挂载在APB2总线下,所以要去RCC相关的库函数中搜索APB2的时钟使能函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//=================================GPIO时钟使能=====================================
//因为要通过PA9和PA10的复用功能来使用UART1,所以也要使能GPIOA的时钟,GPIOA挂载在AHB1总线下
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//===============================二、引脚复用映射===================================
//通过下面这个函数,把PA9配置为复用功能——USART1_TX,PA10配置为复用功能——USART1_RX
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//=============================三、端口模式设置==================================
//下面要配置PA9和PA10配置为复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //GPIO_Mode_OUT; //AF即复用模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//================================四、串口参数初始化=================================
//初始化USART1的配置
USART_InitStruct->USART_BaudRate = 115200; //波特率
USART_InitStruct->USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
USART_InitStruct->USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //把发送和接收功能都进行使能
USART_InitStruct->USART_Parity = USART_Parity_No;//不使用奇偶校验
USART_InitStruct->USART_StopBits = USART_StopBits_1;//使用1个停止位
USART_InitStruct->USART_WordLength = USART_WordLength_8b;//因为没有奇偶检验,所以可以使用8位字长
USART_Init(USART1, USART_InitStruct);
//====================================================================
//=============如果不使用中断,那么这个程序到这里就可以结束了=============
//====================================================================
//=============================五、开启中断并且初始化NVIC============================
//配置NVIC
//首先设置中断优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//再初始化NVIC
NVIC_InitStruct->NVIC_IRQChannel = USART1_IRQn;//不同的通道定义在顶层头文件stm32f4xx.h中 //设置NVIC通道为USART1的通道
NVIC_InitStruct->NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority = 1;//设置抢占优先级为1
NVIC_InitStruct->NVIC_IRQChannelSubPriority = 1;//设置响应优先级为1
NVIC_Init(NVIC_InitStruct);
//==================================六、使能串口====================================
//使能USART1
USART_Cmd(USART1, ENABLE);
//使能USART1的某种中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//把接收非空中断USART_IT_RXNE使能,即一旦接收到了信息,就引发中断,且执行相应的中断函数
//下一步就要在下面定义USART1的中断服务函数,函数名是固定的,在官方的系统系统文件中startup_stm32f40_41xxx.s已经给出:USART1_IRQHandler
}
//================================七、编写中断处理函数===============================
void USART1_IRQHandler(void)
{
u8 res; //用来记录接收到的数据,因为我们在上面设置的8位字长代表一个数据,所以这里可以用u8来记录?
//==============================九、串口传输状态获取=================================
if(USART_GetITStatus(USART1, USART_IT_RXNE)) //读取USART_IT_RXNE标志位的状态)
{
//================================八、串口数据收发==================================
res = USART_ReceiveData(USART1); //读取接收到的数据
USART_SendData(USART1, res); //把接收到的数据发出去
}
}
//主函数
int main(void)
{
My_USART1_Init();
while(1); //在这里无限循环即可,程序会自动执行 中断 和 中断处理函数
return 0;
}
注: 如果编译时程序出错,可能是因为在官方库文件usart.c里已经定义过一个中断处理函数USART1_IRQHandler()
,把它注释掉或者把这个文件删掉即可。