首页 > 其他分享 >寄存器与库函数介绍

寄存器与库函数介绍

时间:2022-12-23 09:25:22浏览次数:87  
标签:总线 介绍 地址 GPIOA 寄存器 外设 库函数

一、寄存器

1、认识存储器

存储器使用类型可分为只读存储器(ROM)随机存储器(RAM)

存储器是许多存储单元的集合,主要是用来存储程序和各种数据信息的部件。

2、存储器映射

存储器本身是不具有地址的,是一块具有特定功能的内存单元,它的地址是由芯片厂商或用户配,给存储其分配地址的过程就叫做存储区映射

给内存单元分配地址之后,就可以通过指针去操作内存地址

3、存储映射表

GD32是一个32位单片机,它的地址范围位 2 的 32 次方,也就是 4GB 的地址空间

2^32bit = 2^(2+10+10+10)bit = 4 * 1024 * 1024 * 1024bit

为了降低不同客户在相同应用时的软件复杂度,存储映射是按 Cortex-M4 处理器提供的规则预先定义的。在存储器映射表中,一部分地址空间由 Arm Cortex-M4 的系统外设所占用,且不可更改。其余部分地址空间可由芯片供应商定义使用。

关于存储映射表的内容,大家可以去查看用户手册的第38页。

8位16进制,即8*4=32位。

4、寄存器映射

寄存器是具有特定功能的内存单元,通过操作这些内存单元可以驱动外设工作。

寄存器按功能又可分为指令寄存器、地址寄存器和数据寄存器,处理器可以使用相互独立的总线来读取指令和加载/存储数据。

程序存储器,数据存储器,寄存器和I / O端口都在同一个线性的 4 GB 的地址空间之内。

每一个寄存器都对应不同的功能,操作相应的寄存器就可以配置不同的功能。

如果我们要控制某个外设工作,那我们可以找到这个单元的起始地址,然后通过c语言指针的方式来访问这些内存单元。

但通常我们会给这个特殊的内存单元取一个名字,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射,这个别名就是我们所说的寄存器。

寄存器重映射:给寄存器再分配一个地址的过程叫做寄存器重映射。

5、总线基地址

系统不同模块间的通信靠总线

总线的工作方式就是将数据和地址从一个外设搬到另一个外设上。

片上外设区域分为三条总线,分别为AHB总线、APB1总线和APB2总线。

AHB总线最高时钟可达200MHZ;

APB1总线最高时钟可达50MHZ;

APB2总线时钟最高可达100MHZ。

根据外设速度的不同,不同的总线挂载着不同的外设。

总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。

关于总线上挂载的外设更多详细信息请查看数据手册的第11页。

总线基地址和地址范围如下:

总线名称 总线基地址 总线地址范围
APB1 0x4000 0000 0x4000 0000 - 0x4000 FFFF
APB2 0x4001 0000 0x4001 0000 - 0x4001 FFFF
AHB1 0x4002 0000 0x4002 0000 - 0x4FFF FFFF
AHB2 0x5000 0000 0x5000 0000 - 0x5FFF FFFF

6、外设基地址

每个总线上都挂载着很多外设,这些外设也都有自己的地址范围。

关于GPIO这个外设的地址映射信息如下表所示。

外设名称 外设基地址 相对于 AHB1 总线的地址偏移
GPIOA 0x4002 0000 0x0000 0000U
GPIOB 0x4002 0400 0x0000 0400U
GPIOC 0x4002 0800 0x0000 0800U
GPIOD 0x4002 0C00 0x0000 0C00U
GPIOE 0x4002 1000 0x0000 1000U
GPIOF 0x4002 1400 0x0000 1400U
GPIOG 0x4002 1800 0x0000 1800U
GPIOH 0x4002 1C00 0x0000 1C00U
GPIOI 0x4002 2000 0x0000 2000U

7、外设寄存器地址

在外设的地址范围内,分布着该外设的寄存器。

以GPIO外设为例,GPIO外设地址范围内有很多个寄存器,每一个都有特定的功能,通过操作对应的寄存器来配置GPIO的功能(按照芯片手册,向其中输入不同功能对应的数值)。每个寄存器都为32位,占4个字节,这里我们以GPIOA端口的寄存器进行介绍。

