资讯详情

STM32入门笔记(02):AFIO复用端口与重映射 、NVIC中断优先级管理、外部中断、串口通信及串口实验(SPL...

目录

  • 端口复用功能
    • 端口复用是什么?
    • 哪些端口引脚可以重复使用?
    • 初始化复用端口有三个步骤:
      • 使用复用功能时,至少要使用能量 2 个时钟:
  • 端口重映射
    • 端口重映射是什么?
    • 四个详细步骤如下:
  • NVIC 优先管理中断
    • 什么是NVIC ?
    • 优先分组中断
    • 抢占优先级 & 响应优先级差异:
    • 优先设置中断
      • 优先级分组函数中断 NVIC_PriorityGroupConfig
      • 初始化函数中断 NVIC_Init
    • 总结
  • 外部中断 F407
    • 外部中断实验
      • 1)初始化 IO 口为输入
      • 2)开启 AFIO 时钟
      • 3)设置中断线和 GPIO 映射关系
      • 4)设置中断 触发模式 等待初始参数
      • 5)设置 NVIC 中断优先级
      • 6)编写中断服务函数
  • 串口通信
    • 串行通信接口背景知识
      • 串行通信和并行通信
      • 全双工、半双工、单工通信
      • 同步通信和异步通信
      • 通信速率(比特率)
      • 通信过程
      • 需要定义的参数
    • STM32F1串口框架图
    • STM常用的32串寄存器和库函数
      • 波特率计算
      • 串口操作函数
    • 串口配置方法及通信实例
    • 串口实验
      • 1) 串口时钟使能,GPIO 时钟使能
      • 2) 串口复位
      • 3) GPIO 设置端口模式
      • 4) 串口参数初始化
      • 5) 打开中断和初始化 NVIC(如果需要开启中断才需要这个步骤)
      • 6) 使能串口
      • 7) 编写中断处理函数
      • 8) 收发串口数据
      • 9) 获取串口传输状态
      • 总结
  • 参考资料


先导知识

  • 1.STM入门笔记(02):Keil5 安装–SPL标准外设驱动库环境建设及新建工程模板教程(SPL库函数版)

  • 2.STM入门笔记(02): GPIO工作原理 – GPIO通用和AFIO复用功能 I/O(SPL库函数版)


端口复用功能

端口复用是什么?

STM32 内置外设很多,这些外设的外引脚都是和 GPIO 复用的。也就是说,一个 GPIO如果可以重用作内置外设的功能引脚,那么当这个 GPIO 用作内置外设时,称为复用。

哪些端口引脚可以重复使用?

这部分知识在《STM32 中文参考手册 V10》的 P109,P116~P121 详细说明什么 GPIO 哪些内置外设可以重用管脚。

大家都知道,MCU 都有串口,STM32 有几个串口。 STM32F103ZET6 有 5 串口,我们可以查手册知道,串口 1 引脚对应 IO 为 PA9,PA10.PA9,PA10 默认功能是 GPIO,所以当 以引脚为串口 1 的 使用引脚时,即端口复用。

在这里插入图片描述

初始化复用端口有三个步骤:

  • GPIO 。当然,使用端口时钟是必要的。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 
  • 复用的。例如,您必须使用端口 PA9,PA10 复用为串口,所以要使串口时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 
  • 端口 。 在 IO 复用位内置外设功能引脚时,必须设置 GPIO 至于端口模式,在复用功能下 GPIO 如何对应模式?您可以查看手册《STM32 中文参考手册 V10》P110 的表格“8.1.11 外设的 GPIO 配置。我们在这里拿 Usart1 举例: 从表中可以看出,我们需要配置,那么 TX 管脚需要配置为输出,RX 管脚配置为
//USART1_TX PA.9 复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); 

在使用复用功能的是时候,最少要使能 2 个时钟:

  • 复用的

例如:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

端口重映射

什么是端口重映射?

为了使不同器件封装的外设 IO 功能数量达到最优,可以

STM32 中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。我们知道每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。

简单的讲就是映射到但不是可以随便映射的,具体对应关系《STM32 中文参考手册 V10》的 P116 页“8.3 复用功能和调试配置”有讲解。这里我们同样拿串口 1 为例来讲解。

