IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【STM32炒冷饭】GPIO和外部中断 -> 正文阅读

[嵌入式]【STM32炒冷饭】GPIO和外部中断

【STM32炒冷饭】GPIO和外部中断(标准外设库)

用STM32系列单片机做项目已经一年了,突然想开个新坑写点东西,相当于自己的理解吧,有空就更,搭建开发环境和配置工程就不多说了,网上大把教程,介于本人实在是看不惯MDK的UI风格,所以一直使用VSCode+Keil Assist+MDK-ARM的开发方式


直接进入主题,本系列会写标准外设库和HAL库两版,使用的单片机为STM32F407ZGT6,使用了正点原子提供的SYSTEM文件夹,因为库函数本没有提供延时函数,为了方便直接使用正点原子提供的延时函数,后面也会讲如何使用系统滴答时钟构建延时函数。


关于F4系列的系统时钟

在我们使用STM32CubeMX构建工程的时候,在时钟树内可以直观的看到系统的锁相环时钟、分频、倍频,更为简单的可以直接设置你所想要的频率,STM32CubeMX会帮你自动配置好,那么在使用标准外设库时,我们没有类似的工具,而且F4系列与F1不同的是,F1已经默认为外部高速晶振为8MHz,并且将系统时钟设置为最高的72MHz,在F4系列中,我的板子HSE晶振为8MHz,那么我们想使用F407能跑到的最高168MHz,在system_stm32f4xx.c这个文件中,将宏定义改为#define PLL_M 8即可


一、什么是GPIO

GPIO的全程是(General-purpose input/output)通用输入输出口,简单的理解就是可以输出1或者0;我们来看STM32的GPIO的基本结构:
在这里插入图片描述可以看到通过下方的P-MOS和N-MOS可以设置引脚为推挽或者开漏,通过内部的上拉或者下拉电阻可以直接把引脚输出强高电平。这里的5V容忍是指引脚可以承受5V的电压,但并不是所有的引脚都可以承受5V的电压,在STMF4xx系列的数据手册中有明确标明能够容忍5V电压的管脚,在这里并不推荐直接给管脚接入5V电压,该芯片的VDD电压标称3.3V,不建议给GPIO直接输入5V电压。
下面我们分别通过发光二极管、蜂鸣器、按键来验证引脚的输入输出功能:

二、代码编写

1.输出部分

首先我们配置LED和蜂鸣器的相关管脚:
在这里插入图片描述在这里插入图片描述LED的阴极与STM32的GPIO相连,阳极通过一个分压电阻接到3.3V,那么当我们的GPIO输出低电平时,LED就能正常点亮;
通过宏定义先定义相关的管脚,使用宏定义的目的是为了提高代码的可移植性,也为了美观和便于理解阅读。这里也使用到了正点原子sys.c文件中提供的直接操作GPIO寄存器的接口函数,便于我们用51的思想去理解GPIO,51的寄存器数量在STM32面前九牛一毛,背寄存器显然是不太现实的,但是并不能完全不懂寄存器的操作。

/* LED Macro */
#define LEDx_GPIO_PROT GPIOF
#define LEDx_RCC_CLOCK RCC_AHB1Periph_GPIOF
#define LED0_PIN_NUM GPIO_Pin_9
#define LED1_PIN_NUM GPIO_Pin_10

#define LED_NUM_0 PFout(9)
#define LED_NUM_1 PFout(10)

/* LED Status */
typedef enum
{
    LED_ON = 0,
    LED_OFF
} LedStatus_TypeDef_t;

上面我们枚举了LED的两种状态,便于理解;

在GPIO的初始化中,根据实际硬件,将GPIO设置为推挽输出模式;这里设置上拉或者下拉都无所谓,推挽输出本就可以直接输出强力高电平和低电平,开漏输出只能输出低电平,需要外部上拉电阻才能输出高电平。
下面提供了两个函数,只是对ST提供的GPIO相关函数进行了封装方便使用;

