GPIO功能描述
STM32F103ZET6共有112个IO可用于GPIO(部分IO也可用作其他功能,如USART、IIC等),每16个作为一组,共分为7组,分别记做GPIOA、GPIOB…GPIOG,每组中的各个GPIO分别记做Px0~Px15,如对于GPIOA,对应PA0、PA1…PA15,把每组GPIO的Px0-Px7称作GPIO的低8位端口,Px8-Px15称作高8位端口。
每一组GPIO由一套共7个寄存器控制,分别是:
- GPIOx_CRL:配置(低8位端口)
- GPIOx_CRH:配置(高8位端口)
- GPIOx_IDR:输入数据
- GPIOx_ODR:输出数据或输入上下拉选择
- GPIOx_BSRR:置位/复位(高16bit复位,低16bit置位)(复位即清零)
- GPIOx_BRR:复位
- GPIOx_LCKR:锁定
详细的寄存器地址映像如下图:

其中CRL和CRH寄存器中的CNF[1:0]和MODE[1:0]对应的意义如下表:

复位期间和刚复位后,复用功能未开启,IO端口被配置成浮空输入模式。
所有端口都有外部中断功能。
在输入模式下由ODR寄存器控制上拉还是下拉。
复用功能:使用复用功能前,必须先配置CRL/CRH寄存器(STM32参考手册8.1.11节对各个外设的IO配置有详细说明)。
如果把端口配置成复用输出功能,则引脚和输出寄存器断开,并和片上外设的输出信号连接。
如果软件把一个GPIO脚配置成复用输出功能,但是外设没有被激活,它的输出将不确定。
IO复用功能重映射
同一个外设可以被映射到不同的端口上去,比如USART1的TX/RX引脚,即可以映射到PA9、PA10,也可以映射到PB6、PB7。此功能通过AFIO寄存器组来配置,可以重映射的接口包括:
- LSE振动器引脚OSC32_IN/OSC32_OUT
- 外部振荡器引脚OSC_IN/OSC_OUT
- CAN1、CAN2
- JTAG/SWD
- ADC1、ADC2的外部触发注入转换、外部触发规则转换引脚
- TIM5_CH1-CH4、TIM4_CH1-CH4、TIM2_CH1~CH4、TIM1_…
- USART3、USART2、USART1
- I2C1
- SPI1、SPI3
AFIO寄存器如下图:

