• stm32—GPIO


    0. 引入

    在单片机产品中,我们常常可以见到三种模块:LED灯、KEY按键、BEEP蜂鸣器


    LED灯:

            一个比较常见的LED电路

    LED0 ---------- 通过控制LED0引脚(电线)

            给它一个低电平(低电压),LED1灯就会亮

            给它一个高电平(高电压),LED1灯就会灭

            

            1  —> 高电平

            0  —> 低电平

            电流:从电势高的地方流向电势低的地方


            CPU  ===> 往LED0引脚去 写1,写0

                    "output" 输出功能

    KEY按键:

            一个比较常见的KEY电路


    KEY0  ------  通过读取KEY0引脚的电平状态来知晓用户是否按下按键

            高电平(1) ---> 弹起

            低电平(0) ---> 按下

            CPU  ===> 读取KEY0引脚的电平状态

                    "input" 输入功能

    BEEP蜂鸣器:

            一个比较常见的BEEP电路

            
    BEEP ----------- 通过控制BEEP引脚(电线)

            给它一个高电平(高电压),BEEP就会响

            给它一个低电平(低电压),BEEP就不响

            

            CPU  ===> 往BEEP引脚去 写1,写0

                    "output" 输出功能


    两种三极管:PNP   and   NPN   ===> P指向N

    这些引脚最终是接入到MCU的某个引脚(gpio)上去的
    控制LED灯、KEY按键、BEEP蜂鸣器等,可以在MCU上面写程序去控制这些引脚

    1. GPIO到底是什么?

    GPIO:General Purpose Input Output 通用功能的输入输出 线
     

    GPIO就是从芯片( CPU + 总线 + 外设控制器)内部引出一根功能复用的口线("电线"),可以由CPU配置成不同的功能

            如:输入功能,输出功能,其他复用功能等

    芯片或CPU控制整个世界就是通过这样的引脚(口线,GPIO)

    STM32F4xx共有144个GPIO口线(引脚,pin),分为9组,记为GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG,GPIOH,GPIOI. 每组管理16个GPIO引脚,编号从0~15

            如:GPIOA这一组有16个引脚,分别记为GPIOA0,GPIOA1,GPIOA2,... GPIOA15
                    其他组类似

                    GPIOA0  -----> PA0

                    GPIOB3  ------> PB3

                    ......

    这些GPIO引脚都是功能复用的,并且由GPIO控制器来控制它们的

            所有的外设都是由"外设控制器"来控制

    GPIO控制器由不同的寄存器来配置或控制它们(GPIOx) 

    1. 每组GPIO地址分配如下:
    2. GPIOA 0x4002 0000 ~ 0x4002 03ff
    3. GPIOB 0x4002 0400 ~ 0X4002 07FF
    4. GPIOC 0x4002 0800 ~ 0x4002 0BFF
    5. GPIOD 0x4002 0C00 ~ 0x4002 0FFF
    6. GPIOE 0x4002 1000 ~ 0x4002 13FF
    7. GPIOF 0x4002 1400 ~ 0x4002 17FF
    8. GPIOG 0x4002 1800 ~ 0x4002 1BFF
    9. GPIOH 0x4002 1C00 ~ 0x4002 1FFF
    10. GPIOI 0x4002 2000 ~ 0x4002 23FF

    2. STM32F4xx GPIO内部结构原理


    每个GPIO内部都可以配置成:

            1. 输入功能:input mode

                    CPU可以获取该GPIO口的外部输入的一个电平状态

                    输入功能有四种模式:

                            (1) 输入悬空(input floating):不接上拉和下拉电阻

                                    输入引脚处于浮空状态,‌即没有特定电压状态,‌引脚悬浮在空中

                                    IO引脚的电平状态完全是外部输入所决定的,这时CPU能够通过读取数据的操作知道状态

                            (2) 带上拉输入(input pull-up):内部接上拉电阻

                                    该引脚被设置为上拉输入时,引脚悬空的状态下,CPU读取到的电平状态为高电平,因为内部有一个上拉电阻;唯有当被外部输入信号下拉时,CPU读取到的电平才为低电平

                            (3) 带下拉输入(input pull-down): 内部接下拉电阻

                                   该引脚被设置为下拉输入时,引脚悬空的状态下,CPU读取到的电平状态为低电平。唯有当被外部输入信号上拉时,CPU读取到的电平状态才为高电平

            

                            (4) 模拟输入(Input Analog)

                                    该引脚被设置为模拟输入时,能够获取外部的模拟信号,通过芯片内的ADC转换为数字量,如变化的电压值


            2. 输出功能:output mode

                    CPU可以往该GPIO口输出一个电平状态

                    输入功能有两种模式:

                            (1) 输出推挽(Push-Pull):可以输出高、低电平

                                    可以往外部引脚输出一个高电平(1)或低电平(0)

                                    1:MOS管上方导通,下方不导通,在此处数字量变成模拟量,输出高电平

                                    0:MOS管下方导通,上方不导通,在此处数字量变成模拟量,输出低电平

                            (2) 输出开漏(Open-Drain):不输出电压

                                    低电平接地,高电平不接地(悬空状态)

                                    如果外部电路接上拉电阻,则在CPU输出1时会接到外部上拉电阻的电源电压上

                                    0:ping  接地

                                    1:ping  悬空    此时需要外部电路中设计上拉电阻

            3. 复用功能:Alternate Function 

                    复用功能是指GPIO口用作其它的功能口线,如: I^2C,UART,SPI等

                    每个GPIO口都可以配置成多达16种复用功能,记为: AF0,AF1,AF2 ...  AF15,具体哪个GPIO口可以配置成哪种复用功能,需要看原理图

                    其实在芯片设计的,每个GPIO口能够复用的功能,就已经定了

                    UART0 ---> TX0 是不是任意一个GPIO口都可以复用这个Tx0这个引脚呢?

                            肯定不是啦,这个在芯片设计的时候,就已经定了
                    

    STM32F4xx每个GPIO口内部都有一个上拉/下拉电阻,你可以enable/disable它,根据具体应用场景需要

    每个GPIO口的操作都是通过GPIO的寄存器来控制的

    3. STM32F4xx GPIO寄存器说明

    每个通用 I/O 端口包括:

        4 个 32 位配置寄存器

            (GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR 和 GPIOx_PUPDR)
        2 个 32 位数据寄存器(GPIOx_IDR 和 GPIOx_ODR)

        1 个 32 位置位/复位寄存器 (GPIOx_BSRR)
        1 个 32 位锁定寄存器 (GPIOx_LCKR) 
        2 个 32 位复用功能选择寄存器(GPIOx_AFRL 和 GPIOx_AFRH)


    (1) GPIOx_MODER
            模式选择寄存器,该寄存器的地址偏移为 0x00
                    地址偏移:每一组GPIO寄存器都有一个地址范围

                            如:GPIOA :  0x4002 0000 ~ 0x4002 03FF

                            GPIOA这一组的寄存器的基地址是0x4002 0000

                    因此, GPIOA_MODER 的地址就是:0x4002 0000 + 0x00

            模式寄存器用来配置GPIO的功能的(input / output / analog(模拟) / AF)

            该寄存器用来控制x(x=A,B,C...,I)组的16个GPIO引脚的模式,每个GPIO口占2bits(总共有四种模式,四种功能)

            编号为y(y=0,1,2...,15)的GPIO引脚在该寄存器的bit位为GPIOx_MODER[2y+1:2y]

            具体配置如下:

                    GPIOx_MODER[2y+1:2y]                     模式

                                    00                                         输入模式

                                    01                                        通用输出模式

                                    10                                    复用功能,Alternate Function

                                    11                                          模拟输入

    (2) GPIOx_OTYPER

            输出类型寄存器,该寄存器的地址偏移为 0x04
            该寄存器用来控制x(x=A,B,C,...,I)分组的16个GPIO的输出类型,每个GPIO口1bit

            编号为y(y=0,1,2,...,15)的GPIO在该寄存器的bit位置为GPIOx_OTYPER[y]
            具体输出类型如下:

                    GPIOx_OTYPER[y]                     输出类型

                                  0                                输出推挽(Push-Pull)

                                  1                                输出开漏(Open-Drain)

            注:

                    输出推挽(Push-Pull),不带上下拉电阻    

                            cpu写1  ----> (外部引脚)高电平

                                      0  -----> (外部引脚)低电平
                    输出开漏(Open-Drain),不带上下拉电阻

                            cpu写0  ----> (外部引脚)接地

                                      1  ----> (外部引脚)悬空


    (3) GPIOx_OSPEEDR

            端口输出速度寄存器,偏移地址:0x08
            该寄存器用来控制x(x=A,B,C,...,I)分组的16个GPIO的输出速度,每个GPIO口2bits

            编号为y(y=0,1,2,...,15)的GPIO在该寄存器中的bit位置为GPIOx_OSPEEDRGPIO

    [2y+1:2y]

            具体输出速度如下:

                                    GPIOx_OSPEEDRGPIO[2y+1:2y]                          输出速度

                                                            00                                                     2Mhz(低速)

                                                            01                                                     25Mhz(中速)

                                                            10                                                     50Mhz (高速)

                                                            11                                                   30pf 时为 100Mhz

                                                                                                                   15pf 时为80Mhz

    (4) GPIOx_PUPDR

            pu: pull up  上拉

            pd: pull down  下拉

            端口上拉/下拉寄存器,地址偏移为:0x0C

            该寄存器用来控制x(x=A,B,C,..,I)分组的16个GPIO内部上下拉电阻的选择,每个GPIO口2bits

            编号为y(y=0,1,2..., 15)的GPIO口在该寄存器中的bit 位置为 GPIOx_PUPDR[2y+1:2y]

            具体选择情况如下:

                  GPIOx_PUPDR[2y+1:2y]                         上下拉情况

                           00                                  无上拉也无下拉 disable pull-up  disable pull_down

                           01                                               上拉   enable pull-up

                           10                                               下拉   enable pull-down

                           11                                                    保留,没用到


    (5) GPIOx_IDR

            Input Data Register  输入数据寄存器

            端口输入数据寄存器,偏移地址为: 0x10,复位值: 0x0000 XXXX (开机后高16位值为0,低16位的值不确定)

            该寄存器用来表示x(x=A,B,C,...,I)分组的16GPIO引脚输入的值,其中31:16保留
            高16bits,必须保持复位值
    。bit 15:0 表示相应的GPIO引脚的输入电平的状态。这些bit,只能为只读,只能在字模式下访问

    (6) GPIOx_ODR
            Output Data Register 输出数据寄存器

            端口输出数据寄存器,偏移地址为:0x14,复位值为: 0x0000 0000
            该寄存器用来表示x(x=A,B,C,...,I)分组的16个GPIO的输出值,其中31:16保留。
    低位15:0 表示相应的GPIO口线的输出状态

            可以读取也可以写入


    (7) GPIOx_BSRR

            BSR: Bit Set Reset  位置位复位操作
                    Set ----> 把相应的bit位置1
                    Reset -----> 把相应的bit位置0
            
    端口置位/复位寄存器,偏移地址:0x18,复位值为:0x0000 0000
                    位 31:16  BRx: Bit Reset 端口的复位bit位,这些位为只写形式,
    只能在字、半字、或字节模式下访问

                            往相应的bit位写:

                                    1: 对相应的ODR(x-16)位进行复位(0)

                                    0: 不会对相应的ODR(x-16)执行任何操作

                    位 15:0  BSx: Bit Set  端口x置位,这些位为只写形式,只能在字、半字、或字节模式下访问
                            往相应的bit位写:

                                    1: 对相应的ODRx位进行置位(1)
                                    0: 不会对相应的ODRx位进行操作


    注意:如果同时对BSx和BRx置位,则BSx的优先级更高

    (8) GPIOx_LCKR
            端口配置锁定寄存器,偏移地址为:0x1C,复位值为:0x0000 0000

            

            LOCK:

                    当一个GPIO引脚的各配置寄存器设定好后,为了防止程序对这些已经配置好的寄存器的一些误操作,可以 LOCK一下

                    如果是一个LOCK的状态,该GPIO引脚的配置就不能再修改了

                    如果要修改就必须 UNLOCK

            

            位 31:17保留,必须保持复位值
            位 16 LCKK[16]:锁定键,可随时读取此位

        
    (9) GPIOx_AFRL  偏移量  0x20

    (10) GPIOx_AFRL   偏移量  0x24

            AFR:Alternate Function Register  复用功能寄存器
            

            每一个GPIO口可以复用多达16种复用功能,每一个GPIO口就需要4bits,一组GPIO口(16个GPIO口),就需要 16x4= 64bits

            所以复用功能寄存器就需要两个32bits的寄存器 GPIOx_AFRL,GPIOx_AFRH

            编号 0...7   -----> GPIOx_AFRL
            编号 8...15 -----> GPIOx_AFRH
                    AF0     0000

                    AF1     0001

                    AF2     0010

                    AF15   1111

                            AFx到底是复用何种功能,得看芯片手册        

    4. GPIO寄存器点灯

    注意:keil 的每个文件最后都必须空格一行

    (1) 通过直接操作STM32f4xx的GPIO寄存器,点亮目标板上的LED1灯

            查看原理图:

                    要把电路图的原理搞懂

                            LED0这个引脚

                                    高电平 -----> led灯灭

                                    低电平 -----> led灯亮

                    看对应的引脚LED0对应的芯片(CPU)内部是哪个引脚:

                            PF9:GPIOF9

            步骤:

                    (1) 使能GPIO分组的时钟   <-----  "时钟单元"  "上电"

                    (2) 根据应用场景(原理图),配置GPIO

                            GPIOx_MODER

                            GPIOx_OTYPER

                            GPIOx_OSPEEDRGPIO

                            GPIOx_PUPDR

                    (3) GPIOx_ODR

    5. 使用stm32官方固件库来操作GPIO口

    stm32固件库移植 -----> 具体移植操作参考文档

    5.1 配置AHB1总线上的外设时钟   RCC_AHB1PeriphClockCmd

    1. // 等同于RCC_AHB1ENR寄存器
    2. void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph,
    3. FunctionalState NewState)
    4. @RCC_AHB1Periph:指定AHB1总线外设,也就是要配置时钟的外设
    5. 可以是以下任意一个宏:
    6. RCC_AHB1Periph_GPIOA
    7. RCC_AHB1Periph_GPIOB
    8. ...
    9. RCC_AHB1Periph_GPIOI
    10. @NewState:指定该外设的时钟状态
    11. ENBALE 使能,为该外设提供时钟信号
    12. DISBALE 禁止,不提供
    13. ------------------------------------------------------------------
    14. 比如:使能GPIOF时钟
    15. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

    5.2 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)     GPIO_init

    1. void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
    2. @GPIOx:指定要初始化的GPIO分组
    3. GPIOA
    4. GPIOB
    5. ...
    6. GPIOI
    7. @GPIO_InitStruct:指向GPIO初始化信息结构体
    8. 该结构体原型如下所示:(已经定义在头文件中,直接使用就好)
    9.     typedef struct {
    10.         uint32_t GPIO_Pin;         
    11.           GPIOMode_TypeDef GPIO_Mode;    
    12.           GPIOSpeed_TypeDef GPIO_Speed;  
    13.           GPIOOType_TypeDef GPIO_OType;  
    14.         GPIOPuPd_TypeDef GPIO_PuPd;  
    15.       } GPIO_InitTypeDef;
    16. @GPIO_Pin:指定要配置的GPIO引脚(可以位或多个,表示配置同一组的多个引脚为相
    17. 同模式,如:GPIO_Pin_0 | GPIO_Pin_1)
    18.         GPIO_Pin_0
    19.                GPIO_Pin_1
    20.               ...
    21.                GPIO_Pin_15
    22. @GPIO_Mode:指定要配置的GPIO引脚的功能模式
    23. GPIO_Mode_IN 输入模式
    24. GPIO_Mode_OUT 输出模式
    25. GPIO_Mode_AF 复用功能模式
    26. GPIO_Mode_AN 模拟模式
    27. @GPIO_Speed:指定引脚速率
    28. GPIO_Speed_2MHz 2M低速
    29. GPIO_Speed_25MHz 25M中速
    30. GPIO_Speed_50MHz 50M快速
    31. GPIO_Speed_100MHz 100M高速
    32. @GPIO_OType:指定输出类型
    33. GPIO_OType_PP 输出推挽
    34. GPIO_OType_OD 输出开漏
    35. @GPIO_PuPd:指定上下拉选择
    36. GPIO_PuPd_NOPULL 无上拉,也无下拉
    37. GPIO_PuPd_UP 上拉
    38. GPIO_PuPd_DOWN 下拉
    39. ---------------------------------------------------------------
    40. 比如:配置PF9为带下拉的推挽输出模式
    41. // 定义GPIO初始化信息结构体
    42. GPIO_InitTypeDef g;
    43. // 根据配置需要,对结构体成员赋值
    44. g.GPIO_Pin = GPIO_Pin_9; // 9号引脚
    45. g.GPIO_Mode = GPIO_Mode_OUT; // 输出模式
    46. g.GPIO_Speed = GPIO_Speed_50MHz; // 50MHz
    47. g.GPIO_OType = GPIO_OType_PP; // 输出推挽
    48. g.GPIO_PuPd = GPIO_PuPd_DOWN; // 下拉
    49. // 根据结构体信息,完成GPIO配置
    50. GPIO_Init(GPIOF, &g);

    5.3 获取指定GPIO分组输入数据寄存器中的值,并通过返回值返回  GPIO_ReadInputData

    1. uint16_t GPIO_ReadInputData(GPIO_TypeDef *GPIOx)
    2. @GPIOx:指定GPIO分组
    3. GPIOA
    4. GPIOB
    5. ...
    6.        GPIOI
    7.        
    8. 返回值:
    9. 返回获取到的GPIO分组的输入数据寄存器中数据
    10.    
    11. 要注意的是该返回值是uint16_t类型,具有16bits,分别对应该GPIO分组的16个GPIO引脚的输入
    12. 电平状态
    13. bit0 --> 0号引脚
    14.    ...
    15.    bit15 --> 15号引脚

    5.4 获取指定GPIO引脚的输入值   GPIO_ReadInputDataBit

    1. uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    2. @GPIOx:指定GPIO分组
    3. GPIOA
    4. GPIOB
    5. ...
    6. GPIOI
    7. @GPIO_Pin:指定GPIO引脚
    8. GPIO_Pin_0
    9. ...
    10. GPIO_Pin_15
    11. 返回值:
    12. 返回指定GPIO引脚的电平状态(1个引脚的状态)
    13. Bit_SET 表示该GPIO引脚输入为高电平
    14. Bit_RESET   表示该GPIO引脚输入为低电平

    5.5 GPIO_SetBits / GPIO_ResetBits

    1. GPIO_SetBits:用来将指定的GPIO引脚输出为高电平(GPIO_BSRR)
    2. void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    3. -----------------------------------------------------------------
    4. GPIO_ResetBits用来将指定的GPIO引脚输出低电平(GPIO_BSRR)
    5. void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

    6. 固件库操作GPIO

    BSP_led.h

    1. #ifndef __BSP_LED_H__
    2. #define __BSP_LED_H__
    3. #include "stm32f4xx.h"
    4. // 哪组寄存器
    5. #define LED1_GPIO GPIOF
    6. #define LED2_GPIO GPIOF
    7. #define LED3_GPIO GPIOE
    8. #define LED4_GPIO GPIOE
    9. // 哪个引脚
    10. #define LED1_Pin GPIO_Pin_9
    11. #define LED2_Pin GPIO_Pin_10
    12. #define LED3_Pin GPIO_Pin_13
    13. #define LED4_Pin GPIO_Pin_14
    14. // LED灯需要配置的时钟外设
    15. #define LED1_RCC_AHB1Periph RCC_AHB1Periph_GPIOF
    16. #define LED2_RCC_AHB1Periph RCC_AHB1Periph_GPIOF
    17. #define LED3_RCC_AHB1Periph RCC_AHB1Periph_GPIOE
    18. #define LED4_RCC_AHB1Periph RCC_AHB1Periph_GPIOE
    19. // 使能时钟
    20. #define LED1_Clock() RCC_AHB1PeriphClockCmd(LED1_RCC_AHB1Periph, ENABLE)
    21. #define LED2_Clock() RCC_AHB1PeriphClockCmd(LED2_RCC_AHB1Periph, ENABLE)
    22. #define LED3_Clock() RCC_AHB1PeriphClockCmd(LED3_RCC_AHB1Periph, ENABLE)
    23. #define LED4_Clock() RCC_AHB1PeriphClockCmd(LED4_RCC_AHB1Periph, ENABLE)
    24. // 开
    25. #define LED1_ON() GPIO_ResetBits(LED1_GPIO, LED1_Pin)
    26. // 关
    27. #define LED1_OFF() GPIO_SetBits(LED1_GPIO, LED1_Pin)
    28. #define LED2_ON() GPIO_ResetBits(LED2_GPIO, LED2_Pin)
    29. #define LED2_OFF() GPIO_SetBits(LED2_GPIO, LED2_Pin)
    30. #define LED3_ON() GPIO_ResetBits(LED3_GPIO, LED3_Pin)
    31. #define LED3_OFF() GPIO_SetBits(LED3_GPIO, LED3_Pin)
    32. #define LED4_ON() GPIO_ResetBits(LED4_GPIO, LED4_Pin)
    33. #define LED4_OFF() GPIO_SetBits(LED4_GPIO, LED4_Pin)
    34. void LED_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x);
    35. #endif

    BSP_led.c

    1. #include "BSP_led.h"
    2. void LED_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x) {
    3. // 2.初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    4. // 2.1 定义GPIO初始化信息结构体
    5. GPIO_InitTypeDef g;
    6. // 2.2 根据配置需要,对结构体成员赋值
    7. g.GPIO_Pin = GPIO_Pin_x; // ????
    8. g.GPIO_Mode = GPIO_Mode_OUT; // ????
    9. g.GPIO_Speed = GPIO_Speed_2MHz; // 2MHz
    10. g.GPIO_OType = GPIO_OType_PP; // ????
    11. g.GPIO_PuPd = GPIO_PuPd_NOPULL; // ???????
    12. // 2.3 根据结构体信息,完成GPIO配置
    13. GPIO_Init(LEDx, &g);
    14. }

    (1) 点亮LED灯

    1. #include "led.h"
    2. int main() {
    3. /* 点亮LED1灯 */
    4. // 1. 使能时钟
    5. LED1_Clock();
    6. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    7. LED_Init(LED1_GPIO, LED1_Pin);
    8. // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开
    9. LED1_ON();
    10. // --------------------------------------------------------
    11. /* 点亮LED2灯 */
    12. // 1. 使能时钟
    13. LED2_Clock();
    14. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    15. LED_Init(LED2_GPIO, LED2_Pin);
    16. // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开
    17. LED2_ON();
    18. // ---------------------------------------------------------
    19. /* 点亮LED3灯 */
    20. // 1. 使能时钟
    21. LED3_Clock();
    22. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    23. LED_Init(LED3_GPIO, LED3_Pin);
    24. // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开
    25. LED3_ON();
    26. // ---------------------------------------------------------
    27. /* 点亮LED4灯 */
    28. // 1. 使能时钟
    29. LED4_Clock();
    30. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    31. LED_Init(LED4_GPIO, LED4_Pin);
    32. // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开
    33. LED4_ON();
    34. while (1);
    35. return 0;
    36. }

    (2) 一个按键控制一个灯,按下就亮,松开就灭

    BSP_key.h

    1. #ifndef __BSP_KEY_H__
    2. #define __BSP_KEY_H__
    3. #include "stm32f4xx.h"
    4. /*
    5. key1:PA0
    6. key2:PE2
    7. key3:PE3
    8. key3:PE4
    9. */
    10. // 哪组寄存器
    11. #define KEY1_GPIO GPIOA
    12. #define KEY2_GPIO GPIOE
    13. #define KEY3_GPIO GPIOE
    14. #define KEY4_GPIO GPIOE
    15. // 哪个引脚
    16. #define KEY1_Pin GPIO_Pin_0
    17. #define KEY2_Pin GPIO_Pin_2
    18. #define KEY3_Pin GPIO_Pin_3
    19. #define KEY4_Pin GPIO_Pin_4
    20. // KEY按键需要配置的时钟外设
    21. #define KEY1_RCC_AHB1Periph RCC_AHB1Periph_GPIOA
    22. #define KEY2_RCC_AHB1Periph RCC_AHB1Periph_GPIOE
    23. #define KEY3_RCC_AHB1Periph RCC_AHB1Periph_GPIOE
    24. #define KEY4_RCC_AHB1Periph RCC_AHB1Periph_GPIOE
    25. // 使能时钟
    26. #define KEY1_Clock() RCC_AHB1PeriphClockCmd(KEY1_RCC_AHB1Periph, ENABLE)
    27. #define KEY2_Clock() RCC_AHB1PeriphClockCmd(KEY2_RCC_AHB1Periph, ENABLE)
    28. #define KEY3_Clock() RCC_AHB1PeriphClockCmd(KEY3_RCC_AHB1Periph, ENABLE)
    29. #define KEY4_Clock() RCC_AHB1PeriphClockCmd(KEY4_RCC_AHB1Periph, ENABLE)
    30. void KEY_Init(GPIO_TypeDef *KEYx, uint16_t GPIO_Pin_x);
    31. /*
    32. 高电平(1) ---> 弹起
    33. 低电平(0) ---> 按下
    34. */
    35. int KEY_ReadInputData(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin_x);
    36. #endif

    BSP_key.c

    1. #include "BSP_key.h"
    2. void KEY_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x) {
    3. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    4. // 2.1 定义GPIO初始化信息结构体
    5. GPIO_InitTypeDef g;
    6. // 2.2 根据配置需要,对结构体成员赋值
    7. g.GPIO_Pin = GPIO_Pin_x; // 几号引脚
    8. g.GPIO_Mode = GPIO_Mode_IN; // 输入模式
    9. g.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上拉也无下拉
    10. // 2.3 根据结构体信息,完成GPIO配置
    11. GPIO_Init(LEDx, &g);
    12. }
    13. /*
    14. 高电平(1) ---> 弹起
    15. 低电平(0) ---> 按下
    16. */
    17. int KEY_ReadInputData(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin_x) {
    18. if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == Bit_SET) {
    19. return 1;
    20. }
    21. return 0;
    22. }
    1. #include "stm32f4xx.h"
    2. #include "BSP_led.h"
    3. #include "BSP_key.h"
    4. int main(void) {
    5. /* LED1灯 */
    6. // 1. 使能时钟
    7. LED1_Clock();
    8. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    9. LED_Init(LED1_GPIO, LED1_Pin);
    10. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    11. LED1_OFF();
    12. // --------------------------------------------------------
    13. /* LED2灯 */
    14. // 1. 使能时钟
    15. LED2_Clock();
    16. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    17. LED_Init(LED2_GPIO, LED2_Pin);
    18. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    19. LED2_OFF();
    20. // ---------------------------------------------------------
    21. /* LED3灯 */
    22. // 1. 使能时钟
    23. LED3_Clock();
    24. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    25. LED_Init(LED3_GPIO, LED3_Pin);
    26. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    27. LED3_OFF();
    28. // ---------------------------------------------------------
    29. /* LED4灯 */
    30. // 1. 使能时钟
    31. LED4_Clock();
    32. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    33. LED_Init(LED4_GPIO, LED4_Pin);
    34. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    35. LED4_OFF();
    36. // ---------------------------------------------------------
    37. /* KEY1 */
    38. // 1. 使能时钟
    39. KEY1_Clock();
    40. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    41. KEY_Init(KEY1_GPIO, KEY1_Pin);
    42. // ---------------------------------------------------------
    43. /* KEY2 */
    44. // 1. 使能时钟
    45. KEY2_Clock();
    46. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    47. KEY_Init(KEY2_GPIO, KEY2_Pin);
    48. // ---------------------------------------------------------
    49. /* KEY3 */
    50. // 1. 使能时钟
    51. KEY3_Clock();
    52. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    53. KEY_Init(KEY3_GPIO, KEY3_Pin);
    54. // ---------------------------------------------------------
    55. /* KEY4 */
    56. // 1. 使能时钟
    57. KEY4_Clock();
    58. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    59. KEY_Init(KEY4_GPIO, KEY4_Pin);
    60. while (1) {
    61. // 1:弹起 0:按下
    62. // KEY1控制LED1
    63. if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin)) {
    64. // 弹起
    65. LED1_OFF();
    66. } else {
    67. // 按下
    68. LED1_ON();
    69. }
    70. // KEY2控制LED2
    71. if (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin)) {
    72. // 弹起
    73. LED2_OFF();
    74. } else {
    75. // 按下
    76. LED2_ON();
    77. }
    78. // KEY3控制LED3
    79. if (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin)) {
    80. // 弹起
    81. LED3_OFF();
    82. } else {
    83. // 按下
    84. LED3_ON();
    85. }
    86. // KEY4控制LED4
    87. if (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin)) {
    88. // 弹起
    89. LED4_OFF();
    90. } else {
    91. // 按下
    92. LED4_ON();
    93. }
    94. }
    95. }

    (3) 一个按键控制一个灯, 按一下就亮,再按一下就灭

    1. #include "stm32f4xx.h"
    2. #include "BSP_led.h"
    3. #include "BSP_key.h"
    4. int main(void) {
    5. /* LED1灯 */
    6. // 1. 使能时钟
    7. LED1_Clock();
    8. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    9. LED_Init(LED1_GPIO, LED1_Pin);
    10. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    11. LED1_OFF();
    12. // --------------------------------------------------------
    13. /* LED2灯 */
    14. // 1. 使能时钟
    15. LED2_Clock();
    16. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    17. LED_Init(LED2_GPIO, LED2_Pin);
    18. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    19. LED2_OFF();
    20. // ---------------------------------------------------------
    21. /* LED3灯 */
    22. // 1. 使能时钟
    23. LED3_Clock();
    24. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    25. LED_Init(LED3_GPIO, LED3_Pin);
    26. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    27. LED3_OFF();
    28. // ---------------------------------------------------------
    29. /* LED4灯 */
    30. // 1. 使能时钟
    31. LED4_Clock();
    32. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    33. LED_Init(LED4_GPIO, LED4_Pin);
    34. // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关
    35. LED4_OFF();
    36. // ---------------------------------------------------------
    37. /* KEY1 */
    38. // 1. 使能时钟
    39. KEY1_Clock();
    40. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    41. KEY_Init(KEY1_GPIO, KEY1_Pin);
    42. // ---------------------------------------------------------
    43. /* KEY2 */
    44. // 1. 使能时钟
    45. KEY2_Clock();
    46. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    47. KEY_Init(KEY2_GPIO, KEY2_Pin);
    48. // ---------------------------------------------------------
    49. /* KEY3 */
    50. // 1. 使能时钟
    51. KEY3_Clock();
    52. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    53. KEY_Init(KEY3_GPIO, KEY3_Pin);
    54. // ---------------------------------------------------------
    55. /* KEY4 */
    56. // 1. 使能时钟
    57. KEY4_Clock();
    58. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    59. KEY_Init(KEY4_GPIO, KEY4_Pin);
    60. unsigned short KEY1_flag = 0;
    61. unsigned short KEY2_flag = 0;
    62. unsigned short KEY3_flag = 0;
    63. unsigned short KEY4_flag = 0;
    64. while (1) {
    65. // 1:弹起 0:按下
    66. // KEY1控制LED1
    67. // if (KEY_ReadInputData(KEY1_GPIO, KEY1_POS) == 0) {
    68. // // 消抖
    69. // while (KEY_ReadInputData(KEY1_GPIO, KEY1_POS) == 0);
    70. // KEY1_flag = ~KEY1_flag;
    71. // if (KEY1_flag != 0) {
    72. // LED1_ON();
    73. // } else {
    74. // LED1_OFF();
    75. // }
    76. // }
    77. if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0) {
    78. // 消抖
    79. while (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0);
    80. KEY1_flag++;
    81. }
    82. if (KEY1_flag == 2) {
    83. KEY1_flag = 0;
    84. LED1_OFF();
    85. } else if (KEY1_flag == 1) {
    86. LED1_ON();
    87. }
    88. // KEY2控制LED2
    89. if (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin) == 0) {
    90. // 消抖
    91. while (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin) == 0);
    92. KEY2_flag++;
    93. }
    94. if (KEY2_flag == 2) {
    95. KEY2_flag = 0;
    96. LED2_OFF();
    97. } else if (KEY2_flag == 1) {
    98. LED2_ON();
    99. }
    100. // KEY3控制LED3
    101. if (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin) == 0) {
    102. // 消抖
    103. while (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin) == 0);
    104. KEY3_flag++;
    105. }
    106. if (KEY3_flag == 2) {
    107. KEY3_flag = 0;
    108. LED3_OFF();
    109. } else if (KEY3_flag == 1) {
    110. LED3_ON();
    111. }
    112. // KEY4控制LED4
    113. if (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin) == 0) {
    114. // 消抖
    115. while (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin) == 0);
    116. KEY4_flag++;
    117. }
    118. if (KEY4_flag == 2) {
    119. KEY4_flag = 0;
    120. LED4_OFF();
    121. } else if (KEY4_flag == 1) {
    122. LED4_ON();
    123. }
    124. }
    125. }

    (4) 通过按键 KEY1 控制蜂鸣器响或不响(BEEP)

    BSP_beep.h

    1. #ifndef __BSP_BEEP_H__
    2. #define __BSP_BEEP_H__
    3. #include "stm32f4xx.h"
    4. // 哪组寄存器
    5. #define BEEP_GPIO GPIOF
    6. // 哪个引脚
    7. #define BEEP_Pin GPIO_Pin_8
    8. // BEEP需要配置的时钟外设
    9. #define BEEP_RCC_AHB1Periph RCC_AHB1Periph_GPIOF
    10. // 使能时钟
    11. #define BEEP_Clock() RCC_AHB1PeriphClockCmd(BEEP_RCC_AHB1Periph, ENABLE)
    12. // 响
    13. #define BEEP_ON() GPIO_SetBits(BEEP_GPIO, BEEP_Pin)
    14. // 不响
    15. #define BEEP_OFF() GPIO_ResetBits(BEEP_GPIO, BEEP_Pin)
    16. void BEEP_Init(GPIO_TypeDef *BEEP, uint16_t GPIO_Pin_x);
    17. #endif

    BSP_beep.c

    1. #include "BSP_beep.h"
    2. void BEEP_Init(GPIO_TypeDef *BEEP, uint16_t GPIO_Pin_x) {
    3. // 2.初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    4. // 2.1 定义GPIO初始化信息结构体
    5. GPIO_InitTypeDef g;
    6. // 2.2 根据配置需要,对结构体成员赋值
    7. g.GPIO_Pin = GPIO_Pin_x; // 哪个引脚
    8. g.GPIO_Mode = GPIO_Mode_OUT; // 输出模式
    9. g.GPIO_Speed = GPIO_Speed_2MHz; // 2MHz
    10. g.GPIO_OType = GPIO_OType_PP; // ????
    11. g.GPIO_PuPd = GPIO_PuPd_NOPULL; // ???????
    12. // 2.3 根据结构体信息,完成GPIO配置
    13. GPIO_Init(BEEP, &g);
    14. }

    main.c

    1. #include "stm32f4xx.h"
    2. #include "BSP_key.h"
    3. #include "BSP_beep.h"
    4. int main(void) {
    5. /* KEY1 */
    6. // 1. 使能时钟
    7. KEY1_Clock();
    8. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    9. KEY_Init(KEY1_GPIO, KEY1_Pin);
    10. /* BEEP */
    11. // 1. 使能时钟
    12. BEEP_Clock();
    13. // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值)
    14. BEEP_Init(BEEP_GPIO, BEEP_Pin);
    15. // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 不响
    16. BEEP_OFF();
    17. unsigned short BEEP_flag = 0;
    18. while (1) {
    19. // 1:弹起 0:按下
    20. // KEY1控制LED1
    21. if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0) {
    22. // 消抖
    23. while (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0);
    24. BEEP_flag = ~BEEP_flag;
    25. if (BEEP_flag != 0) {
    26. BEEP_ON();
    27. } else {
    28. BEEP_OFF();
    29. }
    30. }
    31. }
    32. }
  • 相关阅读:
    nginx(六十八)http_proxy模块 nginx与上游的ssl握手
    六月集训(第23天) —— 字典树
    长安旗下阿维塔科技增资扩股落定:宁德时代将持股约24%!
    基于Java+SpringBoot+Mybatis+Vue+ElementUi的校园闲置物品交易
    SpringBoot集成支付宝 - 少走弯路就看这篇
    Python for循环
    C++中配置json以及使用
    十三届蓝桥杯B组国赛
    【C++】Linux下如何查看opencv的版本
    Linux 权限
  • 原文地址:https://blog.csdn.net/qq_68915288/article/details/141039463