如果你是学过 8 位单片机的,例如 51 单片机,那么对于理解 STM32 的系统架构和功能模块是十分有利的,毕竟它们都属于单片机范畴(英文为 Microcontroller)。只不过前者是 8 位单片机(即数据总线是 8 位的),而后者是 32 位单片机(数据总线是 32 位的)。但是,如果你没有学过任何单片机,那么从零基础学 STM32 也不是没有可能,只是在理解时会困难些。学了C语言,大家都知道,开发PC程序,压根不用了解PC的硬件结构和功能部件的特点。但是开发单片机程序,必须知道单片机的内部结构和功能部件的特点和属性,从学习入门的角度而言,初学者往往被 STM32 复杂而多样的内部结构和功能部件所吓到,从而望而生畏、放弃学习。其实,依作者之见,学习者可以不必完全弄清楚硬件结构和原理后才开始STM32单片机的学习、应用系统的设计与开发。
下面是对 STM32 单片机的内部结构和功能部件的一个大致描述,在了解这些特点的基础上,就可以开始尝试STM32的应用设计与实践。
ARM 这个缩写包含两个意思:一是指 ARM 公司;二是指 ARM 公司设计的低功耗CPU及其架构,包括ARM1~ARM11 与Cortex,其中,被广泛应用的是ARM7、ARM9、ARM11以及Cortex系列。
1.ARM公司及其ARM架构
ARM是全球领先的32位嵌入式RISC芯片内核设计公司。RISC的英文全称是Reduced Instruction Set Computer,对应的中文是精简指令集计算机。特点是所有指令的格式都是一致的,所有指令的指令周期也是相同的,并且采用流水线技术。
ARM 公司本身并不生产和销售芯片,它以出售 ARM 内核的知识产权为主要模式。全球顶尖的半导体公司,例如 Actel、TI、ST、Fujitsu、NXP 等均通过购买 ARM 的内核,结合各自的技术优势进行生产和销售,共同推动基于ARM内核包括Cortex内核的嵌入式单片机的发展。
ARM 的设计具有典型的精简指令系统(RISC)风格。ARM 的体系架构已经经历了 6个版本,版本号分别是 V1~V6。每个版本各有特色,定位也各有不同,彼此之间不能简单地相互替代。其中,ARM9、ARM10 对应的是 V5 架构,ARM11 对应的是发表于 2001 年的V6架构,时钟频率为350~500MHz,最高可达1GHz。
2.Cortex内核
Cortex 是 ARM 的全新一代处理器内核,它在本质上是 ARM V7 架构的实现,它完全有别于ARM的其他内核,是全新开发的。按照3类典型的嵌入式系统应用,即高性能、微控制器、实时类,它又分成 3 个系列,即 Cortex-A、Cortex-M、Cortex-R。而 STM32 就属于Cortex-M系列。
Cortex-M 旨在提供一种高性能、低成本的微处理器平台,以满足最小存储器、小引脚数和低功耗的需求,同时兼顾卓越的计算性能和出色的中断管理能力。目前典型的、使用最为广泛的是Cortex-M0、Cortex-M3、Cortex-M4。
与 MCS-51 单片机采用的冯·诺依曼结构不同,Cortex-M 采用的是哈佛结构,即程序存储器和数据存储器不分开、统一编址。
STM32是意法半导体(STMicroelectronics)较早推向市场的基于Cortex-M内核的微处理器系列产品,该系列产品具有成本低、功耗优、性能高、功能多等优势,并且以系列化方式推出,方便用户选型,在市场上获得了广泛好评。
STM32目前常用的有STM32F103~107系列,简称“1系列”,最近又推出了高端系列STM32F4xx系列,简称“4系列”。前者基于Cortex-M3内核,后者基于Cortex-M4内核。STM32F4xx系列在以下诸多方面做了优化:
(1)增加了浮点运算;
(2)DSP处理;
(3)存储空间更大,高达1M字节以上;
(4)运算速度更高,以168MHz高速运行时可达到210DMIPS的处理能力;
(5)更高级的外设,新增外设,例如,照相机接口、加密处理器、USB高速OTG接口等,提高性能,更快的通信接口,更高的采样率,带FIFO的DMA控制器。
本书侧重于“1系列”中的STM32F103系列,所讨论的内容大部分可用于“4系列”。
STM32 跟其他单片机一样,是一个单片计算机或单片微控制器,所谓单片就是在一个芯片上集成了计算机或微控制器该有的基本功能部件。这些功能部件通过总线连在一起。就STM32 而言,这些功能部件主要包括:Cortex-M 内核、总线、系统时钟发生器、复位电路、程序存储器、数据存储器、中断控制、调试接口以及各种功能部件(外设)。不同的芯片系列和型号,外设的数量和种类也不一样,常有的基本功能部件(外设)是:输入/输出接口 GPIO、定时/计数器 TIMER/COUNTER、串行通信接口 USART、串行总线 I 2 C 和 SPI或I 2 S、SD卡接口SDIO、USB接口等。
根据ST的官方手册,STM32F10X的系统结构图如图1.1所示。
图1.1 STM32F10X系统结构图
为更加简明地理解STM32单片机的内部结构,对图1.1进行抽象简化后得到图1.2,这样对初学者的学习理解会更加方便些。
图1.2 STM32F10X系统结构简化图
现结合图1.2对STM32的基本原理做一简单分析,主要包括以下内容。
(1)程序存储器、静态数据存储器、所有的外设都统一编址,地址空间为 4GB。但各自都有固定的存储空间区域,使用不同的总线进行访问。这一点跟 51 单片机完全不一样。具体的地址空间请参阅 ST 官方手册。如果采用固件库开发程序,则可以不必关注具体的地址问题。
(2)可将Cortex-M3内核视为STM32的“CPU”,程序存储器、静态数据存储器、所有的外设均通过相应的总线再经总线矩阵与之相接。Cortex-M3 内核控制程序存储器、静态数据存储器、所有外设的读写访问。
(3)STM32 的功能外设较多,分为高速外设、低速外设两类,各自通过桥接再通过AHB 系统总线连接至总线矩阵,从而实现与 Cortex-M3 内核的接口。两类外设的时钟可各自配置,速度不一样。具体某个外设属于高速还是低速,已经被 ST 明确规定,可参阅图1.1 标示的信息。所有外设均有两种访问操作方式:一是传统的方式,通过相应总线由 CPU发出读写指令进行访问,这种方式适用于读写数据较小、速度相对较低的场合;二是 DMA方式,即直接存储器存取,在这种方式下,外设可发出 DMA 请求,不再通过 CPU 而直接与指定的存储区发生数据交换,因此可大大提高数据访问操作的速度。
(4)STM32 的系统时钟均由复位与时钟控制器 RCC 产生,它有一整套的时钟管理设备,由它为系统和各种外设提供所需的时钟以确定各自的工作速度。
根据程序存储容量,ST 芯片分为三大类:LD(小于 64KB),MD(小于 256KB),HD (大于 256KB),而 STM32F103ZET6 类型属于第三类,它是 STM32 系列中的一个典型型号。以下是它的性能简介:
(1)基于ARM Cortex-M3核心的32位微控制器,LQFP-144封装。
(2)512KB 片内 Flash(相当于硬盘,程序存储器),64KB 片内 RAM(相当于内存,数据存储器),片内Flash支持在线编程(IAP)。
(3)高达 72MHz 的系统频率,数据、指令分别走不同的流水线,以确保CPU 运行速度达到最大化。
(4)通过片内BOOT区,可实现串口的在线程序烧写(ISP)。
(5)片内双RC晶振,提供8MHz和40kHz的频率。
(6)支持片外高速晶振(8MHz)和片外低速晶振(32kHz)。其中片外低速晶振可用于CPU的实时时钟,带后备电源引脚,用于掉电后的时钟行走。
(7)42个16位的后备寄存器(可以理解为电池保存的RAM),利用外置的纽扣电池,实现掉电数据保存功能。
(8)支持JTAG、SWD 调试。可在廉价的J-LINK的配合下,实现高速、低成本的开发调试方案。
(9)多达80个GPIO(大部分兼容5V逻辑);4个通用定时器,2个高级定时器,2个基本定时器;3路SPI接口;2路I 2 S接口;2路I 2 C接口;5路USART;1个USB从设备接口;1个CAN接口;1个SDIO接口;可兼容SRAM、NOR和NAND Flash接口的16位总线的可变静态存储控制器(FSMC)。
(10)3个共16通道的12位ADC,2个共2通道的12位DAC,支持片外独立电压基准。ADC转换速率最高可达1μs。
(11)CPU的工作电压范围:2.0~3.6V。
STM32 的时钟系统比较复杂,但又十分重要。理解 STM32 的时钟树对理解 STM32 十分重要。下面分五个部分择要对其进行阐述。
1.内部RC振荡器与外部晶振的选择
STM32 可以选择内部时钟(内部 RC 振荡器),也可以选择外部时钟(外部晶振)。但如果使用内部RC振荡器而不使用外部晶振,必须清楚以下几点:
(1)对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空。
(2)对于少于100脚的产品,有两种接法:
方法1:OSC_IN和OSC_OUT分别通过10kΩ电阻接地。此方法可提高EMC性能。
方法2:分别重映射OSC_IN和OSC_OUT至PD0和PD1,再配置PD0和PD1为推挽输出并输出0。此方法相对于方法1,可以减小功耗并节省两个外部电阻。
(3)内部8MHz的RC振荡器的误差在1%左右,内部RC振荡器的精度通常比用HSE (外部晶振)要低十倍以上。STM32的ISP就是利用了HSI(内部RC振荡器)。
2.STM32时钟源
在STM32中,有5个时钟源,分别为HSI、HSE、LSI、LSE、PLL。
(1)HSI是高速内部时钟,RC振荡器,频率为8MHz。
(2)HSE 是高速外部时钟,可接石英谐振器、陶瓷谐振器,或者接外部时钟源,它的频率范围为4MHz~16MHz。
(3)LSI是低速内部时钟,RC振荡器,频率为40kHz。
(4)LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
(5)PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择2~16倍,但是其输出频率最大不得超过72MHz。
3.STM32时钟树的输入与输出
对于初次接触 STM32 的学习者来说,在熟悉了开发环境的使用之后,往往“栽倒”在同一个问题上,这个问题就是如何理解和掌握时钟树。
众所周知,微控制器(处理器)的运行必须要依赖周期性的时钟脉冲,它往往由一个外部晶体振荡器提供时钟输入为始,最终转换为多个外部设备的周期性运作为末,这种时钟“能量”扩散流动的路径,犹如大树的养分通过主干流向各个分支,因此常称之为“时钟树”。在一些传统的低端 8 位单片机,诸如 51、AVR 等单片机,它们也具备自身的一个时钟树系统,但它们中的绝大部分是不受用户控制的,亦即在单片机上电后,时钟树就固定在某种不可更改的状态。例如,51 单片机使用典型的 12MHz 晶振作为时钟源,则其诸如 I/O 口、定时器、串口等外设的驱动时钟速率便被系统固定,用户将无法更改此时钟的速率,除非更换晶振。
而 STM32 微控制器的时钟树则是可配置的,其时钟输入源与最终达到外设处的时钟速率不再有固定的关系。图1.3是STM32微控制器的时钟树。要学会STM32,必须理解时钟树的输入和输出关系。现以图 1.3 中的圆框数字序号标示的部分所示,说明时钟输入与时钟输出之间的关系,输入至输出之间的路径一可表示为①-②-③-④-⑤-⑥-⑦,当然也可以选择路径二:①-⑤-⑥-⑦。此处以路径一为例,做以下具体分析。
图1.3 STM32微控制器的时钟树
①—输入,外部晶振(HSE),可选为2~16MHz。
②—第一个分频器PLLXTPRE,可以选择1分频或2分频。
③—时钟源选择,开关PLLSRC(PLL entry clock source),我们可以选择其输出,输出为外部高速时钟(HSE)或是内部高速时钟(HSI)。这里选择输出为HSE。
④—PLL(锁相环),具有倍频功能(输入倍频因子PLLMUL,2~16倍),经过PLL的时钟称为 PLLCLK。倍频因子设定为 9 倍频,也就是说,经过 PLL 之后,时钟从原来8MHz的 HSE变为72MHz的PLLCLK。
⑤—开关 SW,经过这个开关之后就是 STM32 的系统时钟(SYSCLK)了。通过这个开关,可以切换 SYSCLK 的时钟源,可以选择为 HSI、PLLCLK、HSE。我们选择为PLLCLK时钟,所以SYSCLK就为72MHz了。
⑥—AHB预分频器(分频系数为1~512)。如果选为1,分频系数为1。
⑦—APB2预分频器(分频系数为1,2,4,8,16)。如果选为1,则分频系数为1,所以高速外设APB2(PCLK2)为72MHz。
4.STM32中几个与时钟相关的概念
SYSCLK:系统时钟,STM32大部分器件的时钟来源。它由AHB预分频器分配到各个部件。
HCLK:由AHB预分频器直接输出得到,它是高速总线AHB的时钟信号,提供给存储器、DMA 及 Cortex 内核,是 Cortex 内核运行的时钟,CPU 主频就是这个信号,它的大小与STM32运算速度、数据存取速度密切相关。
FCLK:同样由 AHB 预分频器输出得到,是内核的“自由运行时钟”(free running clock)。“自由”表现在它不来自时钟 HCLK,因此在HCLK时钟停止时FCLK也会继续运行。它的存在,可以保证在处理器休眠时,也能够采样中断和跟踪休眠事件,它与 HCLK互相同步。
PCLK1:外设时钟,由 APB1 预分频器输出得到,最大频率为 36MHz,提供给挂载在APB1总线上的外设(低速外设)。
PCLK2:外设时钟,由 APB2 预分频器输出得到,最大频率可为 72MHz,提供给挂载在APB2总线上的外设(高速外设)。
5.时钟输出的使能及其流程
在以上的时钟输出中有很多是带使能控制的,如 AHB 总线时钟、内核时钟、各种APB1外设时钟、APB2外设时钟等。
当需要使用某模块时,必须先使能对应的时钟。需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。
连接在 APB1 上的设备(低速外设)有:电源接口、备份接口、CAN、USB、I 2 C1、I 2 C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意:USB 模块虽然需要一个单独的48MHz时钟信号,但它不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。
连接在 APB2 上的设备(高速外设)有:GPIO_A-E、USART1、ADC1、ADC2、ADC3、TIM1、TIM8、SPI1、AFIO。
以下是时钟设置的基本流程。
假设,使用 HSE 时钟,并且使用 ST 的固件库函数,那么在程序中设置时钟参数的流程如下。
第1步:
将RCC寄存器重新设置为默认值,即调用函数RCC_DeInit;
第2步:
打开外部高速时钟晶振HSE,调用函数RCC_HSEConfig(RCC_HSE_ON);
第3步:
等待外部高速时钟晶振工作,调用HSEStartUpStatus=RCC_WaitForHSEStartUp();
第4步:
设置AHB时钟,即调用函数 RCC_HCLKConfig;
第5步:
设置高速AHB时钟,即调用函数RCC_PCLK2Config;
第6步:
设置低速AHB时钟,即调用函数RCC_PCLK1Config;
第7步:
设置PLL,即调用函数RCC_PLLConfig;
第8步:
打开PLL,即调用函数RCC_PLLCmd(ENABLE);
第9步:
等待PLL工作,while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);
第10步:
设置系统时钟,即调用函数RCC_SYSCLKConfig;
第11步:
判断PLL是否是系统时钟,while(RCC_GetSYSCLKSource()!=0x08);
第12步:
打开要使用的外设时钟,即调用函数 RCC_APB2PeriphClockCmd();或者 RCC_APB1PeriphClockCmd();