寄存器名称 地址偏移 相对于 GPIOA 的地址
GPIOx_CTL 0x00 GPIOA_BASE + 0x00U
GPIOx_OMODE 0x04 GPIOA_BASE + 0x04U
GPIOx_OSPD 0x08 GPIOA_BASE + 0x08U
GPIOx_PUD 0x0C GPIOA_BASE + 0x0CU
GPIOx_ISTAT 0x10 GPIOA_BASE + 0x10U
GPIOx_OCTL 0x14 GPIOA_BASE + 0x14U
GPIOx_BOP 0x18 GPIOA_BASE + 0x18U
GPIOx_LOCK 0x1C GPIOA_BASE + 0x1CU
GPIOx_AFSEL0 0x20 GPIOA_BASE + 0x20U
GPIOx_AFSEL1 0x24 GPIOA_BASE + 0x24U
GPIOx_BC 0x28 GPIOA_BASE + 0x28U
GPIOx_TG 0x2C GPIOA_BASE + 0x2CU

根据不同外设,可设置相对于基地址的地址偏移。

8、如何操作寄存器

如果如我们想让GPIOA端口的16个引脚都置1,我们需要去配置端口输出寄存器GPIOx_OCTL。通过查找用户手册177页可以知道这个寄存器的地址偏移量为0x14,GPIOA端口的基地址为0x4002 0000,所以GPIOA_OCTL寄存器的地址为0x4002 0000 + 0x14 = 0x4002 0014,那我们就是对这个地址进行操作。操作如下:

通过上图可以了解到要想使能所有引脚配置为1,只需要将OCTLy(y=0..15)对应的位置1即可。也就是配置GPIOA_OCTL寄存器的高16位为0(高位保留),低16位为1(进行配置),换成十六进制就是 0x0000FFFF。

8.1、通过绝对地址访问内存单元

说明:

(unsigned int*) 的作用是将 0x40020014 这个立即数强制类型转化为无符号整形地址,告诉编译器这是一个地址。

(unsigned int)(0x40020014) 相当于绝对地址,也就是对这个地址对应的内存空间的值,(unsigned int*)(0x40020014) = 0xFFFF; 就是相当于对 0x40020014 这个内存空间赋值为 0xFFFF

8.2、通过别名访问内存单元

通过上面的方式确实可以对寄存器地址进行操作,但是操作起来很麻烦,用户也不能清晰的明白这个地址对应的功能。

如果我们给每个地址都起一个名字,这样看到名字就知道这个地址对应什么功能.

二、库函数

1、为什么要使用库函数

从上一节我们了解到如何去用寄存器驱动外设,但我们也同时了解到GD32的寄存器数量非常多,这么多的寄存器光是定义就需要花费很多的时间,更不用说还要去查找对应的功能,找到对应的地址,然后配置需要的值,这在难度和时间上都是不可取的。为此,库函数就在这种情况下应运而生,库函数能使我们的开发效率大大提高。

关于GD32的标准外设库函数,GD32的官方已经给我们开发好了,我们只需要移植到我们的工程使用即可。库函数的使用不需要让我们去了解硬件的机制,只需要根据需要的功能去查找对应的函数,然后调用即可,大大降低了开发要求。

2、如何操作库函数

让GPIOA端口的16个引脚都置1,那么使用库函数该如何配置呢?首先,我们知道我们操作的功能是关于GPIO的,那么首先要从GPIO外设库中去寻找。打开gd32f4xx_gpio.h头文件,拉到最下面可以看到所有声明的有关GPIO的API(Application Programming Interface,程序间接口)函数。我们是让GPIOA的16个端口都置1,也就是对端口进行操作,我们寻找对应的功能函数,从函数名可以知道gpio_port_write这个函数是对端口进行写数据操作,我们对16个引脚全部置1不就是相当于对GPIOA这个端口写入数据0xFFFF吗。可见这个功能函数是我们需要的。

根据之前查找固件库手册获悉的函数原型:

void gpio_port_write(uint32_t gpio_periph,uint16_t data);

所以可用如下语句进行配置:

具体实现:

首先是调用 gpio_port_write(GPIOA,0xFFFF);

我们进入gpio_port_write函数看一下,这个函数下只写了一行代码GPIO_OCTL(gpio_periph) = (uint32_t)data;

