历史上的今天
返回首页

历史上的今天

今天是:2024年11月09日(星期六)

正在发生

2021年11月09日 | 存储器与寄存器(2)

2021-11-09 来源:eefocus

一、存储器映射

1.1、存储器映射

存储器本身不具备地址信息,它的地址是由芯片厂商或用户分配。


给存储器分配地址的过程叫出存储器映射。如果在分配一个地址则叫重映射。

1.2、存储区域划分

开发版4G容量的ARM按功能划分表

Block0、Block1、Block2包含了STM32芯片内部Flash、RAM和片上外设。


Block0 主要用于设计片内的 FLASH,STM32F103 系列芯片内部 FLASH 最大是 512KB,STM32F103ZET6的FLASH也是512KB。


1.2.1、Block0内部区域功能划分

0x0000 0000-0x0007 FFFF:取决于 BOOT 引脚,为 FLASH、系统存储器、SRAM 的别名。

0x0008 0000-0x07FF FFFF:预留。

0x0800 0000-0x0807 FFFF:片内 FLASH,我们编写的程序就放在这一区域(512KB)。

0x0808 0000-0x1FFF EFFF:预留。

0x1FFF F000-0x1FFF F7FF:系统存储器,里面存放的是 ST 出厂时烧写好的isp 自举程序,用户无法改动。使用串口下载的时候需要用到这部分程序。


0x1FFF F800-0x1FFF F80F:选项字节,用于配置读写保护、BOR 级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。当芯片不小心被锁住之后,我们可以从 RAM 里面启动来修改这部分相应的寄存器位。


0x1FFF F810-0x1FFF FFFF:预留


1.2.2、 Block1内部区域功能划分

Block1用于设计片内的SRAM,我们使用的 STM32F103ZET6 的 SRAM是64KB。


0x2000 0000-0x2000 FFFF:SRAM,容量为 64KB。

0x2001 0000-0x3FFF FFFF:预留。


1.2.3、 Block2 内部区域功能划分

Block2 用于设计片内外设,根据外设总线速度的不同,Block2 被划分为 AHB和 APB 两部分,APB 又被分成 APB1 和 APB2 总线。

0x4000 0000-0x4000 77FF:APB1 总线外设。

0x4000 7800-0x4000 FFFF:预留。

0x4001 0000-0x4001 3FFF:APB2 总线外设。

0x4001 4000-0x4001 7FFF:预留。

0x4001 8000-0x4002 33FF:AHB 总线外设。

0x4002 4400-0x5FFF FFFF:预留。

在Block3/4/5中还包含了FSMC扩展区域,这3个块可用于扩展外部存储器,比如 SRAM,NORFLASH 和 NANDFLASH 等。


二、什么是寄存器和寄存器映射

2.1、概述

ARM按功能划分为八个功能区域,Cortex-M3内核是32位、因此存储器内部是以四个字节为一个单元。


把每个单元的功能作为名,给这个内存取一个别名,这个别名就是我们经常说的寄存器。然后通过 C语言指针来操作这些寄存器即可。


实现特定功能的内存即寄存器

给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。


三、如何访问stm32寄存器内容

寄存器就是一些有特定功能的内存单元,所以要访问 STM32 寄存器也就是操作 STM32 的内存单元,根据 C 语言指针的特点,可以使用指针来操作STM32 的内存单元。


3.1、stm32外设地址映射

片上外设区分为四条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1 挂载低速外设,APB2 和 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。APB1总线的地址最低,因此片上外设就从这这个地址开始,也称外设基地址。


3.1.1、总线基地址

从存储器映射那张图的 Block2 可以看到,分为 4 大块,每块都有一个起始地址,这个起始地址就是基地址,然后到下一块起始地址的时候就会和前一块地址出现偏差,这个差值就是偏移量,即相对基地址的偏移量。

从上图可以看到 APB1 总线基地址是 0x4000 0000,相对外设基地址的偏移量是 0,所以此总线也是外设 Block2 的基地址。


3.1.2、外设基地址

每条总线上都会挂接着很多的外设、这些外设也会有自己的地址范围XXX外设的首个地址即最低地址就是 XXX 外设的基地址,也称作 XXX 边界地址。


GPIO外设地址

外设 GPIOx 都是挂接在 APB2 总线上的,属于高速的外设。而 APB2 总线的基地址是 0x4001 0000,故 GPIOA 的相对 APB2 总线的地址偏移是800。

3.1.3、外设寄存器地址

