寄存器1、怎么操作stm32寄存器/操作单片机寄存器

学习单片机时经常听说学会操作寄存器很重要,情况也确实如此,比如某些功能库函数不能实现,或者库函数效率很低时,就只能直接操作寄存器实现。

通过3个问题就能理解寄存器及其操作。

什么是寄存器(就是个数字)?

简单可以理解为能够通过软件改写数值、控制硬件的一个32bit的数字。多个数字一起控制一个外设。

这个数值在哪?

这个数字要被存起来,就一定有一个存储的地址,地址可以通过单片机的数据手册查到。

如何改写这个数值?

 有了地址,可以设置一个指针指向此地址,然后写入数据。有库可以直接写寄存器名字进行操作。

详细解读:

1、什么是寄存器(就是个数字)?

寄存器是软件控制硬件的接口,几乎所有的硬件功能控制都是通过操作寄存器来实现的,库函数也只是将寄存器操作封装成函数,方便调用,一些复杂操作库函数可能无法实现,就只能直接操作寄存器。

一个寄存器32bit,按规则修改这个32bit数值就能控制硬件,通常一个外设需要多个寄存器控制。

2、这个数值在哪?

因为ARM单片机是统一编址的,寄存器像内存一样,内存中每个数据都有一个存储的地址,寄存器也是,只是寄存器的地址是固定的,你只能修改指定地址存储的数字才能控制硬件。

如何查找寄存器:以控制GPIO的寄存器为例。

(1)首先要有《stm32中文参考手册》这个网上很多,随便一搜就有。

(2)在目录中可以看到各种外设,比如下面这些有RCC、GPIO、DMA、ADC等

寄存器1、怎么操作stm32寄存器/操作单片机寄存器_第1张图片

(3)展开目录,基本每一个外设的章节都有一个寄存器描述

寄存器1、怎么操作stm32寄存器/操作单片机寄存器_第2张图片

(4)查看寄存器详细描述这一小节。

这里有三个关键点,了解这三点就知道改寄存器的规则了,如下图的①②③所示。

寄存器1、怎么操作stm32寄存器/操作单片机寄存器_第3张图片

①偏移地址

这个寄存器叫CRL(全称应该是control register low),但具体地址并没有直接给出,而是以具体地址=基地址+偏移地址的形式计算的。其中基地址可以在2.3存储器映像这一章找到,如图:

寄存器1、怎么操作stm32寄存器/操作单片机寄存器_第4张图片

         如果我们要控制GPIOA的CRL寄存器那么,CRL的地址就是0x40010800+0x00=0x40010800;在它后面的CRH寄存器偏移量为0x04,那么GPIOA的CRH地址为0x40010800+0x04=0x40010804;

手册里为什么要这样写?因为GPIO有多组,GPIOA、GPIOB。。。。每一组都有这个寄存器,且功能相同,只是位置不一样。

这样就比较方便在程序上的定义与使用,定义一个结构体,结构体数据形式和寄存器一样,只需要指定结构体基地址,就比较容易进行寄存器读写,而不用指定每个寄存器地址。

----------|----------|----------|----------|----------|           
   GPIOA  |   GPIOB  |   GPIOx  |  GPIOF   |  ....    | |
----------|----------|----------|----------|----------| |
   CRL    |   CRL    |   CRL    |   CRL    |   CRL    | |
----------|----------|----------|----------|----------| |
   CRH    |   CRH    |   CRH    |   CRH    |   CRH    | |
----------|----------|----------|----------|----------| |
   IDR    |   IDR    |   IDR    |   IDR    |   IDR    | ↓
----------|----------|----------|----------|----------| 地址4字节递增
   ...    |          |          |          |          | 

typedef struct
{
  vu32 CRL;
  vu32 CRH;
  vu32 IDR;
  ...
} GPIO_TypeDef;

②这两条方框是干嘛的

仔细看方框上有数字1-15和16-31这就是之前说的,每个寄存器是一个32bit数字。

1bit或几个bit组合在一起用于设置功能,比如③号框圈起来的30和31bit,这2bit一起决定一个功能。

③每bit数字具体功能

那么③里的2bit是什么功能,就要看下面的具体描述,这些描述功能的文字应该很熟悉了吧。

3、怎么修改这些寄存器

现在我们知道了这些寄存器的功能和地址,那么下一步就是修改它的数值了。做开发时我们主要还是使用固件库(只是掺杂一点直接修改寄存器操作),而库文件中定义了这些寄存器的地址,我们只需要通过他的名字访问他就行。

就比如上图中的GPIOx_CRL,这是一个通用名,比如要控制GPIOA的这个寄存器那么具体操作如下

GPIOA->CRL = 0x44444444;

 其实操作寄存器并不难,难点是了解每个外设各个寄存器的作用。要完全了解一个外设的寄存器,可以通过寄存器例程手册中的寄存器描述,结合实践加深对外设控制的理解。

扩展:

1、stm32固件库库是如何实通过寄存器名字操作寄存器的?

要理解这个问题需要有良好的C语言基础,理解起来还是比较简单的。

每一组控制GPIO的几个寄存器地址是连续的,按4字节递增(每个寄存器占4字节)。

那么我们只需要定义一个结构体,成员变量都定义为32位无符号整形变量,成员的数量就是寄存器的数量。以GPIO为例

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

GPIO_TypeDef *GPIOA;
GPIOA = GPIOA的基地址;

GPIOA->ODR = 0x11114444;   
通过GPIOA这个指针访问对应的寄存器地址。
编译器会自动计算ODR相对GPIOA基地址的具体地址。

2、一些给寄存器赋值的小技巧。

通常我们只想修改32bit中的某一位,清零或者置1

GPIOA->CRL |= 1<<6; 将bit6设为1

GPIOA->CRL |= (1<<6+1<<7);GPIOA->CRL |= (0x3<<6); 将bit6和7设为1

GPIOA->CRL &= ~(1<<6); 将bit6清零

GPIOA->CRL &= ~(1<<6+1<<7);GPIOA->CRL &= ~(0x3<<6); 将bit6和7清零

你可能感兴趣的:(stm32,单片机,stm32,嵌入式硬件)