/**
  * @name   Led_Init
  * @brief  LedPin Init Function
  * @param  InitState: Leds initial state
  * @retval None
  */
void Led_Init(LedStatus_TypeDef_t InitState)
{
    GPIO_InitTypeDef GPIO_InitSturcture;
    RCC_AHB1PeriphClockCmd(LEDx_RCC_CLOCK, ENABLE);

    GPIO_InitSturcture.GPIO_Pin = LED0_PIN_NUM | LED1_PIN_NUM;
    GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitSturcture.GPIO_OType = GPIO_OType_PP;
    GPIO_InitSturcture.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_2MHz;

    GPIO_Init(LEDx_GPIO_PROT, &GPIO_InitSturcture);
    LED_NUM_0 = InitState;
    LED_NUM_1 = InitState;
}

/**
  * @name   Led_SetState
  * @brief  LedPin set level function
  * @param  LedPinx: Leds pin number
  * @param  LedState: Leds State
  * @retval None
  */
void Led_SetState(uint16_t LedPinx, LedStatus_TypeDef_t LedState)
{
    GPIO_WriteBit(LEDx_GPIO_PROT, LedPinx, (BitAction)LedState);
}

/**
  * @name   Led_ToggleState
  * @brief  LedPin set state inversion function
  * @param  LedPinx: Leds pin number
  * @retval None
  */
void Led_ToggleState(uint16_t LedPinx)
{
    GPIO_ToggleBits(LEDx_GPIO_PROT, LedPinx);
}

蜂鸣器同理:
在这里插入图片描述硬件电路上使用一个三极管驱动蜂鸣器,个人认为应该在蜂鸣器两端并联一个0.1uF的陶瓷电容,感性元件接通关断的瞬间会产生一个尖刺,可使用0.1uF的电容消除尖刺。S8050是一个NPN型的三极管,基极输入高电平时导通。

#define BUZZER_GPIO_PORT GPIOF
#define BUZZER_RCC_CLOCK RCC_AHB1Periph_GPIOF
#define BUZZER_PIN_NUM GPIO_Pin_8

typedef enum
{
    BUZZER_OFF = 0,
    BUZZER_ON
} BuzzerStatus_TypeDef_t;
/**
  * @name   Buzzer_Init
  * @brief  BuzzerPin Init Function
  * @param  None
  * @retval None
  */
void Buzzer_Init(void)
{
    GPIO_InitTypeDef GPIO_InitSturcture;
    RCC_AHB1PeriphClockCmd(BUZZER_RCC_CLOCK, ENABLE);

    GPIO_InitSturcture.GPIO_Pin = BUZZER_PIN_NUM;
    GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitSturcture.GPIO_OType = GPIO_OType_PP;
    GPIO_InitSturcture.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_2MHz;

    GPIO_Init(BUZZER_GPIO_PORT, &GPIO_InitSturcture);
}

/**
  * @name   Buzzer_SetState
  * @brief  Buzzer set level function
  * @param  None
  * @retval None
  */
void Buzzer_SetState(BuzzerStatus_TypeDef_t BuzzerState)
{
    GPIO_WriteBit(BUZZER_GPIO_PORT, BUZZER_PIN_NUM, (BitAction)BuzzerState);
}

/**
  * @name   Buzzer_ToggleState
  * @brief  Buzzer set state inversion function
  * @param  None
  * @retval None
  */
void Buzzer_ToggleState(void)
{
    GPIO_ToggleBits(BUZZER_GPIO_PORT, BUZZER_PIN_NUM);
}

/**
  * @name   Buzzer_Beep
  * @brief  Buzzer beep once function
  * @param  None
  * @retval None
  */
void Buzzer_Beep(void)
{
    Buzzer_ToggleState();
    delay_ms(10);
    Buzzer_ToggleState();
}

2.输入部分