把我们的参数带进入,即为 GPIO_OCTL(GPIOA) = (uint32_t)0xFFFF; 那我们再看一下 GPIO_OCTL(GPIOA) 是个什么东西,通过跳转可以知道有这么一个宏定义 #define GPIO_OCTL(gpiox) REG32((gpiox) + 0x14U),带入参数即为 #define GPIO_OCTL(GPIOA) REG32((GPIOA) + 0x14U)

把GPIOA的地址带入即为 REG32(0x40020014) = (uint32_t)0xFFFF

gd32F4xx.h 头文件中有这么一个宏定义

#define REG32(addr) (*(volatile uint32_t )(uint32_t)(addr))

REG32(addr) 替换掉就是 ((volatile uint32_t *)(uint32_t)(0x40020014)) = (uint32_t)0xFFFF; 看到这个是不是很熟悉了,这不就是我们上面用寄存器去配置GPIOA端口16个引脚全部置1的例子吗。

说白了,其实库函数就是在寄存器的基础上又封装了一层,使操作起来更简单,最后还是通过寄存器来实现的。

三、寄存器和库函数的区别

  • 寄存器更能理解原理,更直观,库函数相对来说屏蔽底层,直接面向应用。
  • 使用库函数较寄存器代码量会增大,库函数会把所有情况都考虑到函数里,有时会造成代码的冗余。
  • 库函数使用起来相对简单,容易上手,可快速开发应用,大大提高效率。
  • 寄存器占用内存少,速度快,在资源有限或者要求执行速度的情况下寄存器是一个不错的选择。
=========================

转载: U羊U

标签:总线,介绍,地址,GPIOA,寄存器,外设,库函数
From: https://www.cnblogs.com/lxd-koi/p/16995243.html

相关文章

  • Arthas - 基础介绍,Arthas 能解决什么问题
     一、Arthas能解决什么问题   二、在Linux系统安装 2.1离线安装通过Maven地址,下载zip包,然后解压后找到.arthas目录,找到arthas-boot.jar,运行命令即可java......
  • 不同存储资源的应用场景及优缺点介绍
    容器应用应当根据应用系统的特点,综合考虑容器应用对存储类型、存储性能及数据高可用等方面的要求,选择最适合的存储资源类型。常见的存储资源应用场景包括三类:将存储挂载在外......
  • 不同存储资源的应用场景及优缺点介绍
    容器应用应当根据应用系统的特点,综合考虑容器应用对存储类型、存储性能及数据高可用等方面的要求,选择最适合的存储资源类型。常见的存储资源应用场景包括三类:将存储挂载在......
  • 信而泰RENIX 802.1ag功能介绍-网络测试仪实操
    一、EOAM概述1.以太网1.1以太网优点简单易用价格低廉高拓展性大势所趋,一统天下1.2以太网缺点可管理性差定位故障手段少定位故障速度慢维护成本高2.以太网OAM■EOAM为运营商......
  • CPU是寄存器的集合体
    1.程序是把寄存器当作对象来描述的。2.汇编语言采用助记符来编写程序。3.机器语言是指CPU可以直接解释和执行的语言。  通过上面这个代码例子可以看出:机器语言级别......
  • VUE3状态管理Pinia使用介绍
    vue3中推荐使用的状态管理工具:pinia,真的很好用官方文档,中文文档一、安装piniayarnaddpinia#或者使用npmnpminstallpinia二、src文件夹下新建store文件夹,并新建......
  • 20221215 2. k8s 介绍
    kubernetes与dockerswarm对比DockerSwarmKubernetes开发者Docker公司谷歌发布年份20132014ControllerManagerMasterStorageVolumesPers......
  • jmeter目录结构的简单介绍-自动化测试
    一、bin目录Examples:目录中有CSV样例jmeter.bat:windows的启动文件jmeter.log:jmeter运行日志文件jmeter.sh:linux的启动文件jmeter.properties......
  • 嵌入式:ARM多寄存器存取指令详解
    多寄存器传送指令可以用一条指令将16个可见寄存器(R0~R15)的任意子集合(或全部)存储到存储器或从存储器中读取数据到该寄存器集合中。如:可将寄存器列表保存到堆栈,也可将寄存器列......
  • Mobtech 秒验应用介绍
    一、传统APP手机注册登录验证的弊端1、注册过程输入的信息过多,耗费时间长。用户体验感较差。2、传统手机绑定需要通过验证码验证手机真实性,容易被批量注册。3、如果手机A......