初识stm32库

1. 库的层次结构

库是架设在寄存器与用户驱动层之间的代码,向下直接处理与寄存器直接相关的配置,向上为用户提供寄存器的接口。
一切计算机科学问题都可以用分层来解决;库开发方式就体现了封层的思想;(1)用结构体封装寄存器参数;(2)用宏表示参数;(3)用函数封装对寄存器的操作。下面stm32库层之间的关系图;


(1)内核驱动(核内设备函数层)
    core_cm3.c和core_cm3.h文件,为芯片设计商设计的芯片外设提供进入M3内核的接口,所有M3系列的芯片这两个文件相同。core_cm3.c和启动文件一样都是底层文件,都是ARM公司提供的,遵守CMSIS标准。

    在core_cm3.c文件中包含了 stdint.h 这个头文件,一般在MDK的安装目录。在这个头文件中,定义了一些新类型,这些新类型屏蔽了,在不同的芯片平台int的大小是16位还是32位。


(2)system_stm32f10x.c
system_stm32f10x.c 由ST公司提供,符合CMSIS标准,该文件主要用来设置系统时钟和总线时钟;

(3) stm32f10x.h
 对内存的操作封装成宏;该头文件定义了寄存器的地址和使用的结构体封装。

(4)启动文件



文件名英文缩写意义如下:

cl:互联型产品,stm32f105/107系列

vl:超值型产品,stm32f100系列

xl:超高密度产品,stm32f101/103系列

ld:低密度产品,flash小于64K

md:中密度产品,flash=64K or 128K

hd:高密度产品,flash大于128K

启动文件的作用:
** 初始化堆栈指针SP
** 初始化程序计数器指针PC
** 设置堆、栈的大小
** 设置异常向量表的入口地址
** 配置SRAM作为数据存储器

** 设置C库分支入口__main (最终用来调用main函数)
** 3.5版的启动文件还调用system_stm32f10x.c文件中的 SystemInit()函数配置系统时钟。

(5)外设驱动文件
stm32f10x_ppp.c 和 stm32f10x_ppp.h  ppp表示外设的名称。

其中有个特殊文件 misc.c :该文件提供了对内核的NVIC(中断向量控制器)访问函数,因此配置中断时,需要把此函数加进去。
  (6) stm32f10x_it.c 
  文件定义了中断服务函数,中断服务函数的接口可以在启动汇编文件中找到。
 (7)stm32f10x_conf.h 

该文件用来配置使用了什么外设。处于用户层的文件才会与用户打交道,也就是我们编写程序时,要进行修改的文件。

2.存储器的地址映射

    我们可以查ST官方提供的手册,来查外设对应的内存地址。下面将讲述头文件如何通过宏定义和结构体实现外设寄存器与内存之间的映射的。看一次cortex-M3的存储器映射图

0x4000 0000 ~ 0x5FFF FFFF 512MB 地址空间是预留芯片厂商生产芯片进行具体的寄存器到地址的映射。可以通过芯片厂商提供的datasheet查找具体的外设(GPIO,ADC,DAC等)的地址映射。

对下面代码进行分析:
#define PERIPH_BASE           ((uint32_t)0x40000000) 
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)

(1)外设基地址
宏PERIPH_BASE 展开为 0x4000 0000,并把它强制转换为uint32_t,uint32_t 为32位数据类型,进行强制转换的目的主要因为stm32的地址线为32位。0x4000 0000为外设的基地址。
(2)总线基地址
    APB1PERIPH_BASE宏展开为 PERIPH_BASE (外设基地址),再加上相对于外设基地址的偏移地址 0x10000,即指向的地址为0x4001 0000 。APB1PERIPH_BASE称为APB2总线的外设基地址。
   STM32还有APB2、APB1、AHB总线,不同的总线挂在外设不同,主要根据外设速度来决定挂在哪条总线上。

#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

