- 库函数与寄存器开发区别
1.1 寄存器
寄存器开发优点
直接操作寄存器,运行效率高。
寄存器开发缺点
- 开发难度大,开发周期长
- 代码可阅读性差,可移植差
- 后期维护难度高
1.2库函数
库函数开发优点
- 开发难度较小,开发周期短
- 代码可阅读性强,可移植高
- 后期维护难度低
库函数开发缺点
相对于寄存器开发,运行效率略低
注意:库函数其实是ST公司对寄存器的进一步封装。
- 库函数开发LED
库函数开发LED要添加的库函数文件:stm32f4xx_gpio.c
- 理解led灯原理图
LED0连接在PF9
PF9输出低电平(0),灯亮;PF9输出高电平(0),灯灭;
- 打开GPIOF组时钟
在STM32芯片中,所有的外设时钟是不打开,为了降低功耗
//打开GPIOF组时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
- 设置PF9灯为输出模式 输出推挽 上拉 速度(50MHZ)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //输出推挽
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHZ
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF, &GPIO_InitStruct);
4、通过下面控制LED灯亮与灭
GPIO_SetBits() 引脚置1
GPIO_ResetBits() 引脚置0
官方参考案例
路径:D:\GZFX2103\STM32\STM32上课资料\STM32F4xx固件库\stm32f4_dsp_stdperiph_lib\STM32F4xx_DSP_StdPeriph_Lib_V1.4.0\stm32f4xx_dsp_stdperiph_lib_um.chm
- 按键库函数开发
库函数开发按键要添加的库函数文件:stm32f4xx_gpio.c
1、理解按键控制原理
KEY0连接在PA0
按键未按下,PA0为高电平
按键 按下,PA0为低电平
- 打开GPIOA组时钟
//打开GPIOA组时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
- 设置引脚为输入,上拉。
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //引脚0
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA, &GPIO_InitStruct);
4、通过下面函数获取引脚电平
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- 中断
中断概念
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行(面试题)。
STM32外部中断需要注重知识点EXTI与NVIC,STM32所有引脚均可以做为外部中断。
4.1、外部中断/事件控制器 (EXTI)
EXTI 控制器的主要特性如下:
● 每个中断/事件线上都具有独立的触发和屏蔽
● 每个中断线都具有专用的状态位
● 支持多达 23 个软件事件/中断请求
● 检测脉冲宽度低于 APB2 时钟宽度的外部信号。有关此参数的详细信息,请参见
STM32F4xx 数据手册的电气特性部分。
引脚中断线
上升沿:数字电路中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间(时刻)叫作上升沿。
下降沿:数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。
4.2 NVIC
嵌套向量中断控制器 (NVIC)
NVIC 特性
嵌套向量中断控制器 NVIC 包含以下特性:
● STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道, STM32F42xxx
和 STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中
断线)
● 16 个可编程优先级(使用了 4 位中断优先级)
● 低延迟异常和中断处理
● 电源管理控制
● 系统控制寄存器的实现
嵌套向量中断控制器 (NVIC) 和处理器内核接口紧密配合,可以实现低延迟的中断处理和晚
到中断的高效处理。
NVIC优先级分为抢占优先级与响应优先级。
数字越小,优先级别越高
优先级分组
抢占优先级 & 响应优先级区别: 抢占优先级 > 响应优先级
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
例:
假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。 中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。
中断7>中断3>中断6。
4.3外部中断配置流程
外部中断配置流程要添加的库函数:stm32f4xx_exti.c和stm32f4xx_syscfg.c
参考库函数案例
- 理解按键原理
KEY0 连接PA0,选择下降沿触发
- 设置NVIC分组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
3、使能SYSCFG及GPIOA时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
4、 初始化IO口为输入。
GPIO_Init();
5、设置IO口与中断线的映射关系。
void SYSCFG_EXTILineConfig();
6、初始化线上中断,设置触发条件等。
EXTI_Init();
7、配置中断分组(NVIC),并使能中断。
NVIC_Init();
8、 编写中断服务函数。
EXTIx_IRQHandler();
9、清除中断标志位
EXTI_ClearITPendingBit();
五、函数说明
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
函数功能:使能AHB1外设时钟
返回值:无
uint32_t RCC_AHB1Periph:哪个外设
FunctionalState NewState:ENABLE or DISABLE
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
函数功能:GPIO初始化
返回值:无
GPIO_TypeDef* GPIOx:哪个GPIO组
GPIO_InitTypeDef* GPIO_InitStruct:GPIO结构
typedef struct
{
uint32_t GPIO_Pin; //哪个引脚
GPIOMode_TypeDef GPIO_Mode; //模式
GPIOSpeed_TypeDef GPIO_Speed; //速度
GPIOOType_TypeDef GPIO_OType; //输出类型
GPIOPuPd_TypeDef GPIO_PuPd; //上下拉
}GPIO_InitTypeDef;
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数说明:引脚置位
函数返回值:无
GPIO_TypeDef* GPIOx:哪个组IO
uint16_t GPIO_Pin:哪个引脚
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
函数说明:引脚复位
函数返回值:无
GPIO_TypeDef* GPIOx:哪个组IO
uint16_t GPIO_Pin:哪个引脚
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
函数说明:读某个引脚输入状态
函数返回值:
Bit_RESET = 0 (低电平)
Bit_SET = 1 (高电平)
GPIO_TypeDef* GPIOx:哪个组IO
uint16_t GPIO_Pin:哪个引脚
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
函数说明:设置IO口与中断线的映射
函数返回值:无
uint8_t EXTI_PortSourceGPIOx:哪个组IO
uint8_t EXTI_PinSourcex:哪个引脚
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
函数说明:外部中断控制器初始化
返回值:无
typedef struct
{
uint32_t EXTI_Line; //中断线
EXTIMode_TypeDef EXTI_Mode; //模式
EXTITrigger_TypeDef EXTI_Trigger; //触发条件
FunctionalState EXTI_LineCmd; //中断线命令
}EXTI_InitTypeDef;
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
函数说明:NVIC初始化
返回值:无
NVIC_InitTypeDef* NVIC_InitStruct:NVIC结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断通道,可在stm32f4xx.h文件当中查找
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //响应优先级
FunctionalState NVIC_IRQChannelCmd; //中断通道使能
} NVIC_InitTypeDef;
六、应用产品
- LED
家用LED
指示灯
- 按键
汽车按键
电梯按键
led库函数开发
#include "led.h"
/****************************************************
LED0连接在PF9(属于GPIOF组)
PF9输出低电平(0),灯亮;PF9输出高电平(1),灯灭;
*****************************************************/
void Led_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //引脚9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽式输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ
GPIO_Init(GPIOF,&GPIO_InitStruct);
}
#ifndef __LED_H
#define __LED_H
#include "stm32f4xx.h"
void Led_Init(void);
#endif
#include "stm32f4xx.h"
#include "led.h"
void delay(int n)
{
int i, j;
for(i=0; i<n; i++)
for(j=0; j<20000; j++);
}
int main(void)
{
Led_Init();
while(1)
{
//LED0亮
GPIO_ResetBits(GPIOF, GPIO_Pin_9);
delay(1000);
//LED0灭
GPIO_SetBits(GPIOF, GPIO_Pin_9);
delay(1000);
}
}
key库函数开发
#include "key.h"
/****************************************************
引脚说明
KEY0 -- PA0
按键未按下,PA0为高电平
按键按下, PA0为低电平
*****************************************************/
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//使能GPIOA 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //引脚9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
#ifndef __KEY_H
#define __KEY_H
#include "stm32f4xx.h"
void Key_Init(void);
#endif
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
void delay(int n)
{
int i, j;
for(i=0; i<n; i++)
for(j=0; j<20000; j++);
}
int main(void)
{
int key_count = 0;
Led_Init();
Key_Init();
//查询判断按键状态
while(1)
{
/*
//应用场景1 根据按键的时长,相应的动作
//判断按键是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
//延时消抖 20ms
delay(20);
//判断按键是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
key_count++;
//按键已经按下,处理相关事件
//变更引脚状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
}
}
if(key_count == 100)//2S
{
key_count = 0;
//LED3 亮
GPIO_ResetBits(GPIOE, GPIO_Pin_14);
}
*/
//应用场景2 根据按键按下松开一次表示一个动作,比如ATM按键
//判断按键是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
//延时消抖 20ms
delay(25);
//判断按键是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
//等待按键松开
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);
//变更引脚状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
}
}
}
}
key中断
#include "exti.h"
/**************************************
引脚说明
PA0 -- EXTI0
PA0选择下降沿触发
**************************************/
void Exti_PA0_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//使能SYSCFG 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//使能GPIOA 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //引脚9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStruct);
//设置IO口与中断线的映射关系。PA0 -- EXTI0
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0; //中断线0
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStruct.EXTI_LineCmd= ENABLE; //中断线使能
//初始化线上中断,设置触发条件等。
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;//中断通道,中断通道在stm32f4xx.h文件当中查找
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能通道
//配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
}
extern void delay(int n);
/**************************************
中断服务函数
1 中断函数名字在startup_stm32f40_41xxx.s查找,且中断服务函数格式是固定:void 中断服务函数名(void)
2 中断服务函数是EXTI控制检测到下降沿并通知CPU主动去执行的函数,不需要在程序当中进行调用
***************************************/
void EXTI0_IRQHandler(void)
{
//判断标志位是否置1
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
//延时消抖 20ms
delay(25);
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
//变更引脚状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
//等待按键松开
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET);
}
}
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f4xx.h"
void Exti_PA0_Init(void);
#endif
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
void delay(int n)
{
int i, j;
for(i=0; i<n; i++)
for(j=0; j<20000; j++);
}
int main(void)
{
int key_count = 0;
//设置NVIC分组(一个工程只能设置一个分组)
//第二分组;抢占优先组取值范围:0~3 响应先组取值范围:0~3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Led_Init();
Exti_PA0_Init();
while(1)
{
}
}