在上图 中,被控单元的FLASH,RAM,FSMC 和AHB 到APB 的桥(即片上外设),这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过 C 语言对它们进行数据的读和写)。
在这4G的地址空间中,ARM已经品均分成了8 个块,如以下图中,每块 512MB,每个块也都规定了用途。每个块的大小都有 512MB,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。
存储器功能分类:
序号 | 用途 | 地址范围 |
---|---|---|
Block 0 | Code | 0x0000 0000 ~ 0x1FFF FFFF(512MB) |
Block 1 | SRAM | 0x2000 0000 ~ 0x3FFF FFFF(512MB) |
Block 2 | 片上外设 | 0x4000 0000 ~ 0x5FFF FFFF(512MB) |
Block 3 | FSMC 的 bank1 ~ bank2 | 0x6000 0000 ~ 0x7FFF FFFF(512MB) |
Block 4 | FSMC 的 bank3 ~ bank4 | 0x8000 0000 ~ 0x9FFF FFFF(512MB) |
Block 5 | FSMC 寄存器 | 0xA000 0000 ~ 0xCFFF FFFF(512MB) |
Block 6 | 没有使用 | 0xD000 0000 ~ 0xDFFF FFFF(512MB) |
Block 7 | Cortex-M3 内部外设 | 0xE000 0000 ~ 0xFFFFFFFF(512MB) |
在这 8 个 Block 里面,有 3 个块非常重要,也是我们最关心的三个块。
Block0 用来设计成内部 FLASH
Block0 主要用于设计片内的 FLASH ,我们使用的STM32F103ZET6 (霸道)和STM32F103VET6(指南者)的 FLASH 都是 512KB,属于大容量。要在芯片内部集成更大的 FLASH 或者 SRAM 都意味着芯片成本的增加,往往片内集成的 FLASH 都不会太大,ST能在追求性价比的同时做到512KB,实乃良心之举。
块 | 用途 | 地址范围 |
block0 | ||
预留 | 0x1FFE C008 ~ 0x1FFF FFFF | |
选项字节: 用 于 配 置 读写 保 护 、 BOR 级别、软件/硬件看门狗以及器 件处于待机或停止模式下的复位。当 芯片不小心被锁住之后,我们可以从 RAM 里面启动来修改这部分相应的 寄存器位。 | 0x1FFF F800 - 0x1FFF F80F | |
系统存储器:里面存的是ST 出厂时 烧 写 好 的 isp 自 举 程 序 ( 即 Bootloader),用户无法改动。串口 下载的时候需要用到这部分程序。 | 0x1FFF F000- 0x1FFF F7F | |
预留 | 0x0808 0000 ~ 0x1FFF EFFF | |
FLASH:我们的程序就放在这里。 | 0x0800 0000 ~ 0x0807 FFFF (512KB) | |
预留 | 0x0008 0000 ~ 0x07FF FFFF | |
取决于 BOOT 引脚,为 FLASH、系 统存储器、SRAM 的别名。 | 0x0000 0000 ~ 0x0007 FFF |
储存器 Block1 内部区域功能划分
Block1 用 于 设 计 片 内 的 SRAM.我 们 使 用 的 STM32F103ZET6( 霸 道 )和STM32F103VET6(指南者)的 SRAM 都是 64KB。
块 | 用途 | 地址范围 |
block1 | ||
预留 | 0x2001 0000 ~ 0x3FFF FFFF | |
SRAM 64KB | 0x2000 0000 ~0x2000 FFFF | |
储存器 Block2 内部区域功能划分
Block2 用于设计片内的外设,根据外设的总线速度不同,Block 被分成了 APB 和 AHB两部分,其中 APB 又被分为 APB1 和 APB2。
块 | 用途 | 地址范围 |
block2 | ||
APB1 总线外设 | 0x4000 0000 ~ 0x4000 77FF | |
APB2 总线外设 | 0x4001 0000 ~ 0x4001 3FFF | |
AHB 总线外设 | 0x4001 8000 ~ 0x5003 FFFF |
我们知道,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么寄存器映射?寄存器到底是什么?
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
比如,我们找到 GPIOB 端口的输出数据寄存器 ODR 的地址是 0x4001 0C0C(至于这个地址如何找到可以先跳过,后面我们会有详细的讲解),ODR 寄存器是 32bit,低 16bit有效,对应着 16 个外部 IO,写 0/1 对应的的 IO 则输出低/高电平。现在我们通过 C 语言指
针的操作方式,让 GPIOB 的 16 个 IO 都输出高电平。
例:通过绝对地址访问内存单元
1 // GPIOB 端口全部输出 高电平
2 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;
0x4001 0C0C 在我们看来是 GPIOB 端口 ODR 的地址,但是在编译器看来,这只是一个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,把它转换成指针,即(unsigned int *)0x4001 0C0C,然后再对这个指针进行 * 操作。
刚刚我们说了,通过绝对地址访问内存单元不好记忆且容易出错,我们可以通过寄存器的方式来操作。
例:通过寄存器别名方式访问内存单元
1 // GPIOB 端口全部输出 高电平
2 #define GPIOB_OD (unsigned int*) (GPIOB_BASE+0x0C)
3 * GPIOB_ODR = 0xFF;
为了方便操作,我们干脆把指针操作“*”也定义到寄存器别名里面。
例:通过寄存器别名访问内存单元
1 // GPIOB 端口全部输出 高电平
2 #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
3 GPIOB_ODR = 0xFF;
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103VET6 型号的芯片有 GPIOA、GPIOB、GPIOC 至 GPIOE 共 5 组 GPIO,芯片一共 100 个引脚,其中 GPIO就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。
通过 GPIO 硬件结构框图,就可以从整体上深入了解 GPIO 外设及它的各种应用模式。该图从最右端看起,最右端就是代表 STM32 芯片引出的 GPIO 引脚,其余部件都位于芯片内部。
图中编号1-7分别为保护二极管及上下拉电阻、P-MOS管和N-MOS管、输出数据寄存器、复用功能输出、输入数据寄存器、复用功能输入、模拟输入输出。其中每个编号对应的部分功能及介绍参考STM32F103使用指南。
GPIO的结构决定了GPIO有以下的几种模式,这几种模式也相应地体现在固件库的GPIO当中
1 typedef enum 2 {
3 GPIO_Mode_AIN = 0x0, // 模拟输入
4 GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
5 GPIO_Mode_IPD = 0x28, // 下拉输入
6 GPIO_Mode_IPU = 0x48, // 上拉输入
7 GPIO_Mode_Out_OD = 0x14, // 开漏输出
8 GPIO_Mode_Out_PP = 0x10, // 推挽输出
9 GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
10 GPIO_Mode_AF_PP = 0x18 // 复用推挽输出 11
} GPIOMode_TypeDef
但总体来说GPIO的八种工作模式大致可以分为以下三类:
(1)输入模式(模拟/浮空/上拉/下拉)
在输入模式时,施密特触发器打开,输出被禁止,可通过输入数据寄存器 GPIOx_IDR读取 I/O 状态。其中输入模式,可设置为上拉、下拉、浮空和模拟输入四种。上拉和下拉输入很好理解,默认的电平由上拉或者下拉决定。浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。
施密特触发器的作用是用于在现实中信号杂乱无章的抖动中判断高低电频
(2)输出模式(推挽/开漏)
在输出模式中,推挽模式时双 MOS 管以轮流方式工作,输出数据寄存器 GPIOx_ODR可控制 I/O 输出高低电平。开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O输出高阻态或低电平。输出速度可配置,有 2MHz\10MHz\50MHz 的选项。此处的输出速度即 I/O 支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。
在输出模式时施密特触发器是打开的,即输入可用,通过输入数据寄存器 GPIOx_IDR可读取 I/O 的实际状态。
(3)复用功能(推挽/开漏)
复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器 GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取 I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号。
通过对 GPIO 寄存器写入不同的参数,就可以改变 GPIO 的工作模式,再强调一下,要了解具体寄存器时一定要查阅《STM32F10X-中文参考手册》中对应外设的寄存器说明。在 GPIO 外设中,控制端口高低控制寄存器 CRH 和 CRL 可以配置每个 GPIO 的工作模式和工作的速度,每 4 个位控制一个 IO,CRH 控制端口的高八位,CRL 控制端口的低 8 位,具体的看 CRH 和 CRL 的寄存器描述。
官网下载
以管理员身份运行安装程序,点击next。
点击"I accept the terms of this license agreement",接着选择Next:勾选第一个即可,第二个选项是是否同意ST公司收集你的个人使用信息等:
选择安装位置,默认位置是安装在C盘中(注意:安装位置不要出现中文):
点击确定
直接点NEXT,其他不用设置 之后开始安装:
安装完成,点Done退出
**HAL库介绍:**STM32 HAL固件库是Hardware Abstraction Layer的缩写,中文名称是:硬件抽象层。HAL库是ST公司为STM32的MCU最新推出的抽象层嵌入式软件,为更方便的实现跨STM32产品的最大可移植性。HAL库的推出,可以说ST也慢慢的抛弃了原来的标准固件库,这也使得很多老用户不满。但是HAL库推出的同时,也加入了很多第三方的中间件,有RTOS,USB,TCP / IP和图形等等。
和标准库对比起来,STM32的HAL库更加的抽象,ST最终的目的是要实现在STM32系列MCU之间无缝移植,甚至在其他MCU也能实现快速移植。
并且从16年开始,ST公司就逐渐停止了对标准固件库的更新,转而倾向于HAL固件库和 Low-layer底层库的更新,停止标准库更新,也就表示了以后使用STM32CubeMX配置HAL/LL库是主流配置环境;
安装步骤:
打开安装好的STMCubeMX
点击HELP->Manage embedded software packages :
会跳出来一个选择型号界面 勾选上你要安装的HAL库, 点击“Install Now” 直到安装成功。 如下图:
在part name里选择自己的芯片,点击信息栏中的具体芯片信息选中,点击start project
点击system core,进入SYS,在debug下选择serial wire:
配置时钟,进入上面的rcc,有两个时钟,一个是hse和lse,我们要用是GPIO接口,而这些接口都在APB2里
接下来观察时钟架构,APB2总线的时钟由hse控制,同时在这个界面得把PLLCLK右边选上:
再将hse那里设为Crystal/Ceramic Resonator:
接下来就是点击相应的引脚设置输出寄存器了,就是output那一项,一共选了三个,是PA5,PB9,PC14:
点击project manager,配置好自己的路径和项目名,然后IDE那项改为MDK-ARM:
进入 code generate界面,选择生成初始化.c/.h文件,后面点击generate code,选择open project,然后就到KEIL5了
在上一步中我们已经打开了通过STM32CubeMX新建好的项目,并在MDK-Keil5中打开。
打开main.c文件,滑倒主函数那一部分:
将下面代码放入主函数中(替代上图框2里面的内容)
SystemClock_Config();//系统时钟初始化
MX_GPIO_Init();//gpio初始化
while (1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//PA4亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);//PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);//PC15熄灯
HAL_Delay(1000);//延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//PA4熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);//PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);//PC15亮灯
HAL_Delay(1000);//延时1s
}
烧录程序,在build之后会在object文件夹下有对应的hex文件生成。点击第一个图标进行编译,点击load图标进行烧录。
之后就会生成.hex后缀的文件
将连接好的电路连接用USB TO TTL连接到电脑打开mcuisp选择刚刚生成的.hex文件。并检查以下框选。
点击开始编译,会有下载成功的提示:
整理布线过后
Target界面中,选择跟正确的晶振大小,我使用的是8MHz的外部晶振。这个选项在软件仿真中起到很重要的作用,如果选择错误,那么波形一定是错误的,因为时间不准确。
①点击Setup Logic Analyzer