STM32寄存器的简介、地址查找,与直接操作寄存器

什么是寄存器

提到单片机,就不得不提到寄存器。根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址
举个例子
  简单来说,寄存器就是存放东西的东西。从名字来看,跟火车站寄存行李的地方好像是有关系的。只不过火车站行李寄存处,存放的行李;寄存器可能存放的是指令、数据或地址。
  存放数据的寄存器是最好理解的,如果你需要读取一个数据,直接到这个寄存器所在的地方来问问他,数据是多少就行了。问寄存器这个动作,叫做访问寄存器。不同的数据会存放在不同的寄存器,例如引脚PA2与PB8的高低电平数据(1或0)肯定放在不同的寄存器里,那么怎么区分不同的寄存器呢?通过地址,不同的寄存器有不同的地址,就像老张行李寄存处在101号店铺,老王行李寄存处在258号店铺。
  指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。
  个人理解:给CPU存储东西的地方。等CPU转到寄存器这个地方的时候,就拿出寄存器里存放的东西,或是根据寄存器里的命令做一些事情。比如厨房就是个寄存器,负责做饭。仓库也是个寄存器,负责存东西。需要某些功能的时候,就要操作某个寄存器。可以把寄存器类比为,有特殊功能的地方,既然是个地方当然就有地址了,所以,可以把寄存器想象为特殊的地址。

怎么找到某个寄存器的地址?查看数据手册。

手册中没有直接给出所有的寄存器的地址,需要读者稍加计算。STM32给不同的寄存器分配了不同的地址,有点像划分了片区。在《STM32中文参考手册_V10》的第28页,有不同寄存器的地址范围。
  现在,假如我们想读取PB3引脚的电平,该怎么找到相关的寄存器?
第一步,找到GPIOB的基地址
  也就是找到GPIOB的小区。结论是,所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围内。
GPIOB的地址
第二步,找到端口输入寄存器的地址偏移
  找到存储数据的那个屋子,结论是0x4001 0C00+8 = 0x4001 0C08
GPIOB_IDR的地址偏移
第三步,找到知道数据的那个人
  PB3的数据位于从右往左数第4个。
引脚PB3的数据
  而这个寄存器的位数是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位
  • 1
  • 2

直接访问的操作并不好用,每操作一个寄存器就必须去查看数据手册,然后找找这个寄存器的地址。
  意法半导体公司为了方便大家使用,就把这些寄存器都起了一目了然的名字,把寄存器与地址映射关系放在他们提供的头文件里。这个文件就是stm32f10x.h。

直接操作寄存器来点亮LED。

我的板子对应的LED是PB8。

  • 首先要配置时钟使能。

为什么配置时钟?为了省电,默认的时钟都是关闭的。配置STM32的任何资源前,都必须首先使能时钟。
  配置哪个时钟?
时钟的信息在参考手册里边,参考手册十分巨大,不用通读,就像一个字典,需要什么查什么。
  参考手册,搜索"时钟",在表1里可以看到。
时钟控制名字叫做RCC,属于AHB总线。GPIOB属于APB2。
在这里插入图片描述
  下图系统结构可以看到时钟的从属关系,此图位于手册P25页,十分重要。可以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。
在这里插入图片描述
  我们已经知道,GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:
  复位和时钟控制RCC的地址从0x4002 1000开始;
  可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
  看手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。
在这里插入图片描述

  • 配置为通用输出

既然叫做IO,那么肯定就是可以输入,可以输出,到底是输入还是输出呢?
  控制LED需要输出高电平或是低电平,所以需要配置为输出。
  由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。
  配置引脚PB8,使用的寄存器是GPIOB_CRH。下面我们来寻找这个寄存器的地址。
在这里插入图片描述
  关于此寄存器的说明位于8.2.2小节。先看标题GPIOx,表示不管是PA,PB还是PE,都能用。
  偏移地址是0x04,意思是在基地址的基础上再加0x04,所以,对于GPIOB来说就是0x4001 0c04。如果配置PB0-PB7,那么需要的寄存器是低位的寄存器GPIOB_CRL,它的地址是0x4001 0c00。我们需要配置的寄存器是GPIOB_CRH。
  找到需要操作的寄存器后,把它配置为通用输出。
  复位值是0x4444 4444,并不是0x0000 0000。所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。
  我们需要的是输出高低电平,所以要设置为输出。输出模式又有好几种输出:
