地址和寄存器映射原理讲解与GPIO端口的初始化设置

一.STM32F103系列芯片的地址映射和寄存器映射原理

1.什么是寄存器

  根据百度百科介绍,现代的计算机主要包括三级存储,寄存器、内存储器和外存储器,存储数据的速率也依次递减,寄存器是中央处理器内的组成部分,是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。
 寄存器是 CPU 内部的构造,它主要用于信息的存储。简单来说,我们可以将寄存器比作存放东西的地方。从名字来看,跟火车站寄存行李的地方是有关系的,只不过火车站行李寄存处,存放的行李;而寄存器可能存放的是指令、数据或地址。
 存放数据的寄存器是最好理解的,如果你需要读取一个数据,直接到这个寄存器所在的地方来问问他,数据是多少就行了。问寄存器这个动作,叫做访问寄存器。不同的数据会存放在不同的寄存器,例如引脚PA2与PB8的高低电平数据(1或0)肯定放在不同的寄存器里,那么怎么区分不同的寄存器呢?通过地址,不同的寄存器有不同的地址,就像老张行李寄存处在101号店铺,老王行李寄存处在258号店铺。
 指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。
 
寄存器最起码具备以下4种功能。

①清除数码:将寄存器里的原有数码清除。
②接收数码:在接收脉冲作用下,将外输入数码存入寄存器中。
③存储数码:在没有新的写入脉冲来之前,寄存器能保存原有数码不变。
④输出数码:在输出脉冲作用下,才通过电路输出数码。
仅具有以上功能的寄存器称为数码寄存器;有的寄存器还具有移位功能,称为移位寄存器。

2.地址映射和寄存器映射原理

  • 地址映射:由百度词条可知为了保证CPU执行指令时可正确访问存储单元,需将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射。

  • 寄存器映射:在存储器的区域单元中,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。

 提到映射也许大家跟笔者一样脑海中第一反应是函数的映射,其实外围设备的内存映射原理是一样的,只不过左边的集体变成了CPU,右边的集合变成了外围设备,那条带箭头的线就是连接CPU和外设地址引脚的地址总线。
在这里插入图片描述
在这里插入图片描述

3.如何找寻寄存器地址

  由于数据手册中没有直接给出所有寄存器的地址,需要读者稍加计算。例如我们想读取PB3引脚的电平,该如何找到相关寄存器呢?
第一步,找到GPIOB的基地址
查阅数据手册可以发现,所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围内,如图所示:
在这里插入图片描述
第二步,找到端口输入寄存器的地址偏移
 找到存储数据的那个屋子,结论是0x4001 0C00+8 = 0x4001 0C08
 在这里插入图片描述
第三步,找到知道数据的那个人
PB3的数据位于从右往左数第4个。
在这里插入图片描述
 而这个寄存器的位数是32位(虽然高16位没有用到),这就是32位的单片机的意思。每个寄存器都占据4字节,32位。而CPU的总线一次可以操作32位,所以比8位单片机厉害一点。
经过这三步查找,我们可以做出以下结论
 PB3的输入数据位于0x4001 0C08这个地址上,这个地址上存放数据的右起第4个位就是PB3引脚对应的高低电平。
  我们可以简单粗暴地直接访问这个地址:

 unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;
 unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

二、GPIO端口的初始化设置步骤(时钟配置、输入输出模式设置、最大速率设置)

1.时钟配置

(1)什么是时钟

 stm32的时钟是由内部或外部振荡器产生的“频率”,而被人们形象的称为“系统时钟”。最大为72MHz换成周期T为:1/72MHz≈13.9ns,其决定了程序执行的速度,给芯片提供一个稳定的执行频率。

(2)为什么要用时钟

 因为耗电量,stm32功能强大,能做很多事,但与之同时带来的消耗也越严重,当stm32不引入时钟的话,就像51一样外设全开,相应耗电就很严重了,所以厂家(st公司)为了解决这个问题,引入了“时钟概念”,即使用哪个外设就给哪个外设时钟(频率),不使用的就关掉(不震荡)。此做法大大降低了功耗,续航持久。
 在51单片机中一个时钟把所有的都包了,而stm32的时钟是有分工的,并且每类时钟的频率不一样,因为没必要所有的时钟都是最高频率,只要够用就行,好比一个门出来水流大小,如果只要洗脸,但是出来的是和洪水一样涌出来的水,那就没必要了,消耗能源也多,所以不同的时钟也会有频率差别,或者在配置的时候可以配置时钟分频。