从表中可以看出,默认情况下,串口 1 复用的时候的引脚位 PA9,PA10,同时我们可以将 TX 和 RX 重新映射到管脚 PB6PB7 上面去。

所以重映射我们同样要使能复用功能的时候讲解的 外,还要使能 ,然后要

详细4个步骤为:

  • GPIOB
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  • 串口 1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  • AFIO (复用IO)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  • 开启 函数
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);

这样就将串口的 TX 和 RX 重映射到管脚 PB6 和 PB7 上面了。

至于有哪些功能可以重映射,大家除了查看中文参考手册之外,还可以从 函数入手查看第一个入口参数的取值范围可以得知。在 stm32f10x_gpio.h 文件中定义了取值范围为下面宏定义的标识符,这里我们贴一小部分:

#define GPIO_Remap_SPI1 ((uint32_t)0x00000001)
#define GPIO_Remap_I2C1 ((uint32_t)0x00000002)
#define GPIO_Remap_USART1 ((uint32_t)0x00000004)
#define GPIO_Remap_USART2 ((uint32_t)0x00000008)
#define GPIO_PartialRemap_USART3 ((uint32_t)0x00140010)
#define GPIO_FullRemap_USART3 ((uint32_t)0x00140030) 

从上面可以看出,,而对于 USART3,存在

所谓部分重映射就是部分管脚和默认的是一样的,而部分管脚是重新映射到其他管脚。而完全重映射就是所有管脚都重新映射到其他管脚。

看看手册中的 USART3 重映射表:

部分重映射就是 重映射到 上。

而 PB13 和 PB14 和没有重映射情况是一样的,都是 USART3_CTS 和 USART3_RTS 对应管脚。完全重映射就是将这两个脚重新映射到 PD11 和 PD12 上去。

我们要使用 USART3 的部分重映射,我们调用函数方法为:

GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);

NVIC 中断优先级管理

什么是NVIC ?

“ NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。”

支持 256 个中断,其中包含了 ,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。

个中断,包括 ,具有 16 级可编程的中断优先级。(在互联型产品和其它STM32F10xxx产品的向量表: 107 系列才有 68 个)。

而我们常用的就是这 68 个可屏蔽中断(外部中断),但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60外部中断 个。

因为我们开发板选择的芯片是 STM32F103 系列的所以我们就只针对 STM32F103 系列这 60 个可屏蔽中断进行介绍。

在 MDK 内,与 NVIC 相关的寄存器,MDK 为其定义了如下的结构体:

typedef struct
{ 
        
 __IO uint32_t ISER[8]; /*!< Interrupt Set Enable Register */
 uint32_t RESERVED0[24];
 __IO uint32_t ICER[8]; /*!< Interrupt Clear Enable Register */
 uint32_t RSERVED1[24];
 __IO uint32_t ISPR[8]; /*!< Interrupt Set Pending Register */
 uint32_t RESERVED2[24];
 __IO uint32_t ICPR[8]; /*!< Interrupt Clear Pending Register */
 uint32_t RESERVED3[24];
 __IO uint32_t IABR[8]; /*!< Interrupt Active bit Register */
 uint32_t RESERVED4[56];
 __IO uint8_t IP[240]; /*!< Interrupt Priority Register, 8Bit wide */
 uint32_t RESERVED5[644];
 __O uint32_t STIR; /*!< Software Trigger Interrupt Register */
} NVIC_Type;

STM32 的中断在这些寄存器的控制下有序的执行的。只有了解这些中断寄存器,才能方便的使用 STM32 的中断。下面重点介绍这几个寄存器:

  • :ISER 全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。上面说了 CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。ISER[0]的 bit0~bit31 分别对应中断 0~31。ISER[1]的 bit0~27 对应中断 32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。具体每一位对应哪个中断,请参考 stm32f10x.h 里面的第 140 行处(针对编译器 MDK5 来说)。

中断优先级分组

  • 组:5
  • 每组中断:12
  • 外部中断共: 60

这里简单介绍一下 STM32 的中断分组:STM32 将中断分为 5个组 ,组 0~4。该分组的设置是由 寄存器的 bit10~8 来定义的。

说明:

  • AIRCR[10 :8] 表示 3位来选择中断组数,例如 110 表示第1组中断。
  • bit[7: 4] 表示 4为设置分配情况,例如[0 :4] 表示[0位抢占优先级,4位响应优先级]

