本篇文章主要是学习以M3内核的STM32的GPIO的寄存器的配置,为什么要学习寄存器,而不利用库函数呢?我只能说为了让学的知识更加牢固吧!当然,你可以直接去利用库函数,但是如果你能认真读完本篇博客,你会对知识豁然开朗!加油吧!
STM32 的每个 IO 端口都有 7 个寄存器(如果还不懂寄存器是什么,请点击)来控制。他们分别是:配置模式的 2 个 32 位的端口配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;1 个 32 位的置位/复位寄存器BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器 LCKR;这里我们仅介绍常用的几个寄存器,我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR 。
可能罗列了这么多的寄存器,小白可能会一头蒙(心想,用的时候再找呗,我想说,那还不如直接记住,对吧,哈哈哈)接下来我会每一个介绍一下,然后会有一个程序说明,仔细看完文章吧!
一、CRL、CRH
STM32的每个 IO 口都可以*编程,但 IO 口寄存器必须要按 32 位字被编辑被访问(因为系统本身是32位),CRL 和 CRH 控制着每个 IO 口的模式及输出速率,只不过CRL是控制低8位的GPIO接口(GPIO0至GPIO7);CRH是控制高8位的GPIO接口(GPIO8至GPIO15)。而配置一个CRL或CRH都需要4位(MODE+CNF共4位)控制一个IO口。
下面以 CRL (CRH同样)进行描述解析:(可以跳过看图,下面可以根据所写,在回过来看图)
注意:图*32位(是连续的呀,中间不是断的),MODE+CNF共4位用来控制一个IO口,可以配置GPIO0至GPIO7的IO口,
直接上例子,来说明问题:咱们来控制一个 PB5 为推挽输出(5是低7位中数呀,这时就需要使用CRL了),这时候需要看看上表中的信息了,从表中可以,如果想配置B系列的管脚5口为推挽输出,只需要将CNF5设置为00,将MODE5设置为11(此时设置的速率为最大,可以自己去选择)就可以了(简单吧,哈哈),那怎么写程序呢?首先明确的是要使用16进制写(不懂16进制请点击我的博客:C语言中数组的“那些事儿”),此时的情况就是:00 00 00 00 00 11 00 00 00 00 00 00 00 00 00 00(注意这个是从高位到低写的),16进制也就是:0X00300000。
PB5管脚配置程序如下:
GPIOB->CRL&=0XFF0FFFFF;//清掉这个位原来的设置,同时也不影响其他位的设置
GPIOB->CRL|=0X00300000;//清掉完原来的设置,此时就可以设置PB5为推挽输出
同样,如果想配置PB8管脚为推挽输出呢?(8是高8位中的数呀,这时就需要使用CRH了),程序如下:
GPIOB->CRH&=0XFFFFFFF0;//清掉这个位原来的设置,同时也不影响其他位的设置
GPIOB->CRH|=0X00000003;//清掉完原来的设置,此时就可以设置PB8为推挽输出
简单吧,(如果还不懂,就从头再看一遍)
二、IDR、ODR
IDR 是一个端口输入数据寄存器,只用了低 16 位。该寄存器为只读寄存器,并且只能以16 位的形式读出,你如果要想知道某个 IO 口的状态,你只要读这个寄存器,再看某个位的状态就可以了,使用起来是还是比较简单的。
需要注意的是IDR这是个只读寄存器,是不能给它赋值的。举例的程序为:
GPIOA->IDR & 0x0001 //即为 PA1的电平状态,IDR寄存器的16位对应了该GPIO的P0-P15
ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口的输出电平。
举例的程序为:
GPIOA->ODR =1>>13 //即为 PA13的电平状态,IDR寄存器的16位对应了该GPIO的P0-P15
GPIOA->ODR =1>>5 //即为 PA5的电平状态,IDR寄存器的16位对应了该GPIO的P0-P15
没错,你没有看错,这句程序按照格式是可以罗列的。
如果IDR、ODR想调用,举例就是如果想读出PA1的状态,赋值给PA13,程序如下(需要借助一个变量:先定义一个变量unsigned short temp):
temp = GPIOA->IDR & 0x0001;
GPIOA->ODR = temp>>13;
如果看不懂,再看一下上面的内容(哈哈哈)
下面我通过书上找的控制GPIO的PB5和PE5的LED灯使其交替闪烁为例,设置步骤:
1. 使能相关时钟
2. 设置相应的 IO 口为输入或输出
3. 设置输入\输出的类型 设置输出的速度
4. 设置上拉下拉寄存器
5. 通过复位置位寄存器和输入输出数据寄存器进行操作。
程序如下: (led.c)
#include "led.h"
//初始化 PB5 和 PE5 为输出口.并使能这两个口的时钟
//LED IO 初始化
void LED_Init(void)
{
RCC->APB2ENR|=1<<3; //使能 GPIOB 时钟 APB2ENR是APB2总线上的外设时钟使能寄存器
RCC->APB2ENR|=1<<6; //使能 GPIOE 时钟
GPIOB->CRL&=0XFF0FFFFF;
GPIOB->CRL|=0X00300000;//PB.5 推挽输出
GPIOB->ODR|=1<<5; //PB.5 输出高
GPIOE->CRL&=0XFF0FFFFF;
GPIOE->CRL|=0X00300000;//PE.5 推挽输出
GPIOE->ODR|=1<<5; //PE.5 输出高
}
程序如下: (led.h)
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED 端口定义 为了简化程序,设置宏定义
#define LED0 PBout(5)
#define LED1 PEout(5)
void LED_Init(void); //初始化
#endif
主程序如下(main.c)
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与 LED 连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
delay_ms(300);
LED0=1;
LED1=0;
delay_ms(300);
}
}
附:程序中的APB2ENR 是 APB2 总线上的外设时钟使能寄存器:
使能的GPIOB 和GPIOE 的时钟使能位,分别在 bit3 和 bit6,只要将这两位置 1 就可以使能 GPIOB 和GPIOE 的时钟了(简单吧!)
好了,就介绍到这,其实主要是介绍学习的方法,如何去分析,学会后,就可以很快的去接触一款新的控制器芯片。本专栏为一列,请关注。