从本章起则开始进行详细的章节学习,文章按照STM32F1xx中文参考手册的章节逐一进行重点解读和例程测试。顺序如下图:

 1.手册重点解读

 1.1系统架构 

从系统结构图中可以看出一颗芯片包括内核、flash、SRAM、DMA、外设等部分构成。各个部分的访问主要通过总线矩阵来完成。对于使用来说,整体结构的差异并不会对使用体验造成太大的差别。此部分则是需要关注AHB总线下挂载的外设分布,包括APB1、APB2、AHB下挂载了哪些外设,在编程时时钟分配可按照此规则分配。 

通过手册中的介绍我们可以知道APB1 <= 36M、APB2 <= 72M,并且flash和sram也是挂载在总线上的,只是默认进行开启,其他所有外设则需要通过开启外设时钟进行使能。通过查找标准库(rcc.h):

可以看到存储器使用AHB总线时钟,但其实这里产生了一个问题,flash的读取速度事实上是达不到72MHz的,因此要引入FLASH读取等待的问题。看过ST数据手册的同学应该知道,ARM 32位的Cortex™-M3 CPU在最高72M工作频率,存储器0等待周期访问时(Dhrystone 2.1)的跑分达到1.25DMips/MHz。 

 以前读到这里时常常让我感到困惑,存储器不能在0等待周期的时候工作在72M,那这个数据是怎么测量出来的?系统时钟和闪存访问等待时间的关系: 

后来经过半导体前辈的引导才明白,芯片跑分的单位都是每MHz的结果,完全不需要在最高频下测试,只需将0等待周期下得到的每MHz结果乘以最高主频即可。有兴趣的同学可以试试。

回到主题,STM32F103等待周期分三段0-24、24-48、48-72对应0等待、1等待、2等待。在进行系统初始化时,系统会帮我们配置好:

在启动文件中:

 systeminit函数执行系统初始化,进入这个函数

 函数先清除时钟相关寄存器,然后通过setsysclock函数进行时钟配置,再进入这个函数:

发现此函数做了条件编译处理,此时setsysclockto72是被编译的函数,由名字可知此时系统要进行72MHz的配置,其他的函数则是通过定义相关宏来切换时钟。按照我们上面说的,72MHz主频时,等待周期为2,那我们接着进去看看是否如此。

 进入函数:

可以看到函数在开启了HSE(外部高速晶振4-16MHz)之后就进行了预取缓冲使能并配置等待周期为2,在频率较低时即打开等待是因为一旦频率达到72M再进行设置就没有机会了,有兴趣的同学可以试试把等待周期屏蔽掉,在时钟达到72MHz之后程序就不运行了,所以一定要按合理顺序来。那这里同时还引入了一个预取缓存的知识,在ST后面的系列中,预取缓冲越来越强大,可以做到在更高主频时依然保证处理器性能。

 1.2存储器

在上面,我们已经扩展了一部分存储相关的知识,接下来开始介绍存储器相关内容。在数据手册中有一张关于全部地址的功能分布图:

在STM32F103所有内存空间中各个部分的分布就如上图所示,我们知道,寄存器操作的终极目的就是读写某个地址,此地址可看作一种触发器,最终作用于內部电路。所以寄存器都以地址的形式存在。有兴趣的同学可以详细研究下系统的各个分部。

 寄存器的分布地址我们一般是用不到的,但直接使用寄存器编程时或者在某些特殊情况还是要进行了解的。这里的首地址其实就是函数库中定义的某个外设第一个寄存器的基地址。

例如GPIOA在stm32f10x.h中可以找到如下定义:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

再往下找 GPIOA_BASE 定义:

#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)

因为GPIO全部挂载再APB2下,因此地址也分布在以APB2为基地址偏移0x0800的位置,再找到

APB2PERIPH_BASE的定义:

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

再找到 PERIPH_BASE 的定义:

#define PERIPH_BASE           ((uint32_t)0x40000000)

我们发现,外设的基地址是((uint32_t)0x40000000),和上面存储器图的外设区域相符合。

最终我们得到:

#define GPIOA     ( (((uint32_t)0x40000000)+ 0x10000)+ 0x0800) = 0x40010800

和我们上图寄存器地址对比发现相同,由此也算验证了寄存器操作的根本原理。

SRAM

sram中最有趣的可能就是位段操作这一块了,通俗解释起来就是把地址膨胀起来,可以独立操作每一位,可以减少一次操作32位还不能修改其他位的繁琐,但在7系列中这个功能好像没有了,有兴趣的同学可以做下相关实验。 

 

FLASH

 不同容量的flash分块大小多少是不相同,这主要影响在程序中要操作flash的方式,包括读写擦除等要使用的参数并不相同。另外STM32F103有内置固化的串口升级程序,用户可通过串口1来完成程序的烧录,这段固化好的代码就存放在系统存储器区,大小2k,在这个区域后面还有16个字节的option区域,常用此区域完成读写保护的功能。

后面我们会单独开一章来教大家手把手还原这个2K的串口升级程序,在此系列会对flahs和option的操作进行详细说明:

通过上面的介绍,同样会产生一个问题,那就是芯片正常运行程序和用串口进行升级时状态是怎么进行区分的?我们可以看到手册中对这种情况进行了介绍,事实上,程序不仅可以通过上述两种方式工作,还可以在SRAM中运行,具体的切换方式则是在复位时读取BOOT0 和 BOOT1两个引脚进行选择(BOOT0 是专用引脚,BOOT1是PB2引脚)在复位时PB2作为启动引脚,之后恢复IO功能。

在这段的最后,手册明确指出必须将中断向量进行偏移,这是怎么回事呢,原因是芯片从0x00000000处启动,随后定义的中断入口,那程序在偏移到其他位置时受用的中断就找不到了入口,此时必须告诉芯片我的中断入口在哪,为了使中断入口和代码处于同个区域,通常是将中断向量进行偏移。

在系统初始化的最后会进行如下操作 :

不同的区域则需要不同的偏移地址,否则中断响应则会异常。

至此,存储器和总线架构中较为重要的部分记录和讲解完成。

写文章的初衷主要是为了加深学习印象以及记录重要知识点,防止忘记后又要重新研究。同时给处于初学阶段的同学一点启发,作者水平有限,如有错误,欢迎指正。更多问题可留言询问。

                                                                                                                                         2021/12/4

Logo

开源、云原生的融合云平台

更多推荐