文章目录
- 一、输出
-
- [ 1、电路原理图:](# 1、电路原理图:)
- [ 2、LED.h头文件](# 2、LED.h头文件)
- [ 3、LED.c文件](# 3、LED.c文件)
- [ 4、Mian调用](# 4、Mian调用)
- [ 5、总结:](# 5、总结:)
-
- [ 1、针对每个功能处理,都建一个新的.h.c文件](# 1、针对每个功能处理,都建一个新的.h.c文件)
- [ 2、GPIO输出使用方法](# 2、GPIO输出使用方法)
- [ 3、要点](# 3、要点)
- [ 4、初始化](# 4、初始化)
- 二、输入
-
- [ 1、电路原理图](# 1、电路原理图)
- [ 2、KEY.h](# 2、KEY.h)
- [ 3、KEY.c](# 3、KEY.c)
- [ 4、mian调用](# 4、mian调用)
- [ 5、总结:](# 5、总结:)
-
- [ 1、要点](# 1、要点)
- [ 2、初始化](# 2、初始化)
- 三、位带输入输出
-
- [ 0、位带](# 0、位带)
- [ 1、BitBand.h](# 1、BitBand.h)
- [ 2、BitBand.c](# 2、BitBand.c)
- [ 3、Mian调用](# 3、Mian调用)
- 四、GPIO函数
一、输出
1、电路原理图:

2、LED.h头文件
c
#ifndef __LED_H
#define __LED_H
/*此处省略头文件的具体内容*/
#include "stm32f10x.h"
//定义引脚别名=========================================
// R-红色
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色
#define LED2_GPIO_PORT GPIOB
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色
#define LED3_GPIO_PORT GPIOB
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED3_GPIO_PIN GPIO_Pin_1
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED3_TOGGLE digitalToggle(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF digitalHi(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON digitalLo(LED2_GPIO_PORT,LED3_GPIO_PIN)
/* 基本混色,后面高级用法使用PWM可混出全彩颜色,且效果更好 */
//红
#define LED_RED \
LED1_ON;\
LED2_OFF\
LED3_OFF
//绿
#define LED_GREEN \
LED1_OFF;\
LED2_ON\
LED3_OFF
//蓝
#define LED_BLUE \
LED1_OFF;\
LED2_OFF\
LED3_ON
//黄(红+绿)
#define LED_YELLOW \
LED1_ON;\
LED2_ON\
LED3_OFF
//紫(红+蓝)
#define LED_PURPLE \
LED1_ON;\
LED2_OFF\
LED3_ON
//青(绿+蓝)
#define LED_CYAN \
LED1_OFF;\
LED2_ON\
LED3_ON
//白(红+绿+蓝)
#define LED_WHITE \
LED1_ON;\
LED2_ON\
LED3_ON
//黑(全部关闭)
#define LED_RGBOFF \
LED1_OFF;\
LED2_OFF\
LED3_OFF
void LED_GPIO_Config(void);
#endif /* end of __LED_H */
3、LED.c文件
c
#include "LED.h"
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
/*调用库函数,初始化GPIOF*/
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}
4、Mian调用
c
#include "stm32f10x.h"
#include "LED.h"
#define SOFT_DELAY Delay(0x5FFFFF);
void Delay(__IO u32 nCount);
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
while (1)
{
/* LED1_ON; // 亮
SOFT_DELAY;
LED1_OFF; // 灭
LED2_ON; // 亮
SOFT_DELAY;
LED2_OFF; // 灭
LED3_ON; // 亮
SOFT_DELAY;
LED3_OFF; // 灭
*/
/*轮流显示 红绿蓝黄紫青白 颜色*/
LED_RED;
SOFT_DELAY;
LED_GREEN;
SOFT_DELAY;
LED_BLUE;
SOFT_DELAY;
LED_YELLOW;
SOFT_DELAY;
LED_PURPLE;
SOFT_DELAY;
LED_CYAN;
SOFT_DELAY;
LED_WHITE;
SOFT_DELAY;
LED_RGBOFF;
SOFT_DELAY;
}
}
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for (; nCount != 0; nCount--);
}
5、总结:
1、针对每个功能处理,都建一个新的.h.c文件
防止重复包含的处理
#ifndef __LED_H
#define __LED_H
/*此处省略头文件的具体内容*/
#endif /* end of __LED_H */
2、GPIO输出使用方法
在.h中 定义要使用引脚定义
GPIO组,GPIO引脚,GPIO所属时钟。
在.c中配置GPIO
RCC_APB2PeriphClockCmd 开启GPIO所属时钟。
使用GPIO_Init 初始化GPIO。
3、要点
使能GPIO端口时钟;
初始化GPIO目标引脚为推挽输出模式;
编写简单测试程序,控制GPIO引脚输出高、低电平。
4、初始化
使用GPIO_InitTypeDef定义GPIO初始化结构体变量,以便下面用于存储GPIO配置。
调用库函数RCC_APB2PeriphClockCmd来使能LED灯的GPIO端口时钟,在前面的章节中我们是直接向RCC寄存器赋值来使能时钟的, 不如这样直观。该函数有两个输入参数,第一个参数用于指示要配置的时钟,如本例中的"RCC_APB2Periph_GPIOB", 应用时我们使用"|"操作同时配置3个LED灯的时钟;函数的第二个参数用于设置状态,可输入"Disable"关闭或"Enable"使能时钟
向GPIO初始化结构体赋值,把引脚初始化成推挽输出模式,其中的GPIO_Pin使用宏"LEDx_GPIO_PIN"来赋值,使函数的实现方便移植。
使用以上初始化结构体的配置,调用GPIO_Init函数向寄存器写入参数,完成GPIO的初始化, 这里的GPIO端口使用"LEDx_GPIO_PORT"宏来赋值,也是为了程序移植方便。
二、输入
1、电路原理图

2、KEY.h
c
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"
// 引脚定义
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13
/** 按键按下标置宏
* 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* 若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可
*/
#define KEY_ON 1
#define KEY_OFF 0
//初始化引脚
void Key_GPIO_Config(void);
//检测引脚状态
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
#endif /* end of __KEY_H */
3、KEY.c
c
#include "KEY.h"
void Key_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*开启按键端口的时钟*/
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
// 设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用结构体初始化按键
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
//选择按键的引脚
GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
//设置按键的引脚为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用结构体初始化按键
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
}
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
/*检测是否有按键按下 */
if (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) {
/*等待按键释放 */
while (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
return KEY_ON;
} else
return KEY_OFF;
}
4、mian调用
c
#include "stm32f10x.h"
#include "LED.h"
#include "KEY.h"
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
/*初始化按键*/
Key_GPIO_Config();
/* 轮询按键状态,若按键按下则反转LED */
while (1)
{
if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {
/*LED1反转*/
LED1_TOGGLE;
}
if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {
/*LED2反转*/
LED2_TOGGLE;
}
}
}
5、总结:
1、要点
使能GPIO端口时钟;
初始化GPIO目标引脚为输入模式(浮空输入);
编写简单测试程序,检测按键的状态,实现按键控制LED灯
2、初始化
使用GPIO_InitTypeDef定义GPIO初始化结构体变量,以便下面用于存储GPIO配置。
调用库函数RCC_APB2PeriphClockCmd来使能按键的GPIO端口时钟,调用时我们使用"|"操作同时配置两个按键的时钟。
向GPIO初始化结构体赋值,把引脚初始化成浮空输入模式,其中的GPIO_Pin使用宏"KEYx_GPIO_PIN"来赋值,使函数的实现方便移植。 由于引脚的默认电平受按键电路影响,所以设置成浮空输入。
使用以上初始化结构体的配置,调用GPIO_Init函数向寄存器写入参数,完成GPIO的初始化, 这里的GPIO端口使用"KEYx_GPIO_PORT"宏来赋值,也是为了程序移植方便。
使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的GPIO引脚。
三、位带输入输出
0、位带
在STM32中,有两个地方实现了位带,一个是SRAM区的最低1MB空间,另一个是外设区最低1MB空间。
这两个1MB的空间除了可以像正常的RAM一样操作外,他们还有自己的位带别名区,位带别名区把这1MB的空间的每一个位膨胀成一个32位的字, 当访问位带别名区的这些字时,就可以达到访问位带区某个比特位的目的。

一个四字节对应一bit位寄存器。
外设位带区:
外设外带区的地址为:0X40000000~0X40100000,大小为1MB,这1MB的大小在103系列大/中/小容量型号的单片机中包含了片上外设的全部寄存器, 这些寄存器的地址为:0X40000000~0X40029FFF。
外设位带区经过膨胀后的位带别名区地址为:0X42000000~0X43FFFFFF, 这个地址仍然在CM3 片上外设的地址空间中。
SRAM位带区
RAM的位带区的地址为:0X2000 0000~X2010 0000,大小为1MB,经过膨胀后的位带别名区地址为:0X2200 0000~0X23FF FFFF, 大小为32MB。操作SRAM的比特位这个用得很少。
位带区和位带别名区地址转换
外设位带别名区转换:
AliasAddr= =0x42000000+ (A-0x40000000)8 4 +n4
RAM位带别名区转换:
AliasAddr= =0x22000000+ (A-0x20000000)84 +n 4
统一公式:
c
// 把"位带地址+位序号"转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
地址转换指针
c
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
1、BitBand.h
c
#ifndef __BITBAND_H
#define __BITBAND_H
/*此处省略头文件的具体内容*/
#include "stm32f10x.h"
//定义引脚别名=========================================
// R-红色
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
// G-绿色
#define LED2_GPIO_PORT GPIOB
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED2_GPIO_PIN GPIO_Pin_0
// B-蓝色
#define LED3_GPIO_PORT GPIOB
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED3_GPIO_PIN GPIO_Pin_1
void LED_GPIO_Config(void);
// 把"位带地址+位序号"转换成别名地址的宏
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))
// 把一个地址转换成一个指针
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
// 把位带别名区地址转换成指针
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
// GPIO ODR 和 IDR 寄存器地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
// 单独操作 GPIO的某一个IO口,n(0,1,2...15),n表示具体是哪一个IO口
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#endif /* end of __BITBAND_H */
2、BitBand.c
c
#include "BitBand.h"
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
/*调用库函数,初始化GPIOF*/
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}
3、Mian调用
c
#include "stm32f10x.h"
#include "BitBand.h"
#include "KEY.h"
#define SOFT_DELAY Delay(0x5FFFFF);
void Delay(__IO u32 nCount);
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
/*初始化按键*/
Key_GPIO_Config();
/* 轮询按键状态,若按键按下则反转LED */
while (1)
{
// PB0 = 0,点亮LED
PBout(0)= 0;
SOFT_DELAY;
// PB1 = 1,熄灭LED
PBout(0)= 1;
SOFT_DELAY;
}
}
void Delay(__IO uint32_t nCount) //简单的延时函数
{
for (; nCount != 0; nCount--);
}
四、GPIO函数
c
//将处设GPIOx寄存器重调为缺省值
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//将复用功能(重映射事件控制和EXTI设置)重调为缺省值
void GPIO_AFIODeInit(void);
//根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//把GPIO_InitStruct中的每一个参数按缺省值填入
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
GPIO_Pin_All
GPIO_Speed_2MHz
GPIO_Mode_IN_FLOATING
//读取指定端口管脚的输入
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取指定的GPIO端口输入
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//读取指定端口管脚的输出
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//读取指定的GPIO的端口输出
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//置位指定的数据端口位
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//清除指定的数据端口位
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//设置或者清除指定的数据端口位
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
//向指定的GPIO数据端口写入数据
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
//锁定GPIO管脚调置寄存器
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//选择GPIO管脚作事件输出
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
GPIO_ProtSource :用以选择用作事件输出的GPIO端口。
GPIO_PinSource:用以选择用作事件输出的GPIO管脚。
//使能或者失能事件输出
void GPIO_EventOutputCmd(FunctionalState NewState);
//改变指定管脚的映射
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
//选择GPIO管脚用作处部中断线路
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);