通过原理图可以看到,当按键按下时将GPIO直接与GND相接,我们可以通过判断引脚的输入电平是高电平还是低电平进行判断按键按下与否:
在这里插入图片描述

/* Key Macro */
#define KEYx_GPIO_PORT GPIOE
#define WAKE_UP_GPIO_PORT GPIOA
#define KEYx_RCC_CLOCK RCC_AHB1Periph_GPIOE
#define WAKE_UP_RCC_CLOCK RCC_AHB1Periph_GPIOA
#define KEY0_PIN_NUM GPIO_Pin_4
#define KEY1_PIN_NUM GPIO_Pin_3
#define KEY2_PIN_NUM GPIO_Pin_2
#define WAKE_UP_PIN_NUM GPIO_Pin_0
#define KEY0 PEin(4)
#define KEY1 PEin(3)
#define KEY2 PEin(2)
#define WAKE_UP PAin(0)

typedef enum
{
    KEY_DOWN = 0,
    KEY_UP
} KeyStatus_TypeDef_t;

#define KEY_MODE_NORMAL 0x00
#define KEY_MODE_INTERRUPT 0x01
#define KEY_TRIGGER_MODE KEY_MODE_NORMAL

/**
  * @name   Key_Init
  * @brief  LedPin Init Function
  * @param  None
  * @retval None
  */
void Key_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(KEYx_RCC_CLOCK, ENABLE);
    RCC_AHB1PeriphClockCmd(WAKE_UP_RCC_CLOCK, ENABLE);

    GPIO_InitStructure.GPIO_Pin = KEY0_PIN_NUM | KEY1_PIN_NUM | KEY2_PIN_NUM;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

    GPIO_Init(KEYx_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = WAKE_UP_PIN_NUM;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

    GPIO_Init(WAKE_UP_GPIO_PORT, &GPIO_InitStructure);

#if KEY_TRIGGER_MODE

    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;

    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

#endif
}

/**
  * @name   Key_Init
  * @brief  LedPin Init Function
  * @param  None
  * @retval 
  */
uint8_t Key_Scan(void)
{
    static uint8_t KeyState = KEY_UP;
    if (KeyState && (KEY0 == KEY_DOWN || KEY1 == KEY_DOWN || KEY2 == KEY_DOWN || WAKE_UP == KEY_UP))
    {
        delay_ms(10);
        KeyState = KEY_DOWN;
        if (KEY0 == KEY_DOWN)
        {
            Buzzer_Beep();
            return 1;
        }
        else if (KEY1 == KEY_DOWN)
        {
            Buzzer_Beep();
            return 2;
        }
        else if (KEY2 == KEY_DOWN)
        {
            Buzzer_Beep();
            return 3;
        }
        else if (WAKE_UP == KEY_UP)
        {
            Buzzer_Beep();
            return 4;
        }
    }
    else if (KEY0 == KEY_UP && KEY1 == KEY_UP && KEY2 == KEY_UP && WAKE_UP == KEY_DOWN)
        KeyState = KEY_UP;
    return 0;
}

细心地小伙伴已经发现,在按键的初始化代码中,加入了外部中断的初始化,在这里我通过预编译指令进行对按键模式的初始化,通过改变对按键模式的宏定义可以选择是否使能按键触发外部中断的功能


验证功能

在主函数中加入这样一段代码,使用按键来改变LED的状态;

switch (Key_Scan())
        {
        case 1:
            /* code */
            Led_ToggleState(LED0_PIN_NUM);
            break;

        case 2:
            /* code */
            Led_ToggleState(LED1_PIN_NUM);
            break;
        case 3:
            /* code */
            Led_ToggleState(LED0_PIN_NUM);
            Led_ToggleState(LED1_PIN_NUM);
            break;
        }

烧录验证:
在这里插入图片描述
GIF中无法听到按下按键时蜂鸣器的bibi提升音;

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-01 12:05:12  更:2021-09-01 12:07:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:36:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码