阅读前提示: 我们使用的板子是正点原子的ALIENTEK精英STM32F103。本文使用固件库进行编程,因为使用固件库编程较为方便,且阅读程序也较易理解。
7组IO口(具体地址可见头文件stm32f10x.h): GPIOA ~ GPIOG。每组IO口可控制7个寄存器,这7个寄存器可控制一组GPIO的16个IO口:
16个管脚(GPIO_Pin)(位于头文件stm32f10x_gpio.h): GPIO_Pin_0 ~ GPIO_Pin_15,若想全部选中则GPIO_Pin_All。
4种输入模式(GPIO_Mode)(位于头文件stm32f10x_gpio.h):
4种输出模式(GPIO_Mode)(位于头文件stm32f10x_gpio.h):
3种最大输出速度(GPIO_Speed)(位于头文件stm32f10x_gpio.h):
简要介绍常用的输出模式:
推挽输出:
可以输出强高低电平,连接数字器件。开漏输出:
只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)
GPIO结构体定义(位于头文件stm32f10x_gpio.h):
typedef struct
{
uint16_t GPIO_Pin; /* 设置初始化管脚 */
GPIOSpeed_TypeDef GPIO_Speed; /* 设置输入或输出速度 */
GPIOMode_TypeDef GPIO_Mode; /* 设置输入或输出模式 */
}GPIO_InitTypeDef;
(1)初始化函数:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
作用:初始化一个或者多个IO口(同一组)的工作方式和速度。该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器。
(2)读取输入电平函数
读取某个GPIO的输入电平(实际操作的是GPIOx_IDR寄存器):
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取某组GPIO的输入电平(实际操作的是GPIOx_IDR寄存器):
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
(3)读取输出电平函数
读取某个GPIO的输出电平(实际操作的是GPIOx_ODR寄存器):
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取某组GPIO的输出电平(实际操作的是GPIOx_ODR寄存器):
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
(4)设置输出电平函数
设置某个IO口输出为高电平(实际操作BSRR寄存器):
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
设置某个IO口输出为低电平(实际操作的BRR寄存器):
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
不常用的两个函数:
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.定义初始化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的电路图如下所示:
从电路图中可知,当输出IO口电平为低电平时,LED灯亮;当输出IO口电平为高电平时,LED灯灭。
从LED电路图可知,LED0接在PB5,LED1接在PE5。选择的输出模式:推挽输出。
程序如下(本程序包含3个源文件,其中delay.h为正点原子资料盘内自带的头文件,亦可使用51单片机常用的简易延时函数,即循环延时法):
/* =====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。
程序如下:
/* =====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轮流工作的效果。前500ms,LED1和BEEP工作,后500ms,仅LED0工作。
为方便程序阅读,我们将设置高低电平的函数封装成宏。程序如下:
/* =====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;
}