5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)

目录

前言

理论学习

一、USART介绍 

1.1、USART简介

1.2、USART主要特征

1.3、USART功能说明

1.4、USART模式配置(串口的波特率、状态、控制)

二、IO口复用功能输入/输出

2.1 复用功能简介

 2.2 复用功能配置

2.3 USART1对应的复用功能映射引脚

三、NVIC中断优先级管理(串口中断)

3.1 NVIC特征

3.2 中断管理方法

3.3 中断设置相关寄存器

3.3 中断优先级设置步骤 

3.4 USART相关的中断事件

实战演练 

一、设计规划

1.1 实验目标 

1.2 硬件资源

二、程序设计 

2.1 建立工程

2.2 串口配置讲解

2.3 主函数text.c

三、在线调试

四、上板验证


前言

使用的是正点原子的探索者开发板进行学习,芯片:STM32F407ZGTx

学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。


本文档添加了对代码的在线调试功能,有助于大家更好理解相关寄存器的变化。

不用正点原子提供的中断文件uart文件,手把手从头自己编写串口通讯。


理论学习

一、USART介绍 

关于串口的理论可以参考,本人的另一篇博客
1、串口(UART/COM/TTL/RS232/RS485)_追逐者-桥的博客-CSDN博客嵌入式MCU串口通信的基础知识https://blog.csdn.net/ARM_qiao/article/details/125103127在嵌入式中所说的串口也就是USART,因此本片博文是直接对USART做简略介绍。

这里是对STM32F4系列的串口进行介绍。

1.1、USART简介

        通用同步异步收发器(USART)是全双工数据交换,满足外部设备对工业标准NRZ异步串行数据格式要求。通过小数波特率发生器提过多种波特率。支持同步单向通信和半双工单线通信;还支持LIN(局域互连网络)、智能卡协议与IrDA(红外数据协会)SIR ENDEC规范;调制解调器操作(CTS/RTS);多处理器通讯;可以通过配置多个缓冲区使用DMA实现高速数据通讯。

1.2、USART主要特征

  • 全双工异步通讯、单线半双工通信
  • 具有小数波特率发生器,可编程收发波特率
  • 可配置16倍过采样或8倍过采样,因而速度容差与时钟容差的灵活配置提供可能
  • 数据长度可编程(8位或9位),停止位可配置(1位或2位)
  • 可以用于同步发送的发送器时钟输出
  • 具有4个错误标志和10个标志位的中断源
  • 两个接收器唤醒模式:地址位(MSB,第9位),线路空闲

1.3、USART功能说明

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第1张图片

  • 引脚说明:RX-接受数据引脚;TX-发送数据引脚;SCLK-发送器时钟输出;nCTS-"清除以发送"用于在当前传输结束时阻止数据发送(高电平时);nRTS-"请求以发送"用于指示USART已准备好接收数据(低电平)。
  • 正常USART模式下(异步全双工),RX、TX以帧的形式发送和接收串行数据:1、发送或接收前保持空闲线路;2、起始位;3、数据(字长 8 位或 9 位);4、最低有效位在前、用于指示帧传输已完成的 0.5 个、1 个、1.5 个、2 个停止位;5、该接口使用小数波特率发生器 - 12 位尾数和 4 位小数;6、状态寄存器 (USART_SR);7、数据寄存器 (USART_DR);8、波特率寄存器 (USART_BRR) - 12 位尾数和 4 位小数;9、智能卡模式下的保护时间寄存器 (USART_GTPR)
  • 同步模式下:RX、TX、SCLK引脚
  • 硬件流控制模式下:RX、TX、SCLK、nCTS、nRTS

1.3.1 传输的数据结构

  • 起始位
  • 数据位(8位或9位)
  • 奇偶校验位(第9位)
  • 停止位(1,15,2位)

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第2张图片

 可通过对 USART_CR1 寄存器中的 M 位进行编程来选择 8 位或 9 位的字长。

1.3.3 实现的功能

这里不对功能进行详细介绍,如果想深入了解请参考芯片的参考手册。

