STM32学习3 寄存器映射和GPIO寄存器编程

  • 一、STM32外设内存空间
  • 1. 内存空间划分
  • 2. 区域功能说明
  • (1)block0
  • (2)block1
  • (3)block2
  • (4)block3~4
  • (5)block5
  • (6)block6
  • (7)block7
  • 3. APB总线
  • 4. AHB总线
  • 二、寄存器映射与位带操作
  • 1. 寄存器映射
  • 2. 位带操作
  • 三、寄存器映射操作步骤
  • 1. 找到外设基地址
  • 2. 找到 GPIOC 的地址
  • 3. GPIO的寄存器
  • 4. 设置GPIO口工作模式
  • 5. 开启时钟
  • 6. 寄存器映射示例


本文通过介绍GPIO寄存器,介绍寄存器编程方法,实现与前一章库函数编程类似的控制功能。
本系列源码地址: https://gitee.com/xundh/stm32_arm_learn

一、STM32外设内存空间

1. 内存空间划分

STM32 的4G地址空间以512M为单位划分为8个地址区域:

STM32学习3 寄存器映射和GPIO寄存器编程_#define

2. 区域功能说明

以下是8个区域每个区域的功能描述:

(1)block0

block 0 是Flash的第一块,它是Flash存储器的重要部分,主要有以下作用:

  • 让Bootloader可以在不同地址上运行,从而实现不同的启动方式和功能;
  • 片内Flash:0x08000000~0x0807FFFF 512K容量 ;
  • System momory:存储bootloader,0x1FFF F000~0x1FFF F7FF;
  • Option Bytes:存储启动方式 、读保护设置 、写保护设置 、时钟源选择、唯一设备标识信息等 ;

STM32学习3 寄存器映射和GPIO寄存器编程_寄存器_02

(2)block1

block1 也是Flash存储器的区块,用来设计片内SRAM。

(3)block2

通常用于存储片内外设的配置信息、参数设置 以及其它相关数据,这是寄存器编程控制外设的重点,下表是STM32外设空间映射表:

STM32学习3 寄存器映射和GPIO寄存器编程_嵌入式硬件_03

(4)block3~4

用于配置和设置外部存储器接口FSMC(Flexible Static Memory Controller)。

STM32学习3 寄存器映射和GPIO寄存器编程_学习_04

(5)block5

也是用于FSMC。

STM32学习3 寄存器映射和GPIO寄存器编程_#define_05

(6)block6

预留 。

(7)block7

ARM内部使用。

3. APB总线

在STM32微控制器中,APB(Advanced Peripheral Bus)是一种总线结构,用于连接微控制器内部的外设。STM32微控制器通常具有两个或多个APB总线,包括APB1、APB2等。这些总线的作用是连接微控制器的核心部件(如CPU、存储器等)与外设之间,以实现外设的控制和数据传输。

  • APB1总线主要用于连接一些低速外设,前表从TIM2~DAC的部分;
  • APB2总线则用于连接一些高速外设,前表从AFIO~ADC3。

4. AHB总线

AHB(Advanced High-performance Bus)总线是STM32微控制器中的一种高性能总线,通常有更高的带宽和速度。
AHB总线被分为主AHB总线和备用AHB总线。

AHB的外设从前表的 SDIO~CRC。

二、寄存器映射与位带操作

1. 寄存器映射

寄存器映射是一种将硬件设备内部的控制寄存器映射到处理器的内存地址空间中的技术(即对内存单元取一个别名),使得处理器可以通过读写这些特定的内存地址来控制和配置硬件设备的功能和参数。
寄存器映射使用#define

2. 位带操作

位带操作是一种针对特定位进行操作的技术,它可以在单条指令中对某个位进行设置、清除或者翻转,从而实现对寄存器中的单个位的操作,而不影响寄存器中的其他位。

三、寄存器映射操作步骤

1. 找到外设基地址

假设找GPIOC的第 0 管脚 , 首先,要通过手册知道GPIOC挂在APB2上,总线的基地址如下:

总线名称

基地址

相对APB地址

APB1

0x4000 0000

0x0

APB2

0x4001 0000

0x0001 0000

AHB

0x4001 8000

0x0001 8000

