1、微机原理与接口技术STM32实验 指 导 书V2.0龙岩学院 物理与机电工程学院电子工程系 2016.3实验一 GPIO 模块实验一、实验目的 1、学习 STM32 模块的 GPIO 模块的配置2、学习 STM32 模块的输入输出功能的实验二、实验原理 1 、 STM32 的 IO 口 相 比 51 而 言 要 复 杂 得 多 , 所 以 使 用 起 来 也 困 难 很 多 。 首 先 STM32 的 IO 口可以由软件配置成如下 8 种 模 式 : 输 入 浮 空 、 输 入 上 拉 、 输 入 下 拉 、 模 拟 输入、开漏输出、推挽输出、推挽式复用功能、开漏复用功能。STM32 的每个
2、IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的 端口配置寄存器 CRL 和 CRH; 2 个 32 位的数据寄存器 IDR 和 ODR; 1 个 32 位的置位/复位 寄存器 BSRR ;一个 16 位的复位寄存器 BRR; 1 个 32 位的锁存寄存器 LCKR。刚复位后, 复用功能未开启,I/O 端口被配置成浮空输入模式STM32 的 CRL 控 制 着 每 组 IO 端 口 ( AG ) 的 低 8 位 的 模 式 。 每 个 IO 端 口 的 位 占 用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几个常用的配置, 比如
3、0X0 表 示 模 拟输入 模 式 ( ADC 用 ) 、 0X3 表示 推 挽输 出 模式 ( 做输 出口 用 , 50M 速 率) 、 0X8 表 示 上 /下 拉 输 入 模 式 ( 做 输 入 口 用 ) 、 0XB 表 示 复 用 输 出 ( 使 用 IO 口 的 第 二 功 能 ,50M 速 率) 。 CRH 的 作用 和 CRL 完全一 样在固件库开发中, 操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化 函数完成:void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitSt
4、ruct); 第一个参数是用来指定 GPIO,取值范围为 GPIOAGPIOG。第二个 参数 为初 始化 参数 结构体 指针, 结构 体类 型为 GPIO_InitTypeDef。 查看结 构体的 定义 : typedef structuint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode;GPIO_InitTypeDef;通过初始化结构体初始化 GPIO 的常用格式是:GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin =
5、 GPIO_Pin_5; /LED0PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/速度 50MHzGPIO_Init(GPIOB, GPIO_Mode 是用来设置对应 IO 端 口 的 输 出 输 入 模 式 , 这 些 模 式 在 MDK 中是通过一个枚举 类型定义的:typedef enumGPIO_Mode_AIN = 0x0, /模拟输入GPIO_Mode_IN_FLOATING = 0x04, /浮空输入G
6、PIO_Mode_IPD = 0x28, /下拉输入GPIO_Mode_IPU = 0x48, /上拉输入GPIO_Mode_Out_OD = 0x14, /开漏输出GPIO_Mode_Out_PP = 0x10, /通用推挽输出GPIO_Mode_AF_OD = 0x1C, /复用开漏输出GPIO_Mode_AF_PP = 0x18 /复用推挽GPIOMode_TypeDef;IDR 是一个端口输入数据寄存器,要想知道某个 IO 口的电平状态,只要读这个寄存器,再 看某个位的状态就可以了。使用起来是比较简单的。在固件库中操作 IDR 寄存器读取 IO 端口数据是通过 GPIO_ReadInp
7、utDataBit 函数实现的:uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)比如我要读 GPIOA.5 的电平状态,那么方法是:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5); 返回值是 1(Bit_SET)或者 0(Bit_RESET); ODR 是一个端口输出数据寄存器。该寄存器为可读写,从该寄存器读出来的数据可以用于 判断当前 IO 口 的 输 出 状 态 。 而 向 该 寄 存 器 写 数 据 , 则 可 以 控 制 某 个 IO 口 的 输 出 电 平 。
8、在 固 件库中设置 ODR 寄存器的值来控制 IO 口的输出状态是通过函数 GPIO_Write 来实现的:void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);BSRR 寄存器是端口位设置/清除寄存器。在 STM32 固件库中,通过 BSRR 和 BRR 寄存 器设置 GPIO 端口输出是通过函数 GPIO_SetBits()和函数 GPIO_ResetBits() 来完成的。 void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void GPIO_ResetBits(GP
9、IO_TypeDef* GPIOx, uint16_t GPIO_Pin) 2、IO 操作步骤很简单1) 使能 IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd()。2) 初始化 IO 参数。调用函数 GPIO_Init();3) 操作 IO。STM32 的 GPIO 库函数如下表 1 所示表 1 GPIO 库函数列表三、实验设备与器件 1、计算机2、STM32 开发板3、仿真器四、实验内容 1 通过按键来控制 LED 和蜂鸣器,当按键 KEY0 按下 LED1 亮,KEY2 控制 LED0,KEY3控制 BEEP,电路的硬件连接图 1 所示。图 1 电路连接图1) 设计
10、 LED 的初始化程序新建 led.c 和 led.h 文件,并编写初始化程序/初始化PB5和PE5为输出口.并使能这两个口的时钟 /LED IO初始化 void LED_Init(void) GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); /使能PB,PE端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; /LED0PB.5 端口配置 GPIO_InitStructure.GP
11、IO_Mode = GPIO_Mode_Out_PP; /推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /IO口速度为50MHz GPIO_Init(GPIOB, /根据设定参数初始化 GPIOB.5 GPIO_SetBits(GPIOB,GPIO_Pin_5); /PB.5 输出高 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; /LED1PE.5 端口配置, 推挽输出 GPIO_Init(GPIOE, GPIO_SetBits(GPIOE,GPIO_Pin_5); Led.h/推挽输出 ,
12、IO口速度为50MHz /PE.5 输出高 #ifndef LED_H #define LED_H #include “sys.h“ #define LED0 PBout(5)/ PB5 #define LED1 PEout(5)/ PE5 void LED_Init(void);/初始化 #endif 2) 设计 BEEP 驱动程序新建 beep.c 文件并输入下面代码#include “beep.h“ /初始化PB8为输出口.并使能这个口的时钟 /蜂鸣器初始化 void BEEP_Init(void) GPIO_InitTypeDef GPIO_InitStructure; RCC_APB
13、2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /使能GPIOB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; /BEEPPB.8 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /速度为50MHz GPIO_Init(GPIOB, /根据参数初始化GPIOB.8 GPIO_ResetBits(GPIOB,GPIO_Pin_8);/输
14、出0,关闭蜂鸣器输出 新建 beep.h 文件#ifndef BEEP_H #define BEEP_H #include “sys.h“ /蜂鸣器端口定义 #define BEEP PBout(8) / BEEP,蜂鸣器接口 void BEEP_Init(void); /初始化 #endif 3)编写 key.c 文件 #include “stm32f10x.h“ #include “key.h“ #include “sys.h“ #include “delay.h“ /按键初始化函数 void KEY_Init(void) /IO初始化 GPIO_InitTypeDef GPIO_Init
15、Structure; /初始化KEY0GPIOA.13,KEY1GPIOA.15 上拉输入 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);/使能 PORTA,PORTE时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;/PE24 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /设置成上拉输入 GPIO_Init(GPIOE, /初始化GPIOE2,3,4 /初始化 WK
16、_UPGPIOA.0 下拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; /PA0设置成输入,默认下拉 GPIO_Init(GPIOA, /初始化GPIOA.0 /按键处理函数 /返回按键值 /mode:0,不支持连续按;1,支持连续按; /0,没有任何按键按下 /1,KEY0按下 /2,KEY1按下 /3,KEY2按下 /4,KEY3按下 WK_UP /注意此函数有响应优先级,KEY0KEY1KEY2KEY3! u8 KEY_Scan(u8 mode) sta
17、tic u8 key_up=1;/按键按松开标志 if(mode)key_up=1; /支持连按 if(key_up/去抖动 key_up=0; if(KEY0=0)return KEY_RIGHT; else if(KEY1=0)return KEY_DOWN; else if(KEY2=0)return KEY_LEFT; else if(KEY3=1)return KEY_UP; else if(KEY0=1 return 0;/ 无按键按下 新建 key.h 文件,并编写如下的代码 #ifndef KEY_H #define KEY_H #include “sys.h“ /#defin
18、e KEY0 PEin(4) /PE4 /#define KEY1 PEin(3) /PE3 /#define KEY2 PEin(2) /PE2 /#define KEY3 PAin(0) /PA0 WK_UP #define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)/读取按键0 #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)/读取按键1 #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)/读取按键2 #define KEY3 GPI
19、O_ReadInputDataBit(GPIOA,GPIO_Pin_0)/读取按键3(WK_UP) #define KEY_UP 4 #define KEY_LEFT 3 #define KEY_DOWN 2 #define KEY_RIGHT 1 void KEY_Init(void);/IO初始化 u8 KEY_Scan(u8); /按键扫描函数 #endif 3) 编写主函数 main.c#include “led.h“ #include “delay.h“ #include “key.h“ #include “sys.h“ #include “beep.h“ int main(void
20、) u8 t; delay_init(); /延时函数初始化 LED_Init(); /LED端口初始化 KEY_Init(); /初始化与按键连接的硬件接口 BEEP_Init(); /初始化蜂鸣器端口 LED0=0; /先点亮红灯 while(1) t=KEY_Scan(0); /得到键值 if(t) switch(t) case KEY_UP: /控制蜂鸣器 BEEP=!BEEP; break; case KEY_LEFT: /控制LED0翻转 LED0=!LED0; break; case KEY_DOWN: /控制LED1翻转 LED1=!LED1; break; case KEY_
21、RIGHT: /同时控制LED0,LED1翻转 LED0=!LED0; LED1=!LED1; break; else delay_ms(10); 五、实验总结 1、总结库函数开发方法和寄存器开发方式的区别2、理解 STM32 开发的软件的组织方式六、预习要求 1、预习 GPIO 模块的功能2、预习 GPIO 函数库的配置方法实验二 串口通信实验一、实验目的 1、学习 STM32 串口模块的原理、配置2、学习 STM32 串口的发送和接受的功能实现二、实验原理 STM32 最多可提供 5 路串口,有分数波特率发生器、支持单线光通信和半双工单线通 讯、支 持 LIN、智能卡 协 议和 IrDA
22、SIR ENDEC 规 范(仅 串 口 3 支 持) 、具 有 DMA 等 。串口设置的一般步骤可以总结为如下几个步骤: 1) 串口时钟使能,GPIO时钟使能 2) 串口复位 3) GPIO端口模式设置 4) 串口参数初始化 5) 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤) 6) 使能串口 7) 编写中断处理函数 下 面 , 我 们 就 简 单 介 绍 下 这 几 个 与 串 口 基 本 配 置 直 接 相 关 的 几 个 固 件 库 函 数 。 这 些 函 数 和 定 义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。 1.串
23、口时钟使能。串口是挂载在APB2下面的外设,所以使能函数为: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1); 2.串口复位。当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配 置这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复 位该外设的操作。复位的是在函数USART_DeInit() 中完成: void USART_DeInit(USART_TypeDef* USARTx);/串口复位 比如我们要复位串口1,方法为:USART_DeInit(USART1) ; / 复位串口1 3.串口参数初始化。串口初
24、始化是通过USART_Init() 函数实现的, void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); 第一个入口参数是指定初始化的串口标号,这里选择USART1。 第二个入口参数是一个USART_InitTypeDef类型的结构体指针,这个结构体指针的成员变 量用来设置串口的一些参数。一般的实现格式为: 先 申 明 一 个 USART_InitTypeDef USART_InitStruct变 量 ; 后 面 在 对 变 量 进 行 初 始 化 。 初 始 化的过程如下程序所示: USART_
25、InitStructure.USART_BaudRate = bound;/一般设置为 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;/字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;/一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;/无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl= USART_Hardwar
26、eFlowControl_None;/无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;/收发模式 USART_Init(USART1, /初始化串口 从上面的初始化格式可以看出初始化需要设置的参数为:波特率,字长,停止位,奇偶校 验位,硬件数据流控制,模式(收,发)。我们可以根据需要设置这些参数。 4.数据发送与接收。STM32的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双 寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到收据 的时候,也是存在该寄存
27、器内。 STM32库函数操作USART_DR寄存器发送数据的函数是: void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 通过该函数向串口寄存器USART_DR写入一个数据。 STM32库函数操作USART_DR寄存器读取串口接收到的数据的函数是: uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 通过该函数可以读取串口接受到的数据。 5.串口状态。串口的状态可以通过状态寄存器USART_SR读取。USART_SR的各位描述如图2.1 所示: 图2.1 状态寄存器位定义 这里我
28、们关注一下两个位,第5、6位RXNE和TC。 RXNE(读数据寄存器非空),当该位被置1的时候,就是提示已经有数据被接收到了,并且 可以读出来了。这时候我们要做的就是尽快去读取USART_DR,通过读USART_DR可以将该位 清零,也可以向该位写0,直接清除。 TC(发送完成),当该位被置位的时候,表示USART_DR内的数据已经被发送完成了。如果 设置了这个位的中断,则会产生中断。该位也有两种清零方式:1)读USART_SR,写 USART_DR。2)直接向该位写0。 状态寄存器的其他位我们这里就不做过多讲解,大家需要可以查看中文参考手册。 在我们固件库函数里面,读取串口状态的函数是: F
29、lagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); 这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲 解的RXNE(读数据寄存器非空)以及TC(发送完成)。例如我们要判断读寄存器是否非空 (RXNE),操作库函数的方法是: USART_GetFlagStatus(USART1,USART_FLAG_RXNE); 我们要判断发送是否完成(TC),操作库函数的方法是: USART_GetFlagStatus(USART1,USART_FLAG_TC); 这些标识号在MDK
30、里面是通过宏定义定义的: #define USART_IT_PE (uint16_t)0x0028)#define USART_IT_TXE (uint16_t)0x0727)#define USART_IT_TC (uint16_t)0x0626)#define USART_IT_RXNE (uint16_t)0x0525)#define USART_IT_IDLE (uint16_t)0x0424)#define USART_IT_LBD (uint16_t)0x0846)#define USART_IT_CTS (uint16_t)0x096A)#define USART_IT_ERR
31、(uint16_t)0x0060)#define USART_IT_ORE (uint16_t)0x0360)#define USART_IT_NE (uint16_t)0x0260)#define USART_IT_FE (uint16_t)0x0160)6.串口使能。串口使能是通过函数USART_Cmd()来实现的,这个很容易理解,使用方法 是: USART_Cmd(USART1, ENABLE); /使能串口 7.开启串口响应中断。有些时候当我们还需要开启串口中断,那么我们还需要使能串口中 断,使能串口中断的函数是: void USART_ITConfig(USART_TypeDef*U
32、SARTx,uint16_t USART_IT, FunctionalState NewState) 这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中 断类型有很多种。比如在接收到数据的时候(RXNE读数据寄存器非空),我们要产生中 断,那么我们开启中断的方法是: USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);/开启中断,接收到数据中断 我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是: USART_ITConfig(USART1,USART_IT_TC,ENABLE); 8.获取相应中断状态。当我们使
33、能了某个中断的时候,当该中断发生了,就会 设置状态寄存器中的某个标志位。经常我们在中断处理函数中,要判断该中断 是哪种中断,使用的函数是: ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) 比如我们使能了串口发送完成中断,那么当中断发生了,我们便可以在中断处 理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是: USART_GetITStatus(USART1, USART_IT_TC) 返回值是SET,说明是串口发送完成中断发生。 通过以上的讲解,我们就可以达到串口最基本的配置了,关于串口更详细
34、的介 绍,请参考STM32参考手册第516页至548页,通用同步异步收发器一章。 三、实验设备与器件 1、计算机2、STM32 开发板3、仿真器四、实验内容 1、 编 写程 序 通 过 STM32 串口实 现如 下功 能 : PC 向单片 机 发 送 “Hello World! ”, 通过 调 试助手在电脑上显示出来,单片机向 PC 发送接收到的内容,同时蜂鸣器响一次。硬件电 路连接图如下图 2.2 所示。图 2.2 串口硬件连接图五、实验总结 1、串口实现发送的原理2、串口实现接受的原理六、预习要求 1、预习串口模块的功能2、预习串口函数库的配置方法实验三 外部中断实验一、实验目的 1、学习
35、 STM32 外部中断模块的原理、配置2、学习 STM32 外部中断程序实现和中断服务程序编程二、实验原理 STM32的每个IO都可以作为外部中断的中断输入口, STM32F103的中断控制器支持 19个外部中断/事件请求。每个中断设有状态位,每个中断/ 事件都有独立的触发和屏蔽设 置。STM32F103的19个外部中断如表3-1所示: 表3-1 外部中断对应表 中断线号 功能 线015 对应外部IO 口的输入中断 线16 连接到PVD 输出 线17 连接到RTC 闹钟事件 线18 连接到USB 唤醒事件 STM32外部IO口的中断功能的代码主要分布在固件库的stm32f10x_exti.h和
36、 stm32f10x_exti.c文件中。 在库函数中,配置GPIO与中断线的映射关系的函数GPIO_EXTILineConfig()来实现 的: void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)该函数将GPIO端口与中断线映射起来,使用范例是: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2); 将中断线2与GPIOE映射起来,那么很显然是GPIOE.2与EXTI2 中断线连接了。设置好中断 线映射之后,那么到底来自这个IO口的中
37、断是通过什么方式触发的呢?接下来我们就要设 置该中断线上中断的初始化参数了。 产生一个软件中断的函数是 void EXTI_GenerateSWInterrupt(u32 EXTI_Line) 中断线上中断的初始化是通过函数EXTI_Init()实现的。EXTI_Init() 函数的定义是: void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);下面我们用一个使用范例来说明这个函数的使用: 上面的例子设置中断线 4 上的中断为下降沿触发。STM32 的外设的初始化都是通过结构 体 来 设 置 初 始 值 的 , 这 里 就 不 罗 嗦 结 构 体
38、初 始 化 的 过 程 了 。 我 们 来 看 看 结 构 体EXTI_InitTypeDef 的成员变量:EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line=EXTI_Line4; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(/根据EXTI_I
39、nitStruct中指定的 typedef structuint32_t EXTI_Line;EXTIMode_TypeDef EXTI_Mode; EXTITrigger_TypeDef EXTI_Trigger; FunctionalState EXTI_LineCmd;EXTI_InitTypeDef;从定义可以看出,有4个参数需要设置。 第一个参数是中断线的标号,取值范围为EXTI_Line0EXTI_Line15。这个在上面已经讲过 中断线的概念。也就是说,这个函数配置的是某个中断线上的中断参数。 第二个参数是中断模式,可选值为中断EXTI_Mode_Interrupt和事件EXTI
40、_Mode_Event。 第三个参数是触发方式,可以是下降沿触发EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)EXTI_Trigger_Rising_Falling。 最后一个参数就是使能中断线了。 我们设置好中断线和GPIO映射关系,然后又设置好了中断的触发模式等初始化参数。既然 是外部中断,涉及到中断我们当然还要设置NVIC中断优先级。这个在前面已经讲解过,这 里我们就接着上面的范例,设置中断线2的中断优先级。 上面这段代码相信大家都不陌生,我们在前面的串口实验的时候讲解过,这里不再讲解。 我们配置完中断优先级之
41、后,接着我们要做的就是编写中断服务函数。中断服务函数的名 字是在MDK中事先有定义的。这里需要说明一下,STM32的IO 口外部中断函数只有6个, 分别为: 中断线0-4每个中断线对应一个中断函数, 中断线5-9共用中断函数EXTI9_5_IRQHandler, 中断线10-15共用中断函数EXTI15_10_IRQHandler。 在编写中断服务函数的时候会经常使用到两个函数,第一个函数是判断某个中断线上 的中断是否发生(标志位是否置位): ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); 这个函数一般使用在中断服务函数的开头判断中断是否发生。另一
42、个函数是清除某个中断 线上的中断标志位: void EXTI_ClearITPendingBit(uint32_t EXTI_Line); 这个函数一般应用在中断服务函数结束之前,清除中断标志位。 常用的中断服务函数格式为: NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;/使能按键外部中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;/抢占优先级2, NVIC_InitStructure.NVI
43、C_IRQChannelSubPriority = 0x02;/子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/使能外部中断通道 NVIC_Init(/中断优先级分组初始化 EXPORT EXTI0_IRQHandler EXPORT EXTI1_IRQHandler EXPORT EXTI2_IRQHandler EXPORT EXTI3_IRQHandler EXPORT EXTI4_IRQHandler EXPORT EXTI9_5_IRQHandlerEXPORT EXTI15_10_IRQHandlervoid EXTI2_
44、IRQHandler(void)if(EXTI_GetITStatus(EXTI_Line3)!=RESET)/判断某个线上的中断是否发生 中断逻辑 EXTI_ClearITPendingBit(EXTI_Line3); /清除LINE上的中断标志位 在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态 标志位的函数EXTI_GetFlagStatus和EXTI_ClearFlag,他们的作用和前面两个函数的作用类 似。只是在EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标 志位,而EXTI_GetFlagStatus直接用来判断
45、状态标志位。 下面我们再总结一下使用IO口外部中断的一般步骤: 1)初始化IO 口为输入。 2)开启IO 口复用时钟,设置IO 口与中断线的映射关系。 3)初始化线上中断,设置触发条件等。 4)配置中断分组(NVIC),并使能中断。 5)编写中断服务函数。 通过以上几个步骤的设置,我们就可以正常使用外部中断了。三、实验设备与器件 1、计算机2、STM32 开发板3、仿真器四、实验内容 1、 利 用 函 数 void EXTI_GenerateSWInterrupt(u32 EXTI_Line), 实 现 软 件 方 式 触 发 中 断 , 要 求 每隔 0.5S 触发一次软件中断,触发的中断线
46、是中断线 1,在中断线 1 的中断服务程序中实 现对 LED0 的翻转控制。2、 利 用 KEY1 按 键硬 件触 发一次 中断 , 触发 中断 线 3, 在 对应 的中 断服 务程 序中 实现 对 LED1翻转控制。五、实验总结 1、外部中断的配置流程。2、软件触发中断和硬件触发中断的差别。六、预习要求 1、预习外部中断的配置流程。2、预习外部中断的相关函数。实验四 AD 实验一、实验目的 1、学习 STM32 AD 转换器原理、配置2、学习 STM32 AD 程序实现二、实验原理 STM32 拥 有 13 个 ADC(ST M32F101/102 系 列只 有 1 个 ADC) ,这 些
47、ADC 可以独 立 使用, 也可 以使 用双 重模 式 ( 提 高采 样率) 。 STM32 的 ADC 是 12 位逐 次逼 近 型的模 拟数 字 转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单 次 、 连 续 、 扫 描 或 间 断 模 式 执 行 。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。STM32 将 ADC 的转换分为 2 个 通 道 组 : 规 则 通 道 组 和 注 入 通 道 组 。 规 则 通 道 相 当 于 正 常 运 行 的
48、程 序 , 而 注 入 通 道 呢 , 就 相 当 于 中 断 。 在 程 序 正 常 执 行 的 时 候 , 中 断 是 可 以 打 断 你 的 执 行 的 。 同 这 个 类 似 , 注 入 通 道 的 转 换 可 以 打 断 规 则 通 道 的 转 换 , 在 注 入 通 道 被 转 换 完 成 之后,规则通道才得以继续转换。STM32 其 ADC 的规则通道组最多包含 16 个转换,而注入通道组最多包含 4 个通道。 STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样 周期 为 1.5 个 ADC 时钟 下 得到) , 不要 让
49、 ADC 的时 钟超 过 14M, 否则 将导 致 结果准 确度 下 降。使用到的库函数分布在stm32f10x_adc.c 文件和stm32f10x_adc.h文件中。下面讲解其详 细设置步骤: 1)开启PA口时钟和ADC1时钟,设置PA1 为模拟输入。 STM32F103ZET6 的 ADC 通道 1 在 PA1 上,所以,我们先要使能 PORTA 的时钟和 ADC1 时钟,然后设置 PA1 为 模 拟 输 入 。 使 能 GPIOA 和 ADC 时钟用 RCC_APB2PeriphClockCmd 函数,设置 PA1 的输入方式,使用 GPIO_Init 函数即可。这里我们列出 STM32 的 ADC 通 道与 GPIO 对应表:2)复位ADC1,同时设置ADC1分频因子。 开启ADC1时钟之后,我们