发送器、接收器、小数波特率生成、接收器对时钟偏差的容差、多处理器通讯、奇偶校验控制、LIN(局域互连网络)模式、同步模式、单线半双工通讯、智能卡、IrDA SIR END EC模块、使用DMA进行连续通讯、硬件流控制。

1.4、USART模式配置(串口的波特率、状态、控制)

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第3张图片

        本博客只是简单的实现串口的基本异步通讯功能,USB串口和电脑通讯。不做过多深入的介绍。 串口最基本的设置,就是波特率的设置。STM32F4的串口,只要开启串口时钟、并设置相应IO口的模式、配置一下波特率、数据长度、奇偶校验位等信息,就可以使用了。

  • 串口时钟使能:APB1/2ENR
  • 串口控制:每个串口都有3个控制寄存器USART_CR1~3
  • 数据发送与接收:数据寄存器USART_DR(双寄存器),TDR(写入)和RDR(读取)
  • 串口状态:状态寄存器USART_SR
  • 串口波特率设置:波特率寄存器USART_BRR

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第4张图片

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第5张图片

以上是相关的寄存器,寄存器具体相关位配置请参考芯片的参考手册

二、IO口复用功能输入/输出

2.1 复用功能简介

        每个IO引脚都有一个复用器,采用16路复用功能(AF0到AF15),有两个寄存器(GPIOx_AFRL和GPIOx_AFRH)可用来从每个IO可用的16个复用功能输入、输出中进行选择。借助这些寄存器,可以将引脚配置为复用功能。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第6张图片

 2.2 复用功能配置

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第7张图片

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第8张图片

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第9张图片 除ADC和DAC配置为模拟通道以外其他一律为复用功能。 

2.3 USART1对应的复用功能映射引脚

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第10张图片

三、NVIC中断优先级管理(串口中断)

NVIC简介_嗯哦对的博客-CSDN博客_什么是nvic

3.1 NVIC特征

        STM30F40X具有92个中断(10个内核中断,82个可屏蔽中断),16个可编程优先级(使用了4位中断优先级),低延迟异常和中断处理。

3.2 中断管理方法

首先将STM32中断分为0~4组,每组都有一个抢占优先级和响应优先级。通过寄存器SCB->AIRCR来配置。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第11张图片

  • 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
  • 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
  • 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
  • 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

注:常用 ISER、ICER 和 IP 这三个寄存器,ISER 用来使能中断,ICER 用来失能中断,IP 用来设置中断优先级。

3.3 中断设置相关寄存器

与之相匹配的结构体:

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第12张图片

  • __IO uint8_t  IP[240]; //中断优先级控制的寄存器组,Interrupt Priority Registers

240个8位寄存器,每个中断使用一个寄存器来确定优先级,STM32F40x系列一共82个可屏蔽中断,使用IP[81]~IP[0]。每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。 例如:NVIC->IP[37] = 0xf0;   //对应将USART1的中断,设置位抢占2响应2.

这里的37是指中断向量表中的位置

  • __IO uint32_t ISER[8]; //中断使能寄存器组

32位寄存器,每个位控制一个中断的使能。STM32F40x只有82个可屏蔽中断,所以只使用了其中的ISER[0]~ISER[2]。 ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~63;ISER[2]的bit0~17对应中断64~81;   例如:NVIC->ISER[1] = 1<<(37%32);    

  • __IO uint32_t ICER[8]; //中断失能寄存器组,配置方法与ISER相同
  • __IO uint32_t ISPR[8]; //中断挂起寄存器组
  • __IO uint32_t ICPR[8]; //中断解挂寄存器组
  • __IO uint32_t IABR[8]; //中断激活标志位寄存器组,只读,为1时,说明中断正在执行。

3.3 中断优先级设置步骤 

一般情况下,系统代码执行过程中,只执行一次中断优先级分组。

  1. 设置中断优先级分组
  2. 针对每个中断,设置对应的抢占优先级和响应优先级
  3. 如果需要挂起和解挂,查看中断当前激活状态,分别调用相关函数。