由上面的宏定义可知,APB1,APB2,AHB总线相对于外设基地址的偏移量分别为0,0x10000,0x20000。
APB1挂在的外设:
 RCC_APB1Periph_TIM2
  RCC_APB1Periph_TIM3
  RCC_APB1Periph_TIM4
  RCC_APB1Periph_TIM5
  RCC_APB1Periph_TIM6
  RCC_APB1Periph_TIM7
  RCC_APB1Periph_WWDG
  RCC_APB1Periph_SPI2
  RCC_APB1Periph_SPI3
  RCC_APB1Periph_USART2
  RCC_APB1Periph_USART3
  RCC_APB1Periph_USART4
  RCC_APB1Periph_USART5
  RCC_APB1Periph_I2C1
  RCC_APB1Periph_I2C2
  RCC_APB1Periph_USB
  RCC_APB1Periph_CAN1
  RCC_APB1Periph_BKP
  RCC_APB1Periph_PWR
  RCC_APB1Periph_DAC
  RCC_APB1Periph_CEC
  RCC_APB1Periph_TIM12
  RCC_APB1Periph_TIM13
  RCC_APB1Periph_TIM14 

APB2挂在的外设:
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_ADC3, 
RCC_APB2Periph_SPI1, 
RCC_APB2Periph_USART1,
RCC_APB2Periph_TIM1, 
RCC_APB2Periph_TIM8, 
RCC_APB2Periph_TIM9, 
RCC_APB2Periph_TIM10, 
RCC_APB2Periph_TIM11 
RCC_APB2Periph_TIM15, 
RCC_APB2Periph_TIM16, 
RCC_APB2Periph_TIM17, 

AHB 挂在的外设:
RCC_AHBPeriph_DMA1
RCC_AHBPeriph_DMA2
RCC_AHBPeriph_SRAM
RCC_AHBPeriph_FLITF
RCC_AHBPeriph_CRC
RCC_AHBPeriph_FSMC
RCC_AHBPeriph_SDIO 

(3)寄存器组基地址
宏GPIOA_BASE展开为APB1PERIPH_BASE(APB2总线外设基地址)加上相对于相对于APB2总线的外设基地址的偏移地址0x0800,即0x4001 0800,为GPIOA端口寄存器组的基地址。

头文件中还有类似的宏:
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)

GPIO都对应一组寄存器,包括端口配置低寄存器(GPIOx_CRL),端口配置高寄存器(GPIOx_CRH),端口输入数据寄存器(),端口输出数据寄存器()等,通过查数据手册,可以得到这些知识。


其中,有个偏移地址0x00,这个是相对于寄存器组的基地址的;例如,GPIOA的端口配置低寄存器的地址就应该为
GPIOA_BASE+0x00,GPIOB的端口低寄存器地址就应该为GPIOB_BASE+0x00;这时,我们应该想到在stm32f10x.h头文件中是不是应该有一条语句:#define GPIOA_CRL  (unit32_t)GPIOA_BASE+0x00  。但是ST的工程师并没有只么做,而是采用了结构体对寄存器进行了封装;
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
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     __IO    volatile   
volatile 是C语言中的关键字,关于volatile的用法,可以参考下面这篇博文:

宏GPIOA展开为GPIOA_BASE(A端口寄存器组的基地址),并把它强制转换为GPIO_TypeDef类型的指针。GPIO_TypeDef为自定义的结构体类型。

c语言中的结构体存储是连续的,若我们定义一个GPIO_TypeDef类型的结构体指针,并使它指向GPIOA_BASE(0x4001 0800),则结构体中的变量
CRL: 0x4001 0800+0x00
CRH:0x4001 0800+0x04
IDR:    0x4001 0800+0x08

不难发现0x00,0x04,0x08正是数据手册里面CRL、CRH、IDR相对于所在寄存器组的偏移地址。此时就可以通过GPIO->CRL 对端口配置低寄存器进行操作,就像开发51单片机一样就行配置寄存器开发。一般不提倡对stm32进行寄存器开发,因为stm32寄存器比较多,配置起来比较繁琐,移植起来比较麻烦。这篇博文就写到这里,后面将对库开发方式就行讲解。。。。。。。。。。。。。。


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值