时钟配置

PLL的倍频因子:HSE * PLLMUL = 72M Hz
AHB的频率:72M Hz
APB2的频率:72M Hz
APB1的频率:36M Hz

2.输入输出模式设置

(1)GPIO简介

 GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为STM32F4IGT6型号的芯片有GPIOA、GPIOB、GPIOC至GPIOI共9组GPIO,芯片一共176个引脚,其中GPIO就占了一大部分,所有的GPIO引脚都有基本的输入输出功能。
 最基本的输出功能是由STM32控制引脚输出高、低电平,实现开关控制,如把GPIO引脚接入到LED灯,那就可以控制LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。

(2)工作模式

在这里插入图片描述
GPIO的工作模式主要有八种:4种输入方式,4种输出方式。

  • 输入方式为输入浮空,输入上拉,输入下拉,模拟输入
  • 输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出

(1)GPIO_Mode_AIN 模拟输入(应用ADC模拟输入,或者低功耗下省电),将IO口作为模拟输入接口,输入的可能是变化的值,接收外部的模拟信号输入
(2)GPIO_Mode_IN_FLOATING 浮空输入(浮空就是浮在半空,可以被其他物体拉上或者拉下,可以用于按键输入),复位上电的时候,引脚不确定电平的高低
(3) GPIO_Mode_IPD 下拉输入(IO内部下拉电阻输入),将IO口作为通用输入接口,只能输入0或者1,强制下拉,一般是为了输入强低电平
(4) GPIO_Mode_IPU 上拉输入(IO内部上拉电阻输入),将IO口作为通用输入接口,只能输入0或者1,强制下拉,一般是为了输入强高电平
(5) GPIO_Mode_Out_OD 开漏输出(带上拉或者下拉)要得到高电平状态需要上拉电阻才行,可以作为电流型驱动
(6) GPIO_Mode_AF_OD 开漏复用输出(带上拉或者下拉)复用功能,不只是单纯的作为输入输出,可以作为其他功能的引脚:串口、I2C、SPI,要得到高电平状态需要上拉电阻才行
(7) GPIO_Mode_Out_PP 推挽输出(带上拉或者下拉)IO 输出 0-接 GND, IO 输出 1 -接 VCC,读输入值是未知的,输出0,就一定是0,输出1就一定是1
(8) GPIO_Mode_AF_PP 推挽复用输出(带上拉或者下拉)复用功能,不只是单纯的作为输入输出,可以作为其他功能的引脚:串口、I2C、SPI,输出0,就一定是0,输出1就一定是1
(9)GPIO四种最大输出速度:2MHZ、25MHZ、50MHZ、100MHZ

GPIO8种工作模式

