STM32寄存器剖析

STM32寄存器剖析

 次点击
13 分钟阅读

在学习和使用STM32的过程中和寄存器打交道是必不可免的。今天就大家详细了解一下STM32内部的寄存器。

1.寄存器的概念

寄存器是CPU内部的存储区域,具有高速读写的特点。寄存器既可以暂存指令,也可以存储数据和地址。

因此在存储内容上,寄存器就可以分为指令寄存器,数据寄存器,和地址寄存器。

2.寄存器的分类

1.内核寄存器

  • General-Purpose 简称GPR 通用目的寄存器:用于存储数据和地址,参与算术逻辑运算等操作。在 STM32 中,通用目的寄存器包括 R0-R15 等。这里要注意的是R12-R15只是名义上的通用目的寄存器,实际上并不是通用目的寄存器。其中 R0-R7 是低组寄存器,所有指令都能访问;R8-R12 是高组寄存器,仅支持32 位指令。

  • Stack Pointer 简称SP 堆栈指针寄存器(R13):每一种异常模式都有其自己独立的 R13寄存器,通常指向异常模式所专用的堆栈。不同的模式下都有各自独立的堆栈,用于在程序执行过程中保存临时数据、局部变量等,保证各种模式下程序的状态的完整性。在Cortex-M中采用双堆栈模式,异常采用Main SP(简称MSP),线程采用Process SP(PSP),通过CONTROL[1]进行切换,这种双堆栈模式可以将中断和用户程序隔离开,在一定程度上保护了中断。

  • Link Register 简称LR 连接寄存器(R14):保存子程序返回地址。当使用 BL 或 BLX 指令进行跳转时,跳转指令自动把返回地址放入 R14 中,子程序通过把 R14 复制到程序计数器 PC 来实现返回。当异常发生时,异常模式的 R14 用来保存异常返回地址。

  • Program Counter 简称PC 程序计数器(R15):存放正在执行的指令的地址。在读取时,返回的值是当前指令的地址加上一定的偏移量(这与处理器的架构和流水线设计有关);向 PC 中写数据,会引起一次程序的分支。

2.外设寄存器

  • 控制寄存器(xxx_CR):用来控制、配置外设的工作方式,例如 GPIO 端口模式寄存器(GPIOx_MODER),可以配置 GPIO 引脚为输入、输出、模拟等不同的工作模式。

  • 状态寄存器(xxx_SR):存储了当前外设的工作状态,例如串口的状态寄存器(USART_SR),可以通过读取该寄存器的某些位来判断串口是否发送完成、是否接收到数据等,这跟标志位有很大关系,可以这么说状态寄存器就是标志位的🏠。

  • 数据寄存器(xxx_DR):用于存储外设进行输入输出的数据。比如 GPIO 端口的输入数据寄存器(GPIOx_IDR)用于读取 GPIO 引脚的输入状态,输出数据寄存器(GPIOx_ODR)用于设置 GPIO 引脚的输出状态。

  • 位操作寄存器:针对某些需要对单个位进行操作的场景,STM32 提供了位操作寄存器。例如 GPIOx_BSRR(设置 / 清除寄存器),可以对 GPIO 引脚的单个位进行置位和复位操作,方便了对特定引脚位的控制,而不必对整个寄存器进行操作。

  • 锁定寄存器:用于锁定某些寄存器的配置,防止意外的修改。比如 GPIO 端口配置锁定寄存器(GPIOx_LCKR),可以在配置完成后锁定 GPIO 的配置,避免误操作改变引脚的配置。

特别注意,这些外设寄存器不是单独置于外设之外的,而是所有外设内部包含共有的。这些外设寄存器只是在功能上进行了大体分类,实际在具体的外设中,比如GPIO,它的内部会具体分出输出速度寄存器,输出类型寄存器等。

3.如何使用寄存器

熟悉C语言的同学一定了解过指针和宏定义。没错,寄存器就是主要依靠指针和宏定义进行操作的。大家都听过的库函数也是ST公司基于指针操作寄存器开发的,样例代码如下。

#define PERIPH_BASE           ((uint32_t)0x40000000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)
#define RCC                 ((RCC_TypeDef *) RCC_BASE)

/** 
  * @brief Reset and Clock Control
  */

typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;

#ifdef STM32F10X_CL  
  __IO uint32_t AHBRSTR;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_CL */ 

#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)   
  uint32_t RESERVED0;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */ 
} RCC_TypeDef;

/**
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2ENR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2ENR &= ~RCC_APB2Periph;
  }
}

这段代码是外设时钟控制函数。在使用外设前,需要先开启外设时钟,这时候就需要调用该函数开启对应时钟。

4.结尾

至此,你已经对STM32底层逻辑有了基本的认识,希望大家能继续深入STM32的学习,文章有哪些不对的地方欢迎指正。

© 本文著作权归作者所有,未经许可不得转载使用。