一.寄存器简介
STM32 的 IO 口可以由软件配置成如下 8 种模式:
1)、输入浮空
2)、输入上拉
3)、输入下拉
4)、模拟输入
5)、开漏输出
6)、推挽输出
7)、推挽式复用功能
8)、开漏复用功能
每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。STM32 的很多 IO 口都
是 5V 兼容的。
STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:
GPIOx_CRL : 端口配置低寄存器 ,0-7
GPIOx_CRH :端口配置高寄存器, 8-15
GPIOx_IDR : 端口输入寄存器
GPIOx_ODR :端口输出寄存器
GPIOx_BSRR :端口位设置/清除寄存器
GPIOx_BRR :端口清除寄存器
GPIOx_LCKR :端口清配置锁存寄存器
STM32 的 IO 口位配置表如表所示:
STM32 输出模式配置如表 所示:
端口低配置(0-7)寄存器 CRL 的描述,如图所示:
该寄存器的复位值为 0X4444 4444,从图 6.1.1 可以看到,复位值其实就是配置端口为浮空
输入模式。从上图还可以得出:STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。
每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几
个常用的配置,比如 0X0 表示模拟输入模式(ADC 用)、0X3 表示推挽输出模式(做输出口用,
50M 速率)、0X8 表示上/下拉输入模式(做输入口用)、0XB 表示复用输出(使用 IO 口的第二
功能,50M 速率)。
端口高配置(8-15)寄存器 CRH
CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8
位输出口。这里我们对 CRH 就不做详细介绍了。
例:比如我们要设置 PORTC 的 11 位为上拉输入(1000),12 位为推挽输出50MHz(0011)。代码如下:
先清除该位的数据、后设置状态
GPIOC->CRH&=0XFFF00FFF; //清掉这 2 个位原来的设置,同时也不影响其他位的设置
GPIOC->CRH|=0X00038000; //PC11 输入,PC12 输出
GPIOC->ODR=1<<11; //PC11 上拉
通过这 3 句话的配置,我们就设置了 PC11 为上拉输入,PC12 为推挽输出。
IDR 是一个端口输入数据寄存器,只用了低 16 位。该寄存器为只读寄存器,并且只能以
16 位的形式读出。该寄存器各位的描述如图 6.1.2 所示:
要想知道某个 IO 口的状态,你只要读这个寄存器,再看某个位的状态就可以了。使用起
来是比较简单的。
ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读
出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口
的输出电平。该寄存器的各位描述如图 6.1.3 所示:
通过 BSRR 和 BRR 寄存器设置 GPIO 端口输出
BSRR
该寄存器通过举例子可以很清楚了解它的使用方法。
例:设置 GPIOA 的第 1 个端口值为 1,那么只需要往寄存器 BSRR 的低 16 位对应位写 1 即可:
GPIOA->BSRR=1<<1;
设置 GPIOA 的第 1 个端口值为 0,只需要往寄存器高 16 位对应为写 1 即可:
GPIOA->BSRR=1<<(16+1)
该寄存器往相应位写 0 是无影响的,所以我们要设置某些位,我们不用管其他位的值。
BRR
BRR 寄存器是端口位清除寄存器。该寄存器的作用跟 BSRR 的高 16 位雷同,这里就不做
详细讲解。
二.跑马灯寄存器实现
1.硬件设计
引脚: LED0 ---GPIOB_5 LED1 ---GPIOE_5
GPIO输出方式: 推挽输出
2.软件设计
1)使能寄存器(RCC_APB2ENR)
在STM32开发手册中找到APB2 外设时钟使能寄存器(RCC_APB2ENR)
要使能GPIOE_5和GPIOB_5
2)工程搭建
工程搭建与库函数版本相同,这里就不多赘述。
需要新搭建:HARDWARE--->LED--->led.c和led.h文件
led.h代码如下
#ifndef __LED_H
#define __LED_H
void LED_Init(void); //LED初始化函数
#endif
代码分析:这段代码包括了LED初始化函数 void LED_Init(void);
led.c代码如下
#include "led.h"
#include "stm32f10x.h"
void LED_Init(){
//使能 GPIOB,GPIOE 端口时钟
RCC->APB2ENR|=1<<3; //|=1<<3 第3位变成1,其他位不变 使能GPIOB
RCC->APB2ENR|=1<<6; //|=1<<6 第6位变成1,其他位不变 使能GPIOE
//GPIOB.5
GPIOB->CRL&=0xFF0FFFFF; //&=0xFF0FFFFF 第5位清零,其余位保持不变
GPIOB->CRL|=0x00300000; //|=0x00300000 第5位赋值成3,即0011,第5位为推挽输出模式,最大速度50MHz; 其余位置不变
GPIOB->ODR|=1<<5; //|=1<<5 控制第5位输出高电平,即灯灭 ,其余不变
//GPIOE.5
GPIOE->CRL&=0xFF0FFFFF; //&=0xFF0FFFFF 第5位清零,其余位保持不变
GPIOE->CRL|=0x00300000; //|=0x00300000 第5位赋值成3,即0011,第5位为推挽输出模式,最大速度50MHz; 其余位置不变
GPIOE->ODR|=1<<5; //|=1<<5 控制第5位输出高电平,即灯灭 ,其余不变
}
代码分析:
①万能头文件和led头文件
#include "led.h"
#include "stm32f10x.h"
②使能GPIOB和GPIOE
//使能 GPIOB,GPIOE 端口时钟
RCC->APB2ENR|=1<<3; //|=1<<3 第3位变成1,其他位不变 使能GPIOB
RCC->APB2ENR|=1<<6; //|=1<<6 第6位变成1,其他位不变 使能GPIOE
分析:a.需要使能寄存器(RCC_APB2ENR);
b.其中GPIOB是位3,GPIOE是位6;
c. |= 或逻辑 ,|=1<<3 第3位变成1,其他位不变 使能GPIOB,GPIOE同理。
③设置GPIOB_5(推挽输出50MHz)
//GPIOB.5
GPIOB->CRL&=0xFF0FFFFF; //&=0xFF0FFFFF 第5位清零,其余位保持不变
GPIOB->CRL|=0x00300000; //|=0x00300000 第5位赋值成3,即0011,第5位为推挽输出模式,最大速度50MHz; 其余位置不变
GPIOB->ODR|=1<<5; //|=1<<5 控制第5位输出高电平,即灯灭 ,其余不变
分析:a.需要GPIOB的位5 ,为低端口(0-7),则需要用端口低配置(0-7)寄存器 CRL
b.配置时需要先将该位清零,其余保持不变 ; GPIOB->CRL&=0xFF0FFFFF;
c.然后赋值 GPIOB->CRL|=0x00300000; 第5位为推挽输出模式(00),速度50MHz( 11),则第5位为0011,赋值成3。其余位保持不变。
d.最后将该位设置为高电,平即灯灭 ,其余不变。则需要用 ODR 端口输出数据寄存 器;操作为:GPIOB->ODR|=1<<5;
④设置GPIOE_5(推挽输出50MHz)
//GPIOE.5
GPIOE->CRL&=0xFF0FFFFF; //&=0xFF0FFFFF 第5位清零,其余位保持不变
GPIOE->CRL|=0x00300000; //|=0x00300000 第5位赋值成3,即0011,第5位为推挽输出模式,最大速度50MHz; 其余位置不变
GPIOE->ODR|=1<<5; //|=1<<5 控制第5位输出高电平,即灯灭 ,其余不变
与设置GPIOB_5(推挽输出50MHz)原理相同。
main.c代码如下
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
int main(void)
{
//初始化延迟函数
delay_init();
//初始化GPIO口
LED_Init();
while(1){
GPIOB->ODR|=1<<5; //控制第5位输出高电平,即灯灭 ,其余不变
GPIOE->ODR|=1<<5;
delay_ms(500);
GPIOB->ODR&=~(1<<5); //控制第5位输出低电平,即灯亮 ,其余不变
GPIOE->ODR&=~(1<<5);
delay_ms(500);
}
}
代码分析:
①头文件
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
分析:需要万能头文件,led.h 和 延迟函数头文件
②主函数中初始化
//初始化延迟函数
delay_init();
//初始化GPIO口
LED_Init();
③执行操作
while(1){
GPIOB->ODR|=1<<5; //控制第5位输出高电平,即灯灭 ,其余不变
GPIOE->ODR|=1<<5;
delay_ms(500);
GPIOB->ODR&=~(1<<5); //控制第5位输出低电平,即灯亮 ,其余不变
GPIOE->ODR&=~(1<<5);
delay_ms(500);
}
分析:a.执行操作时需要一个while(1)死循环
b.控制GPIOB和GPIOE第5位输出高电平,即灯灭。需要用到ODR 端口输出数据寄存器,
具体操作为 GPIOB->ODR|=1<<5 和 GPIOE->ODR|=1<<5 。
c.控制GPIOB和GPIOE第5位输出低电平,即灯亮 。
具体操作为 GPIOB->ODR|=1<<5 和 GPIOE->ODR|=1<<5 。
d.延迟函数 delay_ms(500);