typedef enum
{
 GPIO_Mode_AIN = 0x0, // 模拟输入
 GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
 GPIO_Mode_IPD = 0x28, // 下拉输入
 GPIO_Mode_IPU = 0x48, // 上拉输入
 GPIO_Mode_Out_OD = 0x14, // 开漏输出
 GPIO_Mode_Out_PP = 0x10, // 推挽输出
 GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
 GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;

(3)输入和输出

输入 进行数据的采集,外部电路通过IO口输入模拟量,然后通过“TTL肖特基触发器”(肖特基触发器是将相对缓慢变化的模拟信号变成矩形信号,便于后面读取),进入输入数据寄存器,最后就能给CPU读取数据。
输出 GPIO的输出与51的 IO口是差不多的概念,都是输出高、低电平来控制外部电路:
处理过程:CPU下达输出高或低电平指令,指令配置“位设置/清除寄存器(GPIOx_BSRR)”(设置就是“1”高电平,清除就是“0”低电平),再由位寄存器配置输出数据寄存器(GPIOx_ODR),经过一个选择器(选择是一般输出还是复用功能输出),然后进行输出控制,控制是什么模式:推挽、开漏或者关闭,然后输出高或低电平到IO口。

a.输入模式

输入浮空(GPIO_Mode_IN_FLOATING)
输入上拉(GPIO_Mode_IPU)
输入下拉(GPIO_Mode_IPD)
模拟输入(GPIO_Mode_AIN)

输入浮空:浮空就是逻辑器件与引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。通俗讲就是浮空就是浮在空中,就相当于此端口在默认情况下什么都不接,呈高阻态,这种设置在数据传输时用的比较多。浮空最大的特点就是电压的不确定性,它可能是0V,页可能是VCC,还可能是介于两者之间的某个值(最有可能) 浮空一般用来做ADC输入用,这样可以减少上下拉电阻对结果的影响。

输入上拉模式:上拉就是把点位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平。电阻同时起到限流的作用。弱强只是上拉电阻的阻值不同,没有什么严格区分。

输入下拉:就是把电压拉低,拉到GND。与上拉原理相似。

模拟输入:模拟输入是指传统方式的输入,数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。
在这里插入图片描述
在这里插入图片描述

b.输出模式

  • 推挽输出模式:(最常用)

推挽电路是两个参数相同的三极管或 MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。

简单来说,当一个mos管电流增大时,另一个mos管的电流就减小,当P-MOS管饱和时,同时N-MOS截止,接通并输出Vdd及高电平“1”,反之,当N-MOS管饱和时,同时P-MOS截止,接通并输出Vss及低电平“0”。 就像是这两个mos管在相互“推、拉(挽)”,所以就叫推挽。

  • 开漏输出模式:(不常用)

输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

利用外部电路的驱动能力,减少 IC 内部的驱动。当 IC 内部 MOSFET 导通时,驱动电流是从外部的VCC 流经 R pull-up ,MOSFET到 GND。IC 内部仅需很少的栅极驱动电流。

  • 推挽、开漏复用模式:当GPIO口被用作第二功能时,需要配置,端口重映射AFIO。
    在这里插入图片描述
    在这里插入图片描述

3.最大速率设置

 GPIO的输出速率:GPIO电平每秒切换的最大次数, 单纯GPIO意义不大,不过在通讯方面对于GPIO是有要求的。GPIO口的驱动电路响应速度,不是输出信号的速度。输出信号的速度与程序有关,通过选择速度来选择不同的驱动电路,降低功耗控制噪声。
 这个输出速率主要体现I/O驱动电路的输出反应能力,通过选择不同的输出驱动速率,实现最佳的噪声与和功耗控制。不难理解,选择输出驱动速率越高,噪声也越大,相应的芯片功耗也会越大。所以对于这个输出频率的选择,不要太随意,合适就好。在满足应用的需求的前提下,就不要随意往高端速率选择。
  如STM32在GPIO配置时都需要选择一个GPIO速率,对于STM32F103系列芯片来说最快的配置应该是50Mhz左右。

4.实例

  • 单个GPIO端口
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

第二步:设置GPIOA参数:输出OR输入,工作模式,端口翻转速率
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8; //设定要操作的管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz

第三步:调用GPIOA口初始化函数,进行初始化。
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA

第四步:调用GPIO-SetBits函数,进行相应为的置位。
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出高


  • 多个GPIO端口
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA,GPIOE的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

第二步:设置GPIOA,GPIOE参数:输出OR输入,工作模式,端口翻转速率
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。

把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 第四个口,PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高

再设置GPIOE
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // 第三个口,PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高

三.总结

  GPIO初始化需要通过时钟配置、输入输出模式设置、最大速率设置三个步骤来实现。
GPIO初始化步骤

第一步:使能GPIOx口的时钟
第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式
第三步:调用GPIOx初始化函数进行初始化
第四步:调用GPIO-SetBits函数,进行相应位的位置


参考

1.https://blog.csdn.net/qq_46467126/article/details/120737655?spm=1001.2014.3001.5502
2.https://blog.csdn.net/geek_monkey/article/details/86291377
3.https://blog.csdn.net/cleveryoga/article/details/120927837

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值