3.4 USART相关的中断事件

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第13张图片

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第14张图片

实战演练 

一、设计规划

1.1 实验目标 

实现如下功能: STM32F4 通过串口和上位机的对话,STM32F4 在收到上位机发过来的字符串后,原原本本的返回给上位机。

1.2 硬件资源

(1)指示灯DS0;(2)串口1

本实验用到了串口1,但PCB上并没有与USB链接,需要使用跳线帽将其硬件连接。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第15张图片

二、程序设计 

2.1 建立工程

  为了更加深入的学习串口的配置,这里单独建立了USART1的配置文件,配置用来学习。

为什么要单独配置,在实际应用串口时,不仅仅只用串口1,但是正点原子只提供了USART1的配置文件,如果不深入只是走马观花而已。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第16张图片

2.2 串口配置讲解

参考资料:

stm32f407之USART(操作寄存器)_w471176877的博客-CSDN博客

STM32F407 纯寄存器操作GPIO,串口,中断(专治花里胡哨)_大大U的博客-CSDN博客

  • 常用的串口相关寄存器:USART_SR(状态)、DR(数据)、BRR(波特率)、CR1~3(控制)
  • 设置步骤:

1.     设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

2.     使能相关时钟:GPIO(PA9/10)、串口(USART1)。

3.     设置相关GPIO引脚为复用模式【MODE】,引脚的速度,方式。

5.     选择相关GPIO引脚的复用功能(【AFR】映射到USART1)。

6.     设置波特率【BRR】,  设置控制寄存器【CR1~3】。

8.     如果要用到中断,设置USART中断优先级,使能USART中断。

10.  如果要用中断,编写中断服务函数(函数名是固定的)。

11.  中断服务函数里检查是哪个中断。

12.  编写相应服务程序。

对以上步骤进行总结:GPIO、USART、中断优先级、串口中断服务函数、主函数

  • usart1.h
#ifndef __USART1_H
#define __USART1_H 
#include "sys.h"
#include "stdio.h"
#define USART1_MAX_REC_LEN  200       

extern u8 USART1_RX_BUF[USART1_MAX_REC_LEN];   //接收数据缓存区
extern u8 RX_data_counter;				       //接收字节计数器
extern u8 RX_stat_fin;                         //串口接收状态,1接收完成,0接收未完成

void usart1_init(u32 pclk2,u32 bound);         
#endif
  • usart1.c
#include "usart1.h" 
#include "sys.h"

	u8 USART1_RX_BUF[USART1_MAX_REC_LEN];
	u8 RX_data_counter=0;
	u8 RX_stat_fin=0;
//USART1中断服务函数
	void USART1_IRQHandler(void)
{
	if(USART1->SR & (1<<5))                              //判断是否接收完成
	{  
		USART1_RX_BUF[RX_data_counter] = USART1->DR;
		RX_data_counter++;
		if(USART1_RX_BUF[RX_data_counter-1] == 0X0D)    //0X0D:结束标志,'\r'回车键 '\n'
		{          
			RX_data_counter = 0;
			USART1->CR1 &= ~(1<<5);       //清除中断使能
			RX_stat_fin = 1;              //接收完成标志
		}	
	}
}