XXX 外设的寄存器就分布在其对应的外设地址范围内。以 GPIO 外设为例,GPIO 是通用输入输出端口的简称,可以通过软件来控制其输入和输出。GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为 32bit,占四个字节,这些寄存器都是按顺序依次排列在外设的基地址上。寄存器的位置都以相对该外设基地址的偏移地址来描述。

1号红色框表示的是相对 GPIOx 地址的偏移值,比如现在我们使用的是GPIOB 外设,其基地址是 0x4001 0C00,那么本寄存器 GPIOx_BSRR 地址=0x4001 0C00+0x10=0x4001 0C10。对于其他的 GPIO 外设也是一个原理。


2号、3号红色框表示的是寄存器的位表。其中2 表示寄存器编号,因为一个寄存器是 32bit,所以范围是 0-31。3 表示的是相应位的权限,w:只写,r:只读,rw:可读可写。本寄存器位权限是 w,所以只能写,如果试图读本寄存器,是无法保证读取到它真正内容的。而有的寄存器位权限为只读,一般是用于表示STM32 外设的某种工作状态的,由 STM32 硬件自动更改,通过读取那些寄存器位来判断外设的工作状态。


4号红色框  是寄存器位功能说明。这个也是寄存器说明中最重要的部分,它详细介绍了寄存器每一个位的功能。例如本寄存器中有两种寄存器位,分别为BRy 及 BSy,其中的 y 数值表示的是管脚号,可以是 0-15。如 BR0、BS0 用于控制 GPIOx 的第 0 个引脚,若 x 表示 GPIOB,那就是控制 GPIOB 的第 0 引脚,而 BR1、BS1 就是控制 GPIOB 第 1 个引脚。其中 BRy 引脚的说明是“0:不会对相应的ODRx 位执行任何操作;1:对相应 ODRx 位进行复位”。这里的“复位”是将该位设置为 0 的意思,而“置位”表示将该位设置为 1;说明中的 ODRx 是另一个寄存器的寄存器位,我们只需要知道 ODRx 位为 1的时候,对应的引脚 x 输出高电平,为 0 的时候对应的引脚输出低电平即可(感兴趣的读者可以查询该寄存器 GPIOx_ODR 的说明了解)。所以,如果对 BR0 写入“1”的话,那么 GPIOx 的第 0 个引脚就会输出“低电平”,但是对 BR0 写入“0”的话,却不会影响 ODR0 位,所以引脚电平不会改变。要想该引脚输出“高电平”,就需要对“ BS0”位写入“ 1”,寄存器位 BSy 与 BRy 是相反的操作。


3.2、使用c语言封装寄存器

//定义外设基地址

#define PERIPH_BASE ((unsigned int)0x40000000) 

 

//定义 APB2 总线基地址

#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) 

 

//定义 GPIOB 外设基地址

#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) 

 

//定义寄存器基地址  使用C语言宏定义对GPIO端口寄存器的地址进行定义

 

// 两个32位的配置寄存器

#define GPIOC_CRL   *(unsigned int*)(GPIOC_BASE+0x00)    //端口配置低位寄存器

#define GPIOC_CRH   *(unsigned int*)(GPIOC_BASE+0x04)    // 端口配置高位寄存器

//两个32位数据寄存器

#define GPIO_IDR    *(unsigned int*)(GPIOC_BASE+0x08)    // 端口输入数据寄存器

#define GPIOC_ODR   *(unsigned int*)(GPIOC_BASE+0x0C)    //端口输出数据寄存器

//一个32位置位/复位寄存器

#define GPIO_BSRR   *(unsigned int*)(GPIOC_BASE+0x10)    // 端口设置/清除寄存器

//复位寄存器

#define GPIO_BRR    *(unsigned int*)(GPIOC_BASE+0x14)    // 端口清除寄存器

//一个32位锁定寄存器

#define GPIO_LCKR   *(unsigned int*)(GPIOC_BASE+0x18)   // 端口配置锁定寄存器

无符号整型(unsigned  int)

(1)整型是4个字节(有些编译器不同,可能会是2个),即32位,无符号整型当然也为32位。

(2)既然是32位,无符号整型的取值是32个0~32个1,即:0~4294967295

//控制 GPIOB 第 5 管脚输出一个低电平

 *(unsigned int *)GPIOB_BSRR = (0x01<<(16+5));

该寄存器的 16-31 位是 BR0-BR15 用来设置对应 GPIO0-GPIO15 为低电平,写1 有效,写 0 无效。所以要控制 GPIO5 输出低电平即对该寄存器中的 BR5 位写 1,而 BR5 是在该寄存器高 16 位的第 5 位(从 0 开始数),因此才会有 0x01<<(16+5)。式中“16”表示操作的是该寄存器的高 16 位,“5”表示高 16 位中的哪一位。然后把 1 左移到对应的操作寄存器位上即可。


