前言
我们对片片上外设的控制,实际上就是对片上外设的寄存器进行操作,进行设置或是清除的操作 ,为了能够更好的操作外设的寄存器,ST公司为每个寄存器都分配了地址,具体是怎么分配的,接下来进行介绍。
TIM2的地址
以定时器TIM2来进行展开介绍,在固件库中对 TIM2 跳转定义,如下代码:
#define TIM2 ((TIM_TypeDef *) TIM2_BASE)
#define TIM3 ((TIM_TypeDef *) TIM3_BASE)
#define TIM4 ((TIM_TypeDef *) TIM4_BASE)
#define TIM5 ((TIM_TypeDef *) TIM5_BASE)
#define TIM6 ((TIM_TypeDef *) TIM6_BASE)
#define TIM7 ((TIM_TypeDef *) TIM7_BASE)
#define TIM12 ((TIM_TypeDef *) TIM12_BASE)
#define TIM13 ((TIM_TypeDef *) TIM13_BASE)
#define TIM14 ((TIM_TypeDef *) TIM14_BASE)
#define RTC ((RTC_TypeDef *) RTC_BASE)
#define WWDG ((WWDG_TypeDef *) WWDG_BASE)
#define IWDG ((IWDG_TypeDef *) IWDG_BASE)
#define SPI2 ((SPI_TypeDef *) SPI2_BASE)
#define SPI3 ((SPI_TypeDef *) SPI3_BASE)
#define USART2 ((USART_TypeDef *) USART2_BASE)
上图不仅能看到TIM2,还能看到其他定时器的宏定义,所以通过介绍TIM2,其他外设也是类似的方法。
看到TIM2后面的注释,((TIM_TypeDef *) TIM2_BASE),看似有些许复杂,实际上是由一个TIM_TypeDef *结构体指针强转TIM2_BASE,接下来先看到TIM2_BASE, TIM2_BASE也就是TIM2定时器的基地址,再次对TIM2_BASE进行定义跳转:
#define TIM2_BASE (APB1PERIPH_BASE + 0x0000)
#define TIM3_BASE (APB1PERIPH_BASE + 0x0400)
#define TIM4_BASE (APB1PERIPH_BASE + 0x0800)
#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00)
#define TIM6_BASE (APB1PERIPH_BASE + 0x1000)
#define TIM7_BASE (APB1PERIPH_BASE + 0x1400)
#define TIM12_BASE (APB1PERIPH_BASE + 0x1800)
#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00)
#define TIM14_BASE (APB1PERIPH_BASE + 0x2000)
#define RTC_BASE (APB1PERIPH_BASE + 0x2800)
#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00)
#define IWDG_BASE (APB1PERIPH_BASE + 0x3000)
#define SPI2_BASE (APB1PERIPH_BASE + 0x3800)
#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00)
#define USART2_BASE (APB1PERIPH_BASE + 0x4400)
TIM2_BASE又是由APB1PERIPH_BASE+0x0000得到,APB1PERIPH_BASE也就是APB1外设基地址,同时也说明了TIM2是属于APB1上的外设,同样再对APB1PERIPH_BASE进行定义跳转:
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
结果是PERIPH_BASE,同样这里等于PERIPH_BASE+0x00000,只不过后面的+0x00000省略了,同时可以看到APB2和AHB都是以PERIPH_BASE为基地址的,PERIPH_BASE也就是外设基地址,跳转定义:
#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */
#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */
可以看到,这里面不仅包含外设基地址,还含有FLASH闪存的基地址,运行内存SRAM的基地址,都是一个确切的地址,如PERIPH_BASE为 ((uint32_t)0x40000000),也就是将0x4000 0000强转为32位作为外设的基地址。
所以,通过上述可以知道TIM2的地址是由外设地址不断+0xxxxxx偏移而来的,接下来对(TIM_TypeDef *)进行展开说明:
#define TIM2 ((TIM_TypeDef *) TIM2_BASE)
同样的跳转定义(这里结构体含有的寄存器较多,对前五个展开说明):
typedef struct
{
__IO uint16_t CR1;
uint16_t RESERVED0;
__IO uint16_t CR2;
uint16_t RESERVED1;
__IO uint16_t SMCR;
uint16_t RESERVED2;
__IO uint16_t DIER;
uint16_t RESERVED3;
__IO uint16_t SR;
uint16_t RESERVED4;
} TIM_TypeDef;
CR1、CR2、SMCR、DIER、SR都为TIM2定时器中包含的寄存器,能够再数据手册找到对应的寄存器描述,接下来就通过手册对这些寄存器的描述来进一步说明。
上图对应的这五个寄存器在TIM2定时器中的位置,实际上,结构体对外设寄存器的定义顺序是按照实际内存顺序来进行的,这这个寄存器总图中能看到对应的地址偏移,同样,我们也能够在寄存器中找到对应的偏移:
通过上面对具体寄存器的查看,也能够找到对应的偏移量,进而得到寄存器的具体地址。
这样,我们定义:
#define TIM2 ((TIM_TypeDef *) TIM2_BASE)
定义TIM2_BASE为(TIM_TypeDef *)结构体指针类型,并且这个指针的起始地址就是这个外设TIM2的起始地址,那么这个结构体TIM2_BASE的成员,正好会映射到TIM2外设的每一个寄存器,如下图:
结构体指针定义的变量指向TIM2外设的起始地址,这样通过对结构体指针变量进行偏移就可以得到想要的寄存器。
这样我们访问结构体指针中的成员,如TIM2->CR1,就相当于访问了TIM2中CR1寄存器。
其他外设也是使用类似的方法来进行访问寄存器来完成一系列的操作的。
文章参考:[8-2] DMA数据转运&DMA+AD多通道_哔哩哔哩_bilibili
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_68915581/article/details/141235306
|