閱讀前提示: 我們使用的板子是正點原子的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):
typedef struct
{
uint16_t GPIO_Pin; /* 設置初始化管腳 */
GPIOSpeed_TypeDef GPIO_Speed; /* 設置輸入或輸出速度 */
GPIOMode_TypeDef GPIO_Mode; /* 設置輸入或輸出模式 */
}GPIO_InitTypeDef;
2. 與GPIO有關的幾個常用庫函數
(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輪流點亮及熄滅的效果。
有關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燈循環
本程序實現了蜂鳴器與兩個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;
}