控制GPIOC引脚0输出低电平(即通过BSRR 寄存器的BR0置1)

*(unsigned int*)GPIOC_BSRR=(0x01<<(16+0))

 

控制GPIOC引脚0输出高电平(即通过BSRR 寄存器的BSO置1)

*(unsigned int *)GPIOC_BSRR=0x01<<0

 

读取GPIOC端口所有引脚的电平(读IDR 寄存器)

unsigned int temp;

temp =*(unsigned int *)GPIOC_IDR

 GPIOB_BSRR 的值是这个寄存器的地址,但是编译器不知道它是地址,而是把它当做立即数,所以我们必须要强制转换为(unsigned int *)指针类型才可以对其操作,这一点特别要注意。然后再在前面加上一个“*”作取指针操作,表示对该地址内内容进行写。


3.3、寄存器封装

typedef unsigned int uint32_t; //无符号 32位变量

typedef unsigned short int uint16_t; //无符号16位变量

//GPIO 寄存器列表

typedef struct

{

uint32_t CRL; /*GPIO 端口配置低寄存器 地址偏移: 0x00 */

uint32_t CRH; /*GPIO 端口配置高寄存器 地址偏移: 0x04 */

uint32_t IDR; /*GPIO 数据输入寄存器 地址偏移: 0x08 */

uint32_t ODR; /*GPIO 数据输出寄存器 地址偏移: 0x0C */

uint32_t BSRR; /*GPIO 位设置/清除寄存器 地址偏移: 0x10 */

uint32_t BRR; /*GPIO 端口位清除寄存器 地址偏移: 0x14 */

uint16_t LCKR; /*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */

 

}GPIO_TypeDef

 

引入C语言中的结构体对寄存器进行封装

这段代码用 typedef 关键字声明了名为 GPIO_TypeDef 的结构体类型,结构体内有 7 个成员变量,变量名正好对应寄存器的名字。C 语言的语法规定,结构体内变量的存储空间是连续的,其中 32 位的变量占用 4 个字节,16 位的变量占用 2 个字节。


我们定义的这个 GPIO_TypeDef ,假如这个结构体的首地址为0x4001 0C00(这也是第一个成员变量 CRL 的地址),那么结构体中第二个成员变量 CRH 的地址即为 0x4001 0C00 +0x04 ,加上的这个 0x04 ,正是代表 CRL所占用的 4 个字节地址的偏移量,其它成员变量相对于结构体首地址的偏移,在上述代码右侧注释已给出。


这样的地址偏移与 STM32 GPIO 外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器了,比如我们还是将GPIOB5 输出低电平。


GPIO_TypeDef * GPIOx; //定义一个 GPIO_TypeDef 型结构体指针 GPIOx

GPIOx = GPIOB_BASE; //把指针地址设置为宏 GPIOB_BASE 地址

GPIOx->BSRR =(1<<(16+5)); //通过指针访问并修改 GPIOB_BSRR 寄存器

这段代码先用 GPIO_TypeDef 类型定义一个结构体指针 GPIOx,并让指针指向 GPIOB 基地址 GPIOB_BASE,地址确定下来,然后根据 C 语言访问结构体的内容,用 GPIOx->BSRR 写寄存器。为了操作更简便灵活,我们直接使用宏定义好GPIO_TypeDef 类型的指针,而且指针指向各个 GPIO 端口的首地址,使用时我们直接用该宏访问寄存器即可。


#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)

#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)

#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)

#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)

#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)

GPIOB->BSRR = (1<<(16+5));


以 GPIO 这个外设为例,何使用 C 语言对寄存器封装。


使用 ST 公司提供的固件库,他们把 STM32 所有外设都已经封装好了,我们只需要调用即可。

推荐阅读

史海拾趣

EPT公司的发展小趣事

EPT深知产品质量是企业发展的基石。因此,公司不仅通过了ISO9001:2015和BSCI等国际质量管理体系认证,还获得了CE、UL、IEC62133、KC等多项证书。此外,EPT还为所有电池购买了产品责任险,为客户提供双重保障。这一举措不仅提升了客户对EPT产品的信任度,也为其在电子行业中树立了良好的品牌形象。

DESIGNERSYSTEMS公司的发展小趣事

