在单片机产品中,我们常常可以见到三种模块: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上面写程序去控制这些引脚
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)
每组GPIO地址分配如下: GPIOA 0x4002 0000 ~ 0x4002 03ff GPIOB 0x4002 0400 ~ 0X4002 07FF GPIOC 0x4002 0800 ~ 0x4002 0BFF GPIOD 0x4002 0C00 ~ 0x4002 0FFF GPIOE 0x4002 1000 ~ 0x4002 13FF GPIOF 0x4002 1400 ~ 0x4002 17FF GPIOG 0x4002 1800 ~ 0x4002 1BFF GPIOH 0x4002 1C00 ~ 0x4002 1FFF GPIOI 0x4002 2000 ~ 0x4002 23FF
每个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 modeCPU可以往该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的寄存器来控制的
每个通用 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_PUPDRpu: 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_IDRInput 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_BSRRBSR: 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 0000AF1 0001
AF2 0010
AF15 1111
AFx到底是复用何种功能,得看芯片手册
注意: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
stm32固件库移植 -----> 具体移植操作参考文档
5.1 配置AHB1总线上的外设时钟 RCC_AHB1PeriphClockCmd
// 等同于RCC_AHB1ENR寄存器 void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState) @RCC_AHB1Periph:指定AHB1总线外设,也就是要配置时钟的外设 可以是以下任意一个宏: RCC_AHB1Periph_GPIOA RCC_AHB1Periph_GPIOB ... RCC_AHB1Periph_GPIOI @NewState:指定该外设的时钟状态 ENBALE 使能,为该外设提供时钟信号 DISBALE 禁止,不提供 ------------------------------------------------------------------ 比如:使能GPIOF时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);5.2 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) GPIO_init
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct) @GPIOx:指定要初始化的GPIO分组 GPIOA GPIOB ... GPIOI @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; @GPIO_Pin:指定要配置的GPIO引脚(可以位或多个,表示配置同一组的多个引脚为相 同模式,如:GPIO_Pin_0 | GPIO_Pin_1) GPIO_Pin_0 GPIO_Pin_1 ... GPIO_Pin_15 @GPIO_Mode:指定要配置的GPIO引脚的功能模式 GPIO_Mode_IN 输入模式 GPIO_Mode_OUT 输出模式 GPIO_Mode_AF 复用功能模式 GPIO_Mode_AN 模拟模式 @GPIO_Speed:指定引脚速率 GPIO_Speed_2MHz 2M低速 GPIO_Speed_25MHz 25M中速 GPIO_Speed_50MHz 50M快速 GPIO_Speed_100MHz 100M高速 @GPIO_OType:指定输出类型 GPIO_OType_PP 输出推挽 GPIO_OType_OD 输出开漏 @GPIO_PuPd:指定上下拉选择 GPIO_PuPd_NOPULL 无上拉,也无下拉 GPIO_PuPd_UP 上拉 GPIO_PuPd_DOWN 下拉 --------------------------------------------------------------- 比如:配置PF9为带下拉的推挽输出模式 // 定义GPIO初始化信息结构体 GPIO_InitTypeDef g; // 根据配置需要,对结构体成员赋值 g.GPIO_Pin = GPIO_Pin_9; // 9号引脚 g.GPIO_Mode = GPIO_Mode_OUT; // 输出模式 g.GPIO_Speed = GPIO_Speed_50MHz; // 50MHz g.GPIO_OType = GPIO_OType_PP; // 输出推挽 g.GPIO_PuPd = GPIO_PuPd_DOWN; // 下拉 // 根据结构体信息,完成GPIO配置 GPIO_Init(GPIOF, &g);5.3 获取指定GPIO分组输入数据寄存器中的值,并通过返回值返回 GPIO_ReadInputData
uint16_t GPIO_ReadInputData(GPIO_TypeDef *GPIOx) @GPIOx:指定GPIO分组 GPIOA GPIOB ... GPIOI 返回值: 返回获取到的GPIO分组的输入数据寄存器中数据 要注意的是该返回值是uint16_t类型,具有16bits,分别对应该GPIO分组的16个GPIO引脚的输入 电平状态 bit0 --> 0号引脚 ... bit15 --> 15号引脚5.4 获取指定GPIO引脚的输入值 GPIO_ReadInputDataBit
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) @GPIOx:指定GPIO分组 GPIOA GPIOB ... GPIOI @GPIO_Pin:指定GPIO引脚 GPIO_Pin_0 ... GPIO_Pin_15 返回值: 返回指定GPIO引脚的电平状态(1个引脚的状态) Bit_SET 表示该GPIO引脚输入为高电平 Bit_RESET 表示该GPIO引脚输入为低电平5.5 GPIO_SetBits / GPIO_ResetBits
GPIO_SetBits:用来将指定的GPIO引脚输出为高电平(GPIO_BSRR) void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) ----------------------------------------------------------------- GPIO_ResetBits用来将指定的GPIO引脚输出低电平(GPIO_BSRR) void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
BSP_led.h
#ifndef __BSP_LED_H__ #define __BSP_LED_H__ #include "stm32f4xx.h" // 哪组寄存器 #define LED1_GPIO GPIOF #define LED2_GPIO GPIOF #define LED3_GPIO GPIOE #define LED4_GPIO GPIOE // 哪个引脚 #define LED1_Pin GPIO_Pin_9 #define LED2_Pin GPIO_Pin_10 #define LED3_Pin GPIO_Pin_13 #define LED4_Pin GPIO_Pin_14 // LED灯需要配置的时钟外设 #define LED1_RCC_AHB1Periph RCC_AHB1Periph_GPIOF #define LED2_RCC_AHB1Periph RCC_AHB1Periph_GPIOF #define LED3_RCC_AHB1Periph RCC_AHB1Periph_GPIOE #define LED4_RCC_AHB1Periph RCC_AHB1Periph_GPIOE // 使能时钟 #define LED1_Clock() RCC_AHB1PeriphClockCmd(LED1_RCC_AHB1Periph, ENABLE) #define LED2_Clock() RCC_AHB1PeriphClockCmd(LED2_RCC_AHB1Periph, ENABLE) #define LED3_Clock() RCC_AHB1PeriphClockCmd(LED3_RCC_AHB1Periph, ENABLE) #define LED4_Clock() RCC_AHB1PeriphClockCmd(LED4_RCC_AHB1Periph, ENABLE) // 开 #define LED1_ON() GPIO_ResetBits(LED1_GPIO, LED1_Pin) // 关 #define LED1_OFF() GPIO_SetBits(LED1_GPIO, LED1_Pin) #define LED2_ON() GPIO_ResetBits(LED2_GPIO, LED2_Pin) #define LED2_OFF() GPIO_SetBits(LED2_GPIO, LED2_Pin) #define LED3_ON() GPIO_ResetBits(LED3_GPIO, LED3_Pin) #define LED3_OFF() GPIO_SetBits(LED3_GPIO, LED3_Pin) #define LED4_ON() GPIO_ResetBits(LED4_GPIO, LED4_Pin) #define LED4_OFF() GPIO_SetBits(LED4_GPIO, LED4_Pin) void LED_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x); #endifBSP_led.c
#include "BSP_led.h" void LED_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x) { // 2.初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) // 2.1 定义GPIO初始化信息结构体 GPIO_InitTypeDef g; // 2.2 根据配置需要,对结构体成员赋值 g.GPIO_Pin = GPIO_Pin_x; // ???? g.GPIO_Mode = GPIO_Mode_OUT; // ???? g.GPIO_Speed = GPIO_Speed_2MHz; // 2MHz g.GPIO_OType = GPIO_OType_PP; // ???? g.GPIO_PuPd = GPIO_PuPd_NOPULL; // ??????? // 2.3 根据结构体信息,完成GPIO配置 GPIO_Init(LEDx, &g); }(1) 点亮LED灯
#include "led.h" int main() { /* 点亮LED1灯 */ // 1. 使能时钟 LED1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED1_GPIO, LED1_Pin); // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开 LED1_ON(); // -------------------------------------------------------- /* 点亮LED2灯 */ // 1. 使能时钟 LED2_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED2_GPIO, LED2_Pin); // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开 LED2_ON(); // --------------------------------------------------------- /* 点亮LED3灯 */ // 1. 使能时钟 LED3_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED3_GPIO, LED3_Pin); // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开 LED3_ON(); // --------------------------------------------------------- /* 点亮LED4灯 */ // 1. 使能时钟 LED4_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED4_GPIO, LED4_Pin); // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 开 LED4_ON(); while (1); return 0; }(2) 一个按键控制一个灯,按下就亮,松开就灭
BSP_key.h
#ifndef __BSP_KEY_H__ #define __BSP_KEY_H__ #include "stm32f4xx.h" /* key1:PA0 key2:PE2 key3:PE3 key3:PE4 */ // 哪组寄存器 #define KEY1_GPIO GPIOA #define KEY2_GPIO GPIOE #define KEY3_GPIO GPIOE #define KEY4_GPIO GPIOE // 哪个引脚 #define KEY1_Pin GPIO_Pin_0 #define KEY2_Pin GPIO_Pin_2 #define KEY3_Pin GPIO_Pin_3 #define KEY4_Pin GPIO_Pin_4 // KEY按键需要配置的时钟外设 #define KEY1_RCC_AHB1Periph RCC_AHB1Periph_GPIOA #define KEY2_RCC_AHB1Periph RCC_AHB1Periph_GPIOE #define KEY3_RCC_AHB1Periph RCC_AHB1Periph_GPIOE #define KEY4_RCC_AHB1Periph RCC_AHB1Periph_GPIOE // 使能时钟 #define KEY1_Clock() RCC_AHB1PeriphClockCmd(KEY1_RCC_AHB1Periph, ENABLE) #define KEY2_Clock() RCC_AHB1PeriphClockCmd(KEY2_RCC_AHB1Periph, ENABLE) #define KEY3_Clock() RCC_AHB1PeriphClockCmd(KEY3_RCC_AHB1Periph, ENABLE) #define KEY4_Clock() RCC_AHB1PeriphClockCmd(KEY4_RCC_AHB1Periph, ENABLE) void KEY_Init(GPIO_TypeDef *KEYx, uint16_t GPIO_Pin_x); /* 高电平(1) ---> 弹起 低电平(0) ---> 按下 */ int KEY_ReadInputData(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin_x); #endifBSP_key.c
#include "BSP_key.h" void KEY_Init(GPIO_TypeDef *LEDx, uint16_t GPIO_Pin_x) { // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) // 2.1 定义GPIO初始化信息结构体 GPIO_InitTypeDef g; // 2.2 根据配置需要,对结构体成员赋值 g.GPIO_Pin = GPIO_Pin_x; // 几号引脚 g.GPIO_Mode = GPIO_Mode_IN; // 输入模式 g.GPIO_PuPd = GPIO_PuPd_NOPULL; // 无上拉也无下拉 // 2.3 根据结构体信息,完成GPIO配置 GPIO_Init(LEDx, &g); } /* 高电平(1) ---> 弹起 低电平(0) ---> 按下 */ int KEY_ReadInputData(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin_x) { if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_x) == Bit_SET) { return 1; } return 0; }
#include "stm32f4xx.h" #include "BSP_led.h" #include "BSP_key.h" int main(void) { /* LED1灯 */ // 1. 使能时钟 LED1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED1_GPIO, LED1_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED1_OFF(); // -------------------------------------------------------- /* LED2灯 */ // 1. 使能时钟 LED2_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED2_GPIO, LED2_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED2_OFF(); // --------------------------------------------------------- /* LED3灯 */ // 1. 使能时钟 LED3_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED3_GPIO, LED3_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED3_OFF(); // --------------------------------------------------------- /* LED4灯 */ // 1. 使能时钟 LED4_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED4_GPIO, LED4_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED4_OFF(); // --------------------------------------------------------- /* KEY1 */ // 1. 使能时钟 KEY1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY1_GPIO, KEY1_Pin); // --------------------------------------------------------- /* KEY2 */ // 1. 使能时钟 KEY2_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY2_GPIO, KEY2_Pin); // --------------------------------------------------------- /* KEY3 */ // 1. 使能时钟 KEY3_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY3_GPIO, KEY3_Pin); // --------------------------------------------------------- /* KEY4 */ // 1. 使能时钟 KEY4_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY4_GPIO, KEY4_Pin); while (1) { // 1:弹起 0:按下 // KEY1控制LED1 if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin)) { // 弹起 LED1_OFF(); } else { // 按下 LED1_ON(); } // KEY2控制LED2 if (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin)) { // 弹起 LED2_OFF(); } else { // 按下 LED2_ON(); } // KEY3控制LED3 if (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin)) { // 弹起 LED3_OFF(); } else { // 按下 LED3_ON(); } // KEY4控制LED4 if (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin)) { // 弹起 LED4_OFF(); } else { // 按下 LED4_ON(); } } }(3) 一个按键控制一个灯, 按一下就亮,再按一下就灭
#include "stm32f4xx.h" #include "BSP_led.h" #include "BSP_key.h" int main(void) { /* LED1灯 */ // 1. 使能时钟 LED1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED1_GPIO, LED1_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED1_OFF(); // -------------------------------------------------------- /* LED2灯 */ // 1. 使能时钟 LED2_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED2_GPIO, LED2_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED2_OFF(); // --------------------------------------------------------- /* LED3灯 */ // 1. 使能时钟 LED3_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED3_GPIO, LED3_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED3_OFF(); // --------------------------------------------------------- /* LED4灯 */ // 1. 使能时钟 LED4_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) LED_Init(LED4_GPIO, LED4_Pin); // 3. 将指定的GPIO引脚输出高电平(GPIO_BSRR) 关 LED4_OFF(); // --------------------------------------------------------- /* KEY1 */ // 1. 使能时钟 KEY1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY1_GPIO, KEY1_Pin); // --------------------------------------------------------- /* KEY2 */ // 1. 使能时钟 KEY2_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY2_GPIO, KEY2_Pin); // --------------------------------------------------------- /* KEY3 */ // 1. 使能时钟 KEY3_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY3_GPIO, KEY3_Pin); // --------------------------------------------------------- /* KEY4 */ // 1. 使能时钟 KEY4_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY4_GPIO, KEY4_Pin); unsigned short KEY1_flag = 0; unsigned short KEY2_flag = 0; unsigned short KEY3_flag = 0; unsigned short KEY4_flag = 0; while (1) { // 1:弹起 0:按下 // KEY1控制LED1 // if (KEY_ReadInputData(KEY1_GPIO, KEY1_POS) == 0) { // // 消抖 // while (KEY_ReadInputData(KEY1_GPIO, KEY1_POS) == 0); // KEY1_flag = ~KEY1_flag; // if (KEY1_flag != 0) { // LED1_ON(); // } else { // LED1_OFF(); // } // } if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0) { // 消抖 while (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0); KEY1_flag++; } if (KEY1_flag == 2) { KEY1_flag = 0; LED1_OFF(); } else if (KEY1_flag == 1) { LED1_ON(); } // KEY2控制LED2 if (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin) == 0) { // 消抖 while (KEY_ReadInputData(KEY2_GPIO, KEY2_Pin) == 0); KEY2_flag++; } if (KEY2_flag == 2) { KEY2_flag = 0; LED2_OFF(); } else if (KEY2_flag == 1) { LED2_ON(); } // KEY3控制LED3 if (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin) == 0) { // 消抖 while (KEY_ReadInputData(KEY3_GPIO, KEY3_Pin) == 0); KEY3_flag++; } if (KEY3_flag == 2) { KEY3_flag = 0; LED3_OFF(); } else if (KEY3_flag == 1) { LED3_ON(); } // KEY4控制LED4 if (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin) == 0) { // 消抖 while (KEY_ReadInputData(KEY4_GPIO, KEY4_Pin) == 0); KEY4_flag++; } if (KEY4_flag == 2) { KEY4_flag = 0; LED4_OFF(); } else if (KEY4_flag == 1) { LED4_ON(); } } }(4) 通过按键 KEY1 控制蜂鸣器响或不响(BEEP)
BSP_beep.h
#ifndef __BSP_BEEP_H__ #define __BSP_BEEP_H__ #include "stm32f4xx.h" // 哪组寄存器 #define BEEP_GPIO GPIOF // 哪个引脚 #define BEEP_Pin GPIO_Pin_8 // BEEP需要配置的时钟外设 #define BEEP_RCC_AHB1Periph RCC_AHB1Periph_GPIOF // 使能时钟 #define BEEP_Clock() RCC_AHB1PeriphClockCmd(BEEP_RCC_AHB1Periph, ENABLE) // 响 #define BEEP_ON() GPIO_SetBits(BEEP_GPIO, BEEP_Pin) // 不响 #define BEEP_OFF() GPIO_ResetBits(BEEP_GPIO, BEEP_Pin) void BEEP_Init(GPIO_TypeDef *BEEP, uint16_t GPIO_Pin_x); #endifBSP_beep.c
#include "BSP_beep.h" void BEEP_Init(GPIO_TypeDef *BEEP, uint16_t GPIO_Pin_x) { // 2.初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) // 2.1 定义GPIO初始化信息结构体 GPIO_InitTypeDef g; // 2.2 根据配置需要,对结构体成员赋值 g.GPIO_Pin = GPIO_Pin_x; // 哪个引脚 g.GPIO_Mode = GPIO_Mode_OUT; // 输出模式 g.GPIO_Speed = GPIO_Speed_2MHz; // 2MHz g.GPIO_OType = GPIO_OType_PP; // ???? g.GPIO_PuPd = GPIO_PuPd_NOPULL; // ??????? // 2.3 根据结构体信息,完成GPIO配置 GPIO_Init(BEEP, &g); }main.c
#include "stm32f4xx.h" #include "BSP_key.h" #include "BSP_beep.h" int main(void) { /* KEY1 */ // 1. 使能时钟 KEY1_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) KEY_Init(KEY1_GPIO, KEY1_Pin); /* BEEP */ // 1. 使能时钟 BEEP_Clock(); // 2. 初始化配置GPIO(完成GPIO的4个配置寄存器的赋值) BEEP_Init(BEEP_GPIO, BEEP_Pin); // 3. 将指定的GPIO引脚输出低电平(GPIO_BSRR) 不响 BEEP_OFF(); unsigned short BEEP_flag = 0; while (1) { // 1:弹起 0:按下 // KEY1控制LED1 if (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0) { // 消抖 while (KEY_ReadInputData(KEY1_GPIO, KEY1_Pin) == 0); BEEP_flag = ~BEEP_flag; if (BEEP_flag != 0) { BEEP_ON(); } else { BEEP_OFF(); } } } }