通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如组设置为 3,那么此时 所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是 响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的 级别高于响应优先级。而数值越小所代表的优先级就越高。

抢占优先级 & 响应优先级区别:

  • 抢占优先级是可以正在进行的 中断的。 A 是省长 B是市长

  • 的中断,不可以打断 的中断。 BB都是市级别 ,C是事情严重程度,D是不紧急事务。

  • 的中断,当两个中断发生的情况下,那个,就先执行。 BB都是市长,CC同级别事务

  • 如果级和先级都是的话,哪个中断。[A:A] [ A:A]

实例说明一下:

假定设置中断优先级为:

(RTC 中断) 的抢占优先级为: 响应优先级为 :

(外部中断 0)的抢占优先级为: 响应优先级为 :

(外部中断 1)的抢占优先级为: 响应优先级为 :

那么这 3 个中断的优先级顺序为:

注意 : 。设置好分组后不要随意改变分组,会导致中断管理混乱。

中断优先级设置

通过以上介绍,我们熟悉了 STM32 中断设置的大致过程。接下来我们介绍如何使用库函数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。NVIC 中断管理函数主要在 misc.c 文件里面。

中断优先级分组函数 NVIC_PriorityGroupConfig

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
  • 参数:NVIC_PriorityGroup - NVIC_PriorityGroup_0 - NVIC_PriorityGroup_1 - NVIC_PriorityGroup_2 - NVIC_PriorityGroup_3 - NVIC_PriorityGroup_4

这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,

比如我们设置整个系统的中断优先级分组值为 2,那么方法是:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

这样就确定了一共为 “ 2 位抢占优先级,2 位响应优先级”。

确认分组后如何设置单个中断的抢占优先级和响应优先级???

中断初始化函数 NVIC_Init

NVIC_Init 其函数申明为:

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)

其中 NVIC_InitTypeDef 是一个结构体,我们可以看看结构体的成员变量:

typedef struct
{ 
        
 uint8_t NVIC_IRQChannel;
 uint8_t NVIC_IRQChannelPreemptionPriority;
 uint8_t NVIC_IRQChannelSubPriority;
 FunctionalState NVIC_IRQChannelCmd; 
 } NVIC_InitTypeDef;

NVIC_InitTypeDef 结构体中间有,这三个成员变量的作用是:

  • NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 。如下所示:
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt */
  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt */
  TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt */
  RTC_IRQn                    = 3,      /*!< RTC global Interrupt */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt */
  DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt */
  DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt */
  DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt */
  DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt */
  DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt */
  DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt */
  DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt */
  • NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。
  • NVIC_IRQChannelSubPriority:定义这个中断的子优先级别。
  • NVIC_IRQChannelCmd:该中断是否使能。

例如:

	NVIC_InitTypeDef NVIC_InitStructure;
   //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

总结

总结一下中断优先级设置的步骤:

  • 1.系统运行开始的时候设置中断分组。,也就是确定抢占优先级和子优先级的分配位数。调用函数为 NVIC_PriorityGroupConfig();

  • 2.所用到的中断的中断。对每个中断调用函数为 NVIC_Init();


外部中断 F407

STM32 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32 的强大之处。

STM32F407 的中断控制器支持 /事件请求。

STM32F407 的 个外部中断为:

  • EXTI线0~15:对应外部IO口的输入中断 (16根)。
  • EXTI线16:连接到PVD输出。 EXTI线17:连接到RTC闹钟事件。
  • EXTI线18:连接到USB OTG FS唤醒事件。
  • EXTI线19:连接到以太网唤醒事件。
  • EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
  • EXTI线21:连接到RTC入侵和时间戳事件。
  • EXTI线22:连接到RTC唤醒事件。

每个中断设有状态位,每个中断/事件都有独立的触发(上升沿,下降沿或者双边沿触发)和屏蔽设置。

从上面可以看出,STM32 供 IO 口使用的中断线只有 16 个,但是 STM32 的 IO 口却远远不止 16 个。

GPIO 的管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口。

例如:

:对应 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0

:GPIOA.8、GPIOB.8、GPIOC.8、GPIOD.8、GPIOE.8、GPIOF.8、GPIOG.8