DESIGNERSYSTEMS公司的创始人在电子行业有着深厚的背景和独特的见解。他们发现市场上缺乏一种能够集成多种功能、高度定制化的电子设备设计解决方案。于是,他们创立了DESIGNERSYSTEMS,专注于提供从概念到原型再到量产的全方位设计服务。通过不断的技术创新和对市场需求的精准把握,DESIGNERSYSTEMS迅速在电子行业树立了自己的品牌形象,赢得了客户的信赖。

FOX [Fox Electronics]公司的发展小趣事

随着全球对环保和可持续发展的重视,Fox Electronics积极响应行业趋势,将绿色生产理念融入企业运营中。公司投资引进先进的环保设备和生产工艺,减少生产过程中的废弃物排放和能源消耗。同时,Fox Electronics还推出了一系列符合RoHS(限制有害物质)指令的产品,满足市场对环保产品的需求。这些举措不仅提升了公司的社会责任感,也为其在绿色电子市场赢得了良好的声誉。

Actel公司的发展小趣事

面对物联网、5G通信和汽车电子等新兴技术的快速发展,Fox Electronics敏锐地洞察到未来市场的变革趋势。公司加大了对高频、低抖动和高精度时钟解决方案的研发力度,以满足新兴应用对时钟源的高要求。同时,Fox Electronics还积极布局汽车电子市场,开发适应汽车自动驾驶和车联网需求的专用时钟产品。通过不断创新和布局未来市场,Fox Electronics为自身的持续发展奠定了坚实基础。

请注意,以上故事均为虚构内容,旨在展示电子行业发展的某些方面和可能的趋势。实际上,Fox Electronics的具体发展历程和成就可能有所不同。

對餘科技(DIOFIT)公司的发展小趣事

DIOFIT公司自创立之初,便面临着激烈的市场竞争和技术挑战。创始人团队凭借对电子技术的深入理解和热情,决心打造一家专注于高端电子产品研发和生产的企业。在资金紧张、人才匮乏的情况下,他们坚持自主研发,不断优化产品设计,最终成功推出了一款具有创新性的智能家居控制系统,赢得了市场的认可,为公司的发展奠定了坚实的基础。

ADDtek公司的发展小趣事

DIOFIT公司一直秉持着品质至上的生产理念,注重产品质量和用户体验。他们严格控制原材料采购和生产流程,确保每一款产品都符合高标准的质量要求。同时,DIOFIT公司还建立了完善的售后服务体系,及时解决用户在使用过程中遇到的问题,赢得了用户的信赖和好评。

问答坊 | AI 解惑

利用ADC0804做一个真正的电压表

本帖转载自www.mcusy.com,作者月夜。…

查看全部问答>

学习学习GPS

GSV GSA GGA分别是什么 a.GPS固定数据输出语句($GPGGA)这是一帧GPS定位的主要数据,也是使用最广的数据。$GPGGA 语句 包括17个字段:语句标识头,世界时间,纬度,纬度半球,经度,经度半球,定位质量指示,使用卫星数量, 水平精确度,海拔高 ...…

查看全部问答>

LED户外大屏幕基础知识

replyreload += \',\' + 374037;Timson,如果您要查看本帖隐藏内容请回复…

查看全部问答>

周公M0新原理图

周公的M0开发板的原理图终于更新了…

查看全部问答>

关于控制USB外设的问题

我是第一次作这方面的东西: 我感觉:首先要判断次外设的存在(包括他的插入于拔除)         向外设发送控制命令(包括向他提供数据)。 不知道思路是不是有问题,请各位给个思路。 …

查看全部问答>

MSP430F149的定时器A的连续计数模式的CCR0

MSP430F149的定时器A的连续计数模式的CCR0的作用?增计数模式是从0增加到CCR0就复位,增减计数模式是从0增加到CCR0,在减到0。可是连续计数模式呢?CCR0的作用?…

查看全部问答>

Linux 常用C函数(中文版)

很详细的c函数介绍文档,我一直把他当做工具书来用,分享给大家! 如果哪位大侠把这个网页式的工具做成chm格式的电子书就完美了,哈哈 截图,先睹为快 [ 本帖最后由 HOHO 于 2012-8-8 16:42 编辑 ]…

查看全部问答>

有没有M0核的视频教程

最近学习STM32 ,都是103的视频,谁能提供点M0核的…

查看全部问答>

【R7F0C089】+虽有小缺憾,但认真努力过

本帖最后由 人民币的幻想 于 2015-11-24 10:36 编辑 视频里演示测量心率时,使用手腕部分更能快速得到准确值。因此,后期考虑将心率做成手表形式。 …

查看全部问答>

负半周失真

我是按照晶体管设计书上绘制的,为什么会出错? …

查看全部问答>