位操作配置STM32的端口模式;使用位域配置寄存器;使用位域的好处

下面这期博客我们来讲一下如何使用寄存器来配置STM32的端口模式

我们想要STM32的PA15引脚配置为输出推挽模式,输出速率为50MHz

首先需要找到控制PA15的位,分别是CNF15[1:0]和MODE15[1:0]

CNF控制端口是输入模式还是输出模式,MODE控制输出模式的速率

我想想要配置的是:PA15 输出推挽模式 速率为50MHz

所以CNF我们要选00,MODE我们要选11

放到寄存器的位上面就是:00 11

使用寄存器的方式来编写,就是:

GPIOA->CRH &= (0X0FFFFFFF);

GPIOA->CRH | = (0X30000000);

下面我们来具体解释一下上面的代码:

GPIOA->CRH,CRH是一个寄存器,这句代码的意思就是:

取出GPIOA中的CRH寄存器

GPIOA->CRH &= (0X0FFFFFFF);

这句代码的作用是对高四位(总共是32位,每个字母代表四位)进行清零操作

GPIOA-CRH |= (0X30000000);

对高四位进行赋值操作,让CNF15[1:0] = 0,MODE15[1:0] = 3;

这样的话,我们就完成了寄存器的配置,

成功将PA15配置成为了输出推挽模式了

上面的这种方式呢,我们是使用位操作的方式去配置STM32的端口

我们不难发现,这种方式有点麻烦,要通过一个一个位的来数

那有没有一种比较简单的方式来操作寄存器呢?有的,那就是我们的位域

在下面,我们将使用位域来操作寄存器,让PA15的端口模式为:输出推挽,输出速率50MHz

看上面的代码,我们定义了名称为CRH_Bits的位域,

这些位域的成员(例如:MODE8 CNF8)和寄存器的位一一对应

MODE8 CNF8这些都是每个域的名字,

后面的"2"表示的是所占位的大小

所有的位加起来,是一个uint32_t 的长度

我们定义了这样的一个位域之后,就可以直接使用位域来操作我们的寄存器了

看下面的代码:

((CRH_Bits*)(&GPIOA->CRH))->CNF15 = 0;

((CRH_Bits*)(&GPIOA->CRH))->MODE15 = 3;

通过上面的代码,我们成功实现了PA15的端口模式为:输出推挽,输出速率50MHz

这种方式是使用位域来操作寄存器的方式来实现的

下面我们具体来看一下解析:

首先是取出CRH寄存器的地址 (&GPIOA->CRH)

将CRH寄存器的地址转换成CRH_Bits这种位域指针类型的

最后使用这种类型的位域指针向位域中的一个成员CNF15赋值为0

同样的方法对MODE15赋值为3

这就是位域,它有点结构化数据解析的意思

原来很多无差别的位,通过位域的定义,就具有了一种结构化信息。

它还和结构体不一样,它是一直能够在"位"这样一个力度级别上的结构化

寄存器和位域具有符合性。寄存器基本上都是按位来进行功能划分的,

而C语言中的位域正好可以用来定义这种结构。

所以我们说,位域是单片机和嵌入式底层驱动的灵魂

那么它是一开始就是用于嵌入式和单片机的底层驱动的吗?

实际上并不是,C语言中的位域,一开始设计出来是为了节省内存的

我们都知道,C语言中是没有位类型的,比如布尔类型这种典型的位类型

布尔类型也就是只有0和1两种取值的类型,用来表示真和假

在C语言中,我们通常使用typedef uin8_t bool,把一个uin8_t类型当做布尔类型来使用

其实这是很浪费内存的(一个字节,有8个位,但是我们只使用到了其中的一个位)

这个时候,我们可以使用g_flags.flag来集中管理程序中的逻辑变量

它相当于是把一个uin8_t的类型切成8份来使用

每当我们有一个位的需求时,就可以只使用一个位而没有必要去使用一个字节

这样就大大节省了我们的内存消耗

这也是位域的一个好处