注意: 中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO 跟中断线的映射关系图:

外部中断实验

实验内容 :通过板载按键PD8控制LEDPE10和蜂鸣器PB10的亮灭鸣叫 。

硬件设计:

按键:默认是上拉输入 – 读取到1 – 按键没有按下

  GPIO_InitTypeDef  GPIO_InitStructure
  
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟 
  //GPIOD8 IO口初始化
  GPIO_InitStructure.GPIO_Pin = KEY1_PIN; //KEY对应引脚 GPIOD8
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化GPIOD8 

LED:上拉 – 输出1模式 --灭

  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOE时钟
  //GPIOE10初始化设置 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOB时钟
  GPIO_InitStructure.GPIO_Pin =  LED_PIN;//LED对应IO口
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIO
  GPIO_SetBits(GPIOE,GPIO_Pin_10); //输出1 --灯灭

NPN S8050 三极管

蜂鸣器 :输出模式 – 输出1–鸣叫

  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
  //初始化蜂鸣器对应引脚GPIOF8
  GPIO_InitStructure.GPIO_Pin =  Buzzer_PIN;//蜂鸣器对应IO口 GPIOB10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

以下内容为软件设计。

外部中断你的一般配置步骤为:

1)初始化 IO 口为输入

初始化LED、蜂鸣器和按键的IO口配置。

2)开启 AFIO 时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟

这个函数非常重要,在使用外部中断的时候一定要先使能SYSCFG时钟 。

例如:

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟

3)设置中断线和 GPIO 映射关系

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex); //配置 GPIO 与中断线的映射关系

  • 入口参数1:将要映射GPIOx的组数 ABCDEFG
  • 入口参数2:将要映射EXTI_PinSourcex外部中断线编号数

例如:

  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource8);  //PD8 连接到中断线8(EXTI_PinSource8) 
  //16根IO中断线0~15 == GPIOx.0~15 

4)设置中断的 触发模式 等初始化参数

	/* 配置中断线初始化,触发条件 EXTI_Line8 */
  EXTI_InitStructure.EXTI_Line = EXTI_Line8;  // 22根中断线 EXTI_Line0~22
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
  EXTI_Init(&EXTI_InitStructure);//配置
  • 入口参数1:EXTI_Line 为中断线的标号
  • 入口参数2 :中 断 模 式

中断:EXTI_Mode_Interrupt 事件:EXTI_Mode_Event

  • 入口参数3:触发方式

下降沿触发:EXTI_Trigger_Falling 上升沿触发: EXTI_Trigger_Rising 任意电平(上升沿和下降沿)触发 :EXTI_Trigger_Rising_Falling

5)设置 NVIC 中断优先级

参数NVIC_IRQChannel 只能选择7个外部中断通道中对应的一个,这里是EXTI9_5_IRQn。 可以在 stm32f4xx.h中查询 。

	//配置中断分组NVIC
 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //7个外部中断通道 stm32f4xx.h中查询 
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);//配置
  • 入口参数 1: NVIC_IRQChannel 定义初始化哪个中断

可以在 stm32f4xx.h 中有个枚举型定义的中断名称,可以搜索 “EXTI9_5_IRQn” 定位到位置。

6)编写中断服务函数

IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。

注意:外部中断线 5~9 分配一个中断向量,共用一个服务函数; 外部中断线 10~15 分配一个中断向量,共用一个中断服务函数。

7个外部中断服务函数:

//7个外部中断服务函数 stm32f4xx.h中
void EXTI9_5_IRQHandler(void)
{ 
        
	delay_ms(10);	//消抖
	if(KEY1==0)	 
	{ 
        				 
		LED=!LED;	
		BEEP=~BEEP;
	}		 
	 EXTI_ClearITPendingBit(EXTI_Line8);//清除LINE8上的中断标志位 
}

exti.c

#include "exti.h"
#include "delay.h" 
#include "led.h" 
#include "key.h"
#include "beep.h"

//7个外部中断服务函数 stm32f4xx.h中
void EXTI9_5_IRQHandler(void)
{ 
        
	delay_ms(10);	//消抖
	if(KEY1==0)	  // 按键
	{ 
        				 
		LED=!LED;//LED
		BEEP=~BEEP;//蜂鸣器
	}		 
	 EXTI_ClearITPendingBit(EXTI_Line8);//清除LINE4上的中断标志位 
}
	   