在这里插入图片描述
  推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
  开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
  开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。
  功能是否是复用呢?复用的意思是有别的功能在这个脚上,比如USB,CAN,串口等,所以这些个脚就可能有多个功能。暂时讲多了反而会迷惑,等用到了这些功能再讲解,我直接告诉大家,PB8没有复用。
  所以配置为输出模式,通用推挽输出。速度暂时不关注,随便填写一个50MHz吧,其它速度当然也可以。所以设置GPIOB_CRH的MODE8与CNF8为0b0011,即0x3。此寄存器中其它的位暂时不做修改,使用默认值,也就是GPIOB_CRH设置为:0x4444 4443。

  • 点亮LED需要输出低电平

在单片机的编程中,要想做某件事,必须寻找相应的寄存器。在8.2.4小节,可以找到端口输出数据寄存器,就是我们需要的。我们需要输出0。但是中文手册有一个小小的BUG,0x0C写成了0Ch,可以参考英文原版。得知地址的偏移是0x0C,所以这个数据寄存器的地址就是0x4001 0C0C,把第8位写为0就行。默认就是0,但是也得学一下怎么写,万一是高电平点亮呢。
在这里插入图片描述
在这里插入图片描述

  • 使用直接赋值的方式写寄存器的地址

在搞清楚我们要用的几个寄存器的地址,以及寄存器中需要装填的数值以后,现在用一个简单粗暴的方法来操作这些寄存器——直接操作。(注意,这段代码不是实用的代码,只是为了写出一个最简单的LED,有些部分是不可取的。)将main函数修改为:

    int main(void)
    {
        unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;
        unsigned int *pGPIOB_CRH = (unsigned int *)0x40010c04;
        unsigned int *pGPIOB_ODR = (unsigned int *)0x40010c0c;
        *pRCC_APB2ENR = 0x00000008;
        *pGPIOB_CRH = 0x44444443;
        *pGPIOB_ODR = 0x00000000;
         return 0;             
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

C语言总是从main函数开始执行。
  定义几个指针,指向刚刚看到的地址。对于编译器来说,它并不知道0x40021018代表的是数据还是指针,所以用(unsigned int *)作强制的类型转换,告诉编译器0x40021018是个指针。指针可以理解为地址。操作指针,把这些地址存放的值修改。
  最后的return,代表main函数结束。

总结

我们写了一段另类的代码,直接操作寄存器的地址,就是想得到这么一个结论:不论代码怎么写,不论是寄存器,库函数,还是其他的操作系统,要在STM32F103这个单片机点亮LED灯,肯定需要把时钟和GPIO这几个相关的特殊地址,进行赋值或修改数值的操作。有点像打篮球,不论进攻时有怎样花哨的运球与传切配合,最后都要完成把球放入篮筐的动作,才能得分。
在这里插入图片描述

STM32中,寄存器操作是通过直接访问寄存器来对硬件进行配置和控制的一种方式。这种操作方式可以使用寄存器地址和指针来实现。STM32系列芯片提供了一个头文件stm32f10x.h,其中包含了寄存器地址的映射关系。通过引用这个头文件,我们可以使用寄存器的具体名称而不是地址来进行操作。 要找到所需的寄存器,我们可以使用C语言中的指针和取地址操作符(&)。类似于使用scanf语句从控制台输入一个变量时需要使用&符号来获取变量的地址一样,不同的寄存器有不同的地址。因此,要找到对应的寄存器,我们只需找到对应的地址即可[2]。 在学习STM32的过程中,大部分人最初可能会使用标准库版本,对于寄存器操作可能不太熟悉。寄存器是CPU内部用来存放数据的小型存储区域,用于临时存放参与运算的数据和运算结果。 综上所述,STM32寄存器操作是通过直接访问寄存器来配置和控制硬件,可以使用寄存器地址和指针来实现。为方便使用,STM32提供了一个头文件stm32f10x.h,其中包含了寄存器地址的映射关系。使用指针和取地址操作符(&),我们可以找到所需寄存器地址寄存器是CPU内部用于存放数据的小型存储区域,用于临时存放参与运算的数据和运算结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STM32寄存器简介地址查找,与直接操作寄存器](https://blog.csdn.net/geek_monkey/article/details/86291377)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [关于STM32寄存器操作学习](https://blog.csdn.net/lemou1211/article/details/129170414)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值