C定义:

//  外设基地址
#define PERIPH_BASE ((unsigned int)0x40000000)
// APB2 总线基地址
#define APB2PERIPH_BASE ((PERIPH_BASE + 0x00010000)

2. 找到 GPIOC 的地址

0x4001 1000~ 0x4001 13FF

STM32学习3 寄存器映射和GPIO寄存器编程_stm32_06


C 定义:

#define GPIOC_BASE (AHB2PERIPH_BASE + 0x1000)

在 stm32f10x.h 里可以找到它的定义:

STM32学习3 寄存器映射和GPIO寄存器编程_#define_07

3. GPIO的寄存器

GPIOC的寄存器偏移根据手册查询:

寄存器

地址

相对GPIOC基址的偏移

GPIOC_CRL

0x4001 1000

0x00

GPIOC_CRH

0x4001 1004

0x04

GPIOC_IDR

0x4001 1008

0x08

GPIOC_ODR

0x4001 100C

0x0C

GPIOC_BSRR

0x4001 1010

0x10

GPIOC_BRR

0x4001 1014

0x14

GPIOC_LCKR

0x4001 1018

0x18

C 定义:

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;
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)

这样使用 GPIOC->CRL 就可以操作对应的寄存器。

4. 设置GPIO口工作模式

根据前一章的介绍,GPIO的CRL和 CRH是工作模式寄存器。如果要设置推挽输出,可以使用:

GPIOC->CRL = 0x33333333; // 配置低八位引脚为推挽输出模式
    GPIOC->CRH = 0x33333333; // 配置高八位引脚为推挽输出模式

5. 开启时钟

在 STM32 中,每个外设都需要时钟信号来工作,在使用外设之前需要先打开对应外设的时钟。

RCC 寄存器中的 RCC_APB2ENR 用于控制 APB2 总线上的外设时钟使能。寄存器编程时,使用宏定义:RCC_APB2ENR_IOPCEN 用来让 GPIOC 的时钟使能位,它对应于 RCC_APB2ENR 寄存器中的 GPIOC 时钟使能位。

以下是 RCC_APB2ENR 寄存器中常用的一些位定义:

  • Bit 0: AFIOEN,用于使能 AFIO 外设的时钟。
  • Bit 2: IOPAEN,用于使能 GPIOA 外设的时钟。
  • Bit 3: IOPBEN,用于使能 GPIOB 外设的时钟。
  • Bit 4: IOPCEN,用于使能 GPIOC 外设的时钟。
  • Bit 5: IOPDEN,用于使能 GPIOD 外设的时钟。
  • Bit 6: IOPEEN,用于使能 GPIOE 外设的时钟。
  • Bit 7: IOPFEN,用于使能 GPIOF 外设的时钟。
  • Bit 8: IOPGEN,用于使能 GPIOG 外设的时钟。
  • Bit 11: ADC1EN,用于使能 ADC1 外设的时钟。

编程时,使用 |= 运算符把相应位置为 1,如:RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

6. 寄存器映射示例

#include "stm32f10x.h"

// GPIO配置函数
void GPIO_Configuration(void)
{
    // 开启GPIOC的时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

    // 设置GPIOC的模式为推挽输出
    GPIOC->CRL = 0x33333333; // 配置低八位引脚为推挽输出模式
    GPIOC->CRH = 0x33333333; // 配置高八位引脚为推挽输出模式
}

// 延时函数
void delay(uint32_t i)
{
    while (i--) //当i不为0时,持续减1,实现延时
        ;
}

// 打开指定位置的LED
void on(int position)
{
    GPIOC->BSRR = (1 << position); // 设置对应的位,输出低电平
}

// 关闭指定位置的LED
void off(int position)
{
    GPIOC->BRR = (1 << position); // 清除对应的位,输出高电平
}

// 主函数
int main(void)
{
    GPIO_Configuration(); //调用GPIO配置函数

    int j;

    while (1) //无限循环
    {
        for (j = 0; j < 8; j++) //遍历0到7号位
        {
            on(j); //打开j号位的LED
            delay(0xfffff); //延时
            off(j); //关闭j号位的LED
            delay(0xfffff); //延时
        }
    }
}