普通I/O口的追根溯源
第一次写博客比较仓促,排版布局、文字都比较差,请见谅。o(∩_∩)o 哈哈
以往在传统的8位单片机里,只要写下面这串就可以点亮LED灯。
#include<reg52.h>
int main (void)
{
P0=0;
}
当时上课的时候就会在想,为什么把P0清零就可以实现LED的点亮和熄灭。仔细翻看程序,在包含的头文件<reg52.h> 中,包含着这么一段:
/* BYTE Registers */
sfr P0 = 0x80;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0;
sfr PSW = 0xD0;
****** *******
这就是单片机中的地址映射,将存储器,I/0口与地址相连接起来。然后通过设置TMOD寄存器进行相应的配置。而在STM32里面,也有这样一整套完整的地址映射,在stm32f10x.h这个头文件里。当然ARM的这款32位MCU比传统的8位单片机来说性能增强了N倍(具体无从考证)。所以,在对于像我这样的初学者该如何学习这个问题,我的想法是先学库函数,然后根据自己的理解可以选择配置寄存器开发,追根溯源学习库函数可以大大增强我们这些菜鸟的C语言水平。
由于STM32是ARM CORTEX-M3架构的32位MCU,所以理论上有2的32次方的地址范围(平方不会打o(︶︿︶)o ),
0X4000000到0X5FFFFFFF这里就是用于片上外设,也就是咱地址映射的区域(其他地方我也不懂,求大神指点)。
在stm32f10x.h这个文件里有这么一个定义
/*!< Peripheral memory map */
#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 APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
在这里的PERIPH_BASE也就对应了上图的0X40000000地址,而我们今天要讲的I普通/0口地址就在AHB2PERIPH_BASE这条高速总线上。
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
这个宏定义的意思就是在PERIPH_BASE地址的基础上偏移0X10000,也就是0X40010000这个地址上。
同理,GPIOX_BASE的地址就是在AHB2PERIPH_BASE的地址偏移若干得到。
而在这里用GPIO_TypeDef是个结构体类型,这里用指针也就是让GPIOX取得GPIOX_BASE的地址,这样修改GPIOX也就是相当于修改相应GPIOX_BASE。而关于GPIO_TypeDef这个结构体类型。
这里回到GPIO_TypeDef 这段代码,这个代码用typedef 关键字声明了名为GPIO_TypeDef 的结构体类型,里面有7 个 __IO uint32_t。这些变量每个都为32 位,也就是每个变量占内存空间4 个字节。在c 语言中,结构体内变量的存储空间是连续的,也就是说假如我们定义了一个GPIO_TypeDef ,这个结构体的首地址(变量CRL 的地址)若为0x40011000,那么结构体中第二个变量(CRH )的地址即为0x4001 1000 +0x04 , 加上的这个0x04 ,正是代表4 个字节地址的偏移量。 即
7个32位的寄存器也就是占用了28个字节。可是在下面的定义里明明占用了1024个字节。(不懂,请大神指点。)所有的I/O口配置都是在这个基础上进行的。
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)