//外部中断初始化程序
//初始化为中断输入PD8
void EXTIX_Init(void)
{ 
        
	NVIC_InitTypeDef   NVIC_InitStructure;
	EXTI_InitTypeDef   EXTI_InitStructure;
	
	KEY_Init(); //按键对应的IO口初始化
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
	
  //中断线映射IO口
  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource8);  //PD8 连接到中断线8(EXTI_PinSource8) //16根IO中断线0~15 == GPIOx.0~15 

	/* 配置中断线初始化,触发条件 EXTI_Line8 */
  EXTI_InitStructure.EXTI_Line = EXTI_Line8;  // 22根中断线 EXTI_Line0~22
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
  EXTI_Init(&EXTI_InitStructure);//配置
  
 //配置中断分组NVIC
 // NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0
  NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;   //7个外部中断通道 stm32f4xx.h中查询 
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);//配置
   
}

F407实验0318: 外部中断实验源码


串口通信

STM32F103ZET6 最多可提供 5 路串口:3个USART通用同步异步收发器 和 2个UART通用异步收发器。

串行通信接口背景知识

串行通信和并行通信

处理器 与 外部设备通信的两种方式:

  • 并行通信

按数据传送的方式,通信可分为

串行通信是指设备之间通过少量数据信号线(一般是8根以下)、地线以及控制信号线,按数据位形式一位一位地传输数据的通信方式。而并行通信一般是指使用8、16、32及64根或更多的数据线同时进行传输的通信方式。

并行通信就像多个车道的公路,可以同时传输多个数据位的数据,而串行通信则像单车道的公路,同一时刻只能传输一个数据位的数据。

二者特性对比:

全双工、半双工及单工通信

根据数据通信的方向,通信又分为全双工、半双工及单工通信,它们主要以信道的方向来区分。

仍以公路来类比:

  • 就是一个双向车道,两个方向上的车流互不相干;
  • 则像乡间小道那样,同一时刻只能让一辆小车通过,另一方向的来车只能等待道路空出来时才能经过;
  • 则像单行道,另一方向的车辆完全禁止通行。

同步通信和异步通信

同步通信:带时钟同步信号传输(I2C 、SPI)

异步通信:不带时钟同步信号 (UART)

根据通信的数据同步方式,又分为两种,可以根据通信过程中是否使用到时钟信号进行简单的区分。

中,收发设备双方会使用一根,在时钟信号的驱动下双方进行协调,同步数据。通信中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。

中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据。某些通信中还需要双方约定数据的(比特率),以便更好地同步。

二者区别:

1、在同步通信中,数据信号所传输的内容绝大部分都是,而异步通信中会包含有帧的各种标识符,所以同步通信的效率更高。

2、同步通信双方的时钟允许误差较小,而异步通信双方的时钟允许误差较大。

通信速率(比特率)

衡量通信性能的一个非常重要的参数就是,通常以(Bitrate)来表示,即)。

容易与比特率混淆的概念是波特率(Baudrate),它表示

而码元是的概念,通信中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。

如常见的通信传输中,用 0V 表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特,此时波特率的大小与比特率一致;如果在通信传输中,用0V、2V、4V以及6V分别表示二进制数00、01、10、11,那么每个码元可以表示4种状态,即两个二进制比特,所以码元数是二进制比特数的一半,这个时候的波特率为比特率的一半。

因为常见的通信中一个码元都是表示两种状态,所以人们常常直接以波特率来表示比特率。虽然严格来说没什么错误,但还是能了解它们的区别。

通信过程

需要定义的参数

STM32F1串口框架图

STM32串口常用寄存器和库函数

  • 状态寄存器
  • 数据寄存器
  • 波特比率寄存器
  • 控制寄存器1 USART_CR1
  • 控制寄存器2 USART_CR2
  • 控制寄存器3 USART_CR3
  • 保护时间和预分频寄存器 USART_GTPR

波特率计算

串口的时钟 PCLK1 (USART2、3、4、5) , PCLK2(USART1)

整数部分 39

小数部分需要 “乘以 16”

串口操作函数

在stm32f10x_usart.h头文件中查看相关函数。

锐单商城 - 一站式电子元器件采购平台