阅读前提示: 我们使用的板子是正点原子的ALIENTEK精英STM32F103。本文使用固件库进行编程,因为使用固件库编程较为方便,且阅读程序也较易理解。

零、GPIO简介及其库函数

1. GPIO简介

7组IO口(具体地址可见头文件stm32f10x.h): GPIOA ~ GPIOG。每组IO口可控制7个寄存器,这7个寄存器可控制一组GPIO的16个IO口:

  • GPIOx_CRL:端口配置低寄存器
  • GPIOx_CRH:端口配置高寄存器
  • GPIOx_IDR:端口输入寄存器
  • GPIOx_ODR:端口输出寄存器
  • GPIOx_BSRR:端口位设置/清除寄存器
  • GPIOx_BRR:端口位清除寄存器
  • GPIOx_LCKR:端口配置锁存寄存器

16个管脚(GPIO_Pin)(位于头文件stm32f10x_gpio.h): GPIO_Pin_0 ~ GPIO_Pin_15,若想全部选中则GPIO_Pin_All。

4种输入模式(GPIO_Mode)(位于头文件stm32f10x_gpio.h):

  • 浮空输入(GPIO_Mode_IN_FLOATING)
  • 上拉输入(GPIO_Mode_IPU)
  • 下拉输入(GPIO_Mode_IPD)
  • 模拟输入(GPIO_Mode_AIN)

4种输出模式(GPIO_Mode)(位于头文件stm32f10x_gpio.h):

  • 开漏输出( GPIO_Mode_Out_OD)
  • 复用开漏输出(GPIO_Mode_AF_OD)
  • 推挽输出(GPIO_Mode_Out_PP)
  • 复用推挽输出(GPIO_Mode_AF_PP)

3种最大输出速度(GPIO_Speed)(位于头文件stm32f10x_gpio.h):

  • GPIO_Speed_2MHz
  • GPIO_Speed_10MHz
  • GPIO_Speed_50MHz

简要介绍常用的输出模式:

推挽输出:
可以输出强高低电平,连接数字器件。

开漏输出:
只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)

GPIO结构体定义(位于头文件stm32f10x_gpio.h):

1
2
3
4
5
6
7
8
typedef struct
{
uint16_t GPIO_Pin; /* 设置初始化管脚 */

GPIOSpeed_TypeDef GPIO_Speed; /* 设置输入或输出速度 */

GPIOMode_TypeDef GPIO_Mode; /* 设置输入或输出模式 */
}GPIO_InitTypeDef;

2. 与GPIO有关的几个常用库函数

(1)初始化函数:

1
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

作用:初始化一个或者多个IO口(同一组)的工作方式和速度。该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器。

(2)读取输入电平函数

读取某个GPIO的输入电平(实际操作的是GPIOx_IDR寄存器):

1
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取某组GPIO的输入电平(实际操作的是GPIOx_IDR寄存器):

1
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

(3)读取输出电平函数

读取某个GPIO的输出电平(实际操作的是GPIOx_ODR寄存器):

1
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

读取某组GPIO的输出电平(实际操作的是GPIOx_ODR寄存器):

1
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

(4)设置输出电平函数

设置某个IO口输出为高电平(实际操作BSRR寄存器):

1
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

设置某个IO口输出为低电平(实际操作的BRR寄存器):

1
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

不常用的两个函数:

1
2
3
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
/* 这两个函数不常用,也是用来设置IO口输出电平 */

(5)初始化GPIO口的一般步骤

我们以一段程序为例来说明初始化GPIO口的几个步骤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 1.定义初始化GPIO结构体 */
GPIO_InitTypeDef GPIO_InitStructure;

/* 2.开启GPIO外设的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

/* 3.设置为推挽输出模式 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

/* 4.设置输出速度为50MHz*/
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

/* 5.选择管脚为5 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

/* 6.进行初始化操作 */
GPIO_Init(GPIOB, &GPIO_InitStructure);