//串口1初始化:pclk2:APB2的时钟 84M  bound:波特率 115200
void usart1_init(u32 pclk2,u32 bound)
{
//求除波特率对用的BRR相关位的值
	float temp;
	u16 mantissa;
	u16 fraction;
	temp=(float)(pclk2*1000000)/(bound*16);
	mantissa=temp;				         
	fraction=(temp-mantissa)*16; 
    mantissa<<=4;
	mantissa+=fraction;  
//时钟使能    
 	RCC->AHB1ENR |= 1<<0;	  //GPIOA
	RCC->APB2ENR |= 1<<4;     //USART1 
//GPIO:	AF PP 50M PU 
	GPIOA->MODER &= 0XFFC3FFFF;
	GPIOA->MODER |= 0X00280000;   //PA9/10 AF
	GPIOA->OSPEEDR &= 0XFFC3FFFF;
	GPIOA->OSPEEDR |= 0X00280000;  //PA9/10  50M
	GPIOA->PUPDR &= 0XFFC3FFFF;
	GPIOA->PUPDR |= 0X00140000;     //PA9/10 PU
//GPIOA->AF(USART1)
	GPIOA->AFR[1] &= 0XFFFFF00F;  
	GPIOA->AFR[1] |= 0X00000770;
//波特率寄存器设置
	USART1->BRR=mantissa;	
	USART1->CR1&=~(1<<15);
//USART1控制
	USART1->CR1 |= 1<<3;      //串口发送使能
	USART1->CR1 |= 1<<2;      //串口接收使能
	USART1->CR1 |= 1<<5;      //接收缓冲区非空中断使能,ORE=1或RXNE=1时,自动置为,生成中断
	USART1->CR1 |= 1<<13;     //串口使能
//中断优先级设置,这里未进行分组,将分组放到了主函数(分组最好只分一次)
	NVIC->IP[37] = 0xf0;             //相关寄存器配置请参考前面的理论学习部分
	NVIC->ISER[1] = 1<<(37%32);	


这里注意时钟的使能:RCC->APB2ENR |= 1<<4;     //USART1 ,外设是挂在再APB总线上。

而不是AHB2ENR |= 1<<4,如过这里出错将无法往寄存器中写值。

2.3 主函数text.c

此时已经将串口配置完成,也声明了接收数据的变量。要将接收到的数据发送回去,就需要在主函数中进行发送即,将接收的数据重新写入到串口的DR寄存器中即可。

这里说一下数据格式:串口中断服务函数,在接收数据时判断是否有回车键作为接收完成标志, //0X0D:结束标志,'\r'回车键 (在调试的时候会观察数据)。

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "led.h"

int main(void)
{
	char TX_data_counter = 0;       //发送计数器
	Stm32_Clock_Init(336,8,2,7);     
	delay_init(168); 
    SCB->AIRCR |= 0X05FA0000 | 0x500;    //在这里设置中断组号
	usart1_init(84,115200);    
	LED_Init();
	
	while(1)
	{
		if(RX_stat_fin)
		{
			LED0 = 1;
			LED1 = 0;           //嵌入式发送指示灯
            delay_ms(10);     
			//for(j=0;jSR & (1<<7))         
			{
				USART1->DR = USART1_RX_BUF[TX_data_counter];
				TX_data_counter++;
				if(USART1_RX_BUF[TX_data_counter] == '\r')      //将回车键'r'传回
				{
					USART1->DR = USART1_RX_BUF[TX_data_counter];
					TX_data_counter = 0;
					USART1->CR1 |= 1<<5;
					RX_stat_fin = 0;
				}				
			}			
		}
		else
		{
			LED0=0;	
			LED1=1;
		}		
	}	
}

三、在线调试

下载完成完成后,打开调试,点击运行,并调出RCC、GPIOA、USART1相关的寄存器,,添加变量USART1_RX_BUF观察接收的数据。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第17张图片

 在串口初始化函数处打断点,并进入函数观察寄存器的变化,点击

 点击进入串口初始化函数内部,并在函数内部结束处打一个断点观察寄存器的变化。

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第18张图片

时钟响应的位已经使能:

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第19张图片

根据GPIOA相关位配置对照,也并无问题:

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第20张图片

串口USART1相关的寄存器配置也并无问题:

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第21张图片

 观察接收变量:地址 名称 值

当打开串口调试助手向单片机发送4256可以观察到接收变量其值的变化

四、上板验证

将程序编译并下载到开发板中

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第22张图片

  • 打开串口调试助手

5、【STM32】串口(USART)通讯 (一文搞懂寄存器操作、端口复用和中断优先级(NVIC)配置)_第23张图片

 向从串口调试助手向开发板发送4256,其也会返回4256的值。

同时在串口助手向开发板发送数据时,也出出现DS0和DS1闪烁的现象。DS0常量表示静默状态(既不发送,也不接收)

你可能感兴趣的:(#,一,stm32,单片机,arm,串口通讯)