配置注意事项:
- 配置STM32任何外设的时候,都要先使能该外设的时钟。
- 可使用位带操作单独控制某个bit
- STM32F1单个IO最大可以提供25mA电流。LED驱动电流在10~50mA,电流越大寿命越短,使用推挽输出;板上蜂鸣器驱动电流30mA,使用推挽输出,三极管扩流。
- IO口作为输入时,如果外部没有上下拉电阻,则需要在STM32F1内部设置上下拉
实战
我所使用的开发板上有2个LED,3个按键分别是:
- LED0接PB5,低电平亮
- LED1接PE5,低电平亮
- KEY_UP接PA0,按下高电平(可用作唤醒功能)
- KEY_0接PE4,按下低电平
- KEY_1接PE3,按下低电平
- KEY_2接PE2,按下低电平
计划实现的功能(功能之间互斥):
- LED闪烁
LED0每隔500ms翻转一次
- 按键控制LED0和LED1
按下并松开KEY_0,翻转LED0
按下并松开KEY_1,翻转LED1
按下并松开KEY_2,翻转LED0和LED1
- 按下KEY_UP, LED0和LED1亮,松开灭
代码实现:
Cortex-M系列的位带功能特别适合这种需要操作单个bit位的场景,所以本示例中的IO口输出和检测高低电平都用位带操作实现,位带操作的详细介绍参见《Cortex-M3权威指南》的5.5节。
共有两个位带区,0x20000000和0x40000000起始的1M字节范围,分别位于SARM和外设寄存器区,我们要操作的GPIO位于第二个位带区,但是我们可以把接口做成兼容二者。
代码中会用到我们上一节中写的延时函数。
把LED和key的代码合在一起,放在led_key.c文件中
led_key.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #ifndef LED_KEY_H #define LED_KEY_H #include "common.h"
#define LED0 PBout(5) #define LED1 PEout(5)
#define KEY_PRESSED 0 #define KEY_RELEASE 1
#define KEY_UP 0 #define KEY_0 1 #define KEY_1 2 #define KEY_2 3
void led_key_init(void); int KeyScan(u32 key); #endif
|
led_key.c
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
| #include "led_key.h" #include "common.h" #include "delay.h"
void led_key_init(void) { SETUP_BIT(RCC->APB2ENR, 2); SETUP_BIT(RCC->APB2ENR, 3); SETUP_BIT(RCC->APB2ENR, 6);
SET_BITS(GPIOB->CRL, 21, 20, 1); SET_BITS(GPIOB->CRL, 23, 22, 0); SET_BITS(GPIOE->CRL, 21, 20, 1); SET_BITS(GPIOE->CRL, 23, 22, 0);
LED0 = 0; LED1 = 0;
SET_BITS(GPIOA->CRL, 1, 0, 0); SET_BITS(GPIOA->CRL, 3, 2, 0x2); CLR_BIT(GPIOA->ODR, 0); SET_BITS(GPIOE->CRL, 17, 16, 0); SET_BITS(GPIOE->CRL, 19, 18, 0x2); SETUP_BIT(GPIOE->ODR, 4); SET_BITS(GPIOE->CRL, 13, 12, 0); SET_BITS(GPIOE->CRL, 15, 14, 0x2); SETUP_BIT(GPIOE->ODR, 3); SET_BITS(GPIOE->CRL, 9, 8, 0); SET_BITS(GPIOE->CRL, 11, 10, 0x2); SETUP_BIT(GPIOE->ODR, 2); }
int CheckKeyPressed(u32 key) { switch (key) { case KEY_UP: return PAin(0)?KEY_PRESSED:KEY_RELEASE; case KEY_0: return PEin(4)?KEY_RELEASE:KEY_PRESSED; case KEY_1: return PEin(3)?KEY_RELEASE:KEY_PRESSED; case KEY_2: return PEin(2)?KEY_RELEASE:KEY_PRESSED; } return KEY_RELEASE; }
int KeyScan(u32 key) { if (CheckKeyPressed(key) == KEY_PRESSED) { delay_ms(10); if (CheckKeyPressed(key) == KEY_PRESSED) { return KEY_PRESSED; } }
return KEY_RELEASE; }
|
main()函数中3个功能每次只能开启一个,剩下的两个要屏蔽掉
main.c
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
| #include "common.h" #include "led_key.h" #include "delay.h" #include "sys.h"
int main(void) { int key0_pressed = 0; int key1_pressed = 0; int key2_pressed = 0; Stm32_Clock_Init(); delay_init(72); led_key_init();
while (1) { #if 0 LED0 = ~LED0; delay_ms(500); #endif
#if 0 if (KeyScan(KEY_0) == KEY_PRESSED) { key0_pressed = 1; } else if (key0_pressed){ key0_pressed = 0; LED0 = ~LED0; }
if (KeyScan(KEY_1) == KEY_PRESSED) { key1_pressed = 1; } else if (key1_pressed){ key1_pressed = 0; LED1 = ~LED1; }
if (KeyScan(KEY_2) == KEY_PRESSED) { key2_pressed = 1; } else if (key2_pressed){ key2_pressed = 0; LED0 = ~LED0; LED1 = ~LED1; } #endif
#if 1 if (KeyScan(KEY_UP) == KEY_PRESSED) { LED0 = 0; LED1 = 0; } else { LED0 = 1; LED1 = 1; } #endif
delay_ms(10); } }
|