一、LED跑马灯的实现

本程序实现了两个LED轮流点亮及熄灭的效果。

有关LED的电路图如下所示:

从电路图中可知,当输出IO口电平为低电平时,LED灯亮;当输出IO口电平为高电平时,LED灯灭。

从LED电路图可知,LED0接在PB5,LED1接在PE5。选择的输出模式:推挽输出。

程序如下(本程序包含3个源文件,其中delay.h为正点原子资料盘内自带的头文件,亦可使用51单片机常用的简易延时函数,即循环延时法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* =====main.c===== */
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"

int main(void)
{
delay_init();
LED_Init();

while(1)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOE, GPIO_Pin_5);
delay_ms(500);

GPIO_SetBits(GPIOB, GPIO_Pin_5);
GPIO_ResetBits(GPIOE, GPIO_Pin_5);
delay_ms(500);
}
}


/* =====led.h===== */
//这样的宏定义是为了防止led.c内的函数被重复声明
#ifndef __LED_H
#define __LED_H

void LED_Init(void);

#endif


/* =====led.c===== */
#include "led.h"
#include "stm32f10x.h"

//当函数参数为空时,写上void是很不错的习惯
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

/* 开启使能IO口时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
//也可以写成:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOB, ENABLE);
// “|”为或运算符

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_5);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE, GPIO_Pin_5);

}

二、蜂鸣器循环响灭

有关蜂鸣器的电路图如下所示:

从电路图中可知,当输出IO口电平为低电平时,三极管处于截止状态,蜂鸣器不响;当输出IO口电平为高电平时,三极管处于放大状态,蜂鸣器响。BEEP接在PB8。

程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* =====main.c===== */
#include "stm32f10x.h"
#include "delay.h"
#include "beep.h"

int main(void)
{
Beep_Init();
delay_init();
while(1)
{
GPIO_SetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
delay_ms(500);
}
}


/* =====beep.h===== */
#ifndef __BEEP_H
#define __BEEP_H

void Beep_Init(void);

#endif


/* =====beep.c===== */
#include "stm32f10x.h"
#include "beep.h"

void Beep_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}

三、蜂鸣器和LED灯循环

本程序实现了蜂鸣器与两个LED轮流工作的效果。前500ms,LED1和BEEP工作,后500ms,仅LED0工作。

为方便程序阅读,我们将设置高低电平的函数封装成宏。程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/* =====main.c===== */
#include "stm32f10x.h"
#include "delay.h"
#include "beep.h"
#include "led.h"

int main(void)
{
LED_Init();
Beep_Init();
delay_init();
while(1)
{
BEEP_ON;
LED0_OFF;
LED1_ON;
delay_ms(500);

BEEP_OFF;
LED0_ON;
LED1_OFF;
delay_ms(500);
}
}


/* =====led.h===== */
#ifndef __LED_H
#define __LED_H

#define LED0_OFF GPIO_SetBits(GPIOB, GPIO_Pin_5)
#define LED0_ON GPIO_ResetBits(GPIOB, GPIO_Pin_5)
#define LED1_OFF GPIO_SetBits(GPIOE, GPIO_Pin_5)
#define LED1_ON GPIO_ResetBits(GPIOE, GPIO_Pin_5)

void LED_Init(void);

#endif


/* =====led.c===== */
#include "stm32f10x.h"
#include "led.h"

void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_Init(GPIOE, &GPIO_InitStructure);

LED0_OFF;
LED1_OFF;
}


/* =====beep.h===== */
#ifndef __BEEP_H
#define __BEEP_H

#define BEEP_ON GPIO_SetBits(GPIOB, GPIO_Pin_8)
#define BEEP_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)

void Beep_Init(void);

#endif



/* =====beep.c===== */
#include "stm32f10x.h"
#include "beep.h"

void Beep_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOB, &GPIO_InitStructure);
BEEP_OFF;
}