打印
[其他ST产品]

初学 STM32 (从 51 过度到 STM32 ,初学 STM32 必须了解的知识)

[复制链接]
1203|36
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
V853|  楼主 | 2022-9-13 13:06 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
最近小编也是在学习 STM32F1 系列,因为前一段时间学过 51 ,所以这几天总结了一下从 51 过度到 STM32 经验与心得,现在与大家分享分享我的心得。

对于引脚的操作和 STM32 与 51 的不同
在 51 中,我们如果想要操作 P0^0 这个引脚,应该有一下几种操作吧。
//这个是小编最开始学的操作IO口。
P0 = 0x01;                //直接操作整个P0口,使P00口为高电平,其他为低电平。
P0^0 = 1;                //单独操作一个51单片机IO口。
//后来学会了只影响一个IO口,而其他IO口不受影响。
P0 |= 0x01;                //这样做可以保证在不影响P0的其他IO的情况下只改变P00口。
//在51单片机中还有一种独有的(这个我查阅了其他资料,并没有发现sbit是51单片机独有,但是我所学过的里面只有51能用sbit,就暂时说是51单片机独有吧,希望大佬指正。)引脚定义。
sbit IO = P0^0;        //将P00口单独定义出来,这样就只会操作到一个IO口了。
以上就是我目前用到过的对于 51 单片机的 IO 口的操作。
不过我一直在思考为什么这样做?为什么 P0^0 这个符号就可以代表的 P0^0 这个引脚,是不是 STM32 也一样?
直到我学习了 STM32 ,我对于它的操作原理有了新的定义。

对于每一个 IO 口其实是有一个地址,要操作这个 IO 口其实是对这个地址的操作,下面我放一段 <STC12C5A60S2.H> 的代码进行讲解。
//由于原程序里面代码太长,我这儿截取几段来集中讲解。
//这儿我截取了51单片机里对P0和P1的定义
sfr P0   = 0x80;        //首先我们要知道sfr的意义。
//sfr的定义有点长,在这儿的作用我就长话短说,它是将0x80定义为P0.
//这儿就可以发现,曾经我以为的P0就是IO口的引脚其实也是认为定义的,IO口真正的地址其实是0x80,这个也是接下来说STM32要用到的原理。
sbit P00 = P0^0;        //对于sbit就不生疏了吧,这儿为什么要这样定义呢?
//因为我们在上面已经知道了P0的地址,0x80,这个是属于P0口的基地址,什么是基地址?简单来说,就是第一个地址。
//因此我们可以由此推断,P00就是P0的基地址在位移0位,也就是0x80,而P01就是P0位移1位,也就是0X81,所以后面的代码也就由此退出了。
sbit P01 = P0^1;
sbit P02 = P0^2;
sbit P03 = P0^3;
sbit P04 = P0^4;
sbit P05 = P0^5;
sbit P06 = P0^6;
sbit P07 = P0^7;
sfr P0M0 = 0x94;
sfr P0M1 = 0x93;
//下面与上面意义一样
sfr P1   = 0x90;
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
sfr P1M0 = 0x92;
sfr P1M1 = 0x91;
sfr P1ASF = 0x9D;


使用特权

评论回复
沙发
V853|  楼主 | 2022-9-13 13:08 | 只看该作者
对于 51 单片机的 IO 口定义也就分析完了,现在我们开始进入正题,由于上面的知识,对于 STM32 我觉得应该会相当好理解了。
首先,我们要明白, STM32 中可没有 sfr 或者 sbit 或者 bit 这种方便的定义,所以我们只有操作地址来操作 IO 口。

在具体分析之前,有个概念必须了解一下,就是储存器,储存器映射,寄存器和寄存器映射。

储存器

储存器应该很好理解,就是每一个外设(外设就是我们上面说的 P0 , P1 等 IO 口在 STM32 中这些 IO 口被分为了不同的外设。)储存了一个地址,而对于储存器本身是不具有地址信息的,它的地址是由芯片厂商或用户分配,这个过程(给储存器分配地址的过程)就称为储存器映射。

寄存器

我们已经了解到了储存器映射,那么什么是寄存器映射呢?寄存器到底是什么呢?
已经知道了储存器, 那我们可以找到每个内存单元
(每一个外设都是分配了内存单元的,用 51 单片机来讲,就可以这样理解,P00 这一个 IO 口就占用 1 bit 的内存单元)
的地址,然后通过 C 语言指针的操作方式来访问这些单元,但是,每次都通过寻址来访问,不仅不好**,还容易出错。所以这个时候我们可以根据每个每个单元 功能 的不同,以功能为名给这些内存单元取一个别名,这个别名就是我们常说的寄存器。取名字这个过程就叫做寄存器映射(和储存器映射相仿。)。

看到这儿,想必对寄存器和储存器的概念已经很清晰了吧,接下来我们就可以深入了解 STM32 了!


上面是我所使用的 STM32F103 系列的寄存器的起始地址。
可能天马行空的这样看图不是很理解,接下来我就用代码与讲解的方式来讲解。

我就用图中的 GPIOB 这个寄存器来作为例子。(因为小编最开始接触 STM32 的时候就是学习的这个寄存器的使用。)
在图中可以看到 GPIOB 的起始地址是 0x40010c00;
所以我们就可以通过控制这个基地址来操作 GPIOB 了。但是,要怎么操作呢? 是像 51 单片机里面一样直接让这个地址等于 0 吗?显然不是!
从 STM32F103 的数据手册的目录,我们可以找到与 GPIO 有关的寄存器。

下面我们随便打开一个寄存器来分析。

有这个图片来分析,首先是偏移地址。什么是偏移地址? 因为对于 GPIO 口不只有 GPIOB ,还有 GPIOA 、 GPIOC 等等每个 GPIO 都有相对应的这种寄存器(图中是 BSRR )寄存器,所以,偏移地址就是 各自 GPIO 口的基地址所偏移后的地址。
//理论如果不好理解,可以参考代码,综合理解。
//由上可知GPIOB的基地址是0x40010c00,所以我们可以使用宏定义。
#define GPIOB_BASE                        ((unsigned int)0x40010c00)
//有了GPIOB的基地址的用定义,我们就可以在这个的基础上定义BSRR寄存器了(重点:使用指针!!!!!!)。
#define GPIOB_BSRR                        *(unsigned int*)(GPIOB_BASE + 0x10)
//现在,我们就把GPIOB的BSRR寄存器的地址定义好了。
接下来我们先分析 BSRR 的作用。
由图我们可以看到, BSRR 一共有 32 位,低 16 位的作用是控制 GPIOB 的 16 个输出端口输出 1(高电平);
//就像是51单片机里面的。
P0 = 0x01;
而高 16 位是控制 GPIOB 的 16 个端口清除(就是输出 0(低电平) 。)。
所以,我们可以这样控制 STM32 的 GPIOB 的 1 到 16 口的输出。
//由于在这之上我们使用了宏定义,所以可以直接使用所定义的宏。
GPIOB_BSRR |= (1 << x);                                                //从这儿就可看出,将GPIOB的GPIOB_x这个引脚设置为高电平了。
//具体想要输出/清除哪个引脚,只需要改变x的值就行,若x=0,就是使GPIOB的Pin0口输出高电平。
//当然,也可以直接用地址(不过比较麻烦。)。
*(unsigned int *)0x40010c10 |= (1 << x);        //作用与上面相同。


使用特权

评论回复
板凳
V853|  楼主 | 2022-9-13 13:09 | 只看该作者

这就完了?

当然不是!
再来看一下这张图:

上面有些 AHP ,APB2,是什么呢?
我们就要结合这张图了。

这儿可以看到 GPIO 、 ADC (这些称之为外设)等等外设是挂载到 APB1、 APB2、AHB上面的,在 STM32 中,要使用这些外设,我们就必须将这些总线的时钟打开(因为默认是关闭,所以需要手动打开。)。

这儿的操作与上面操作 GPIOB 操作一样,找到时钟( RCC )的基地址,再找到寄存器地址,通过操作寄存器的方法来是使APB2 (因为我们操作的是 GPIOB ,而 GPIOB 是挂载在 APB2 上面的)的 RCC 打开。

然后我们的 GPIOB就可以正常使用了。

#define RCC_BASE                        0x40021000                                                        //RCC的基地址。
#define RCC_APB2END                        *(unsigned int*)(RCC_BASE + 0x18)        //RCC的使能寄存器地址。
RCC_APB2END |= (1 << 3)                                                                                        //由数据手册来看第三位是使能GPIOB的时钟。

好了,今天的分享就说完了,不过,这儿,只是从原理分析了 STM32 的 IO 的操作,对于固件库还有 HAL 库的讲解以后会分享,使用库可以更加容易的理解和使用 STM32 !

谢谢大家的耐心观看,代码之路任重道远,愿与大家努力习之!


使用特权

评论回复
地板
Bowclad| | 2022-9-13 20:07 | 只看该作者
库开发调用api就行了

使用特权

评论回复
5
Henryko| | 2022-9-13 20:47 | 只看该作者
只调库不了解底层白瞎

使用特权

评论回复
6
V853|  楼主 | 2022-9-16 09:49 | 只看该作者
Henryko 发表于 2022-9-13 20:47
只调库不了解底层白瞎

在理,不懂底层最后出问题不好找。

使用特权

评论回复
7
jf101| | 2022-11-2 18:56 | 只看该作者
Henryko 发表于 2022-9-13 20:47
只调库不了解底层白瞎

请问你说的底层是个啥?就是IO各个寄存器对应的地址嘛?API接口熟练的基本时间长了必然会了解内在机制的

使用特权

评论回复
8
Undshing| | 2022-11-2 19:12 | 只看该作者
了解底层了以后上手什么芯片都很容易

使用特权

评论回复
9
V853|  楼主 | 2022-12-3 19:44 | 只看该作者
jf101 发表于 2022-11-2 18:56
请问你说的底层是个啥?就是IO各个寄存器对应的地址嘛?API接口熟练的基本时间长了必然会了解内在机制的 ...

就是寄存器的含义,也就是知道该寄存器的哪一位对应着是什么状态或者功能。

使用特权

评论回复
10
V853|  楼主 | 2022-12-3 19:46 | 只看该作者
Undshing 发表于 2022-11-2 19:12
了解底层了以后上手什么芯片都很容易

是的,因为大家的底层基本都一样,就是寄存器不一样。

使用特权

评论回复
11
jf101| | 2022-12-5 14:02 | 只看该作者
V853 发表于 2022-12-3 19:44
就是寄存器的含义,也就是知道该寄存器的哪一位对应着是什么状态或者功能。 ...

哦!确实寄存器的含义在实际使用过程中挺重要的

使用特权

评论回复
12
MessageRing| | 2022-12-6 14:11 | 只看该作者
不懂底层最后出问题不好找

使用特权

评论回复
13
Stahan| | 2022-12-6 15:09 | 只看该作者
库封装的就很好了,初学不了解底层的问题不大

使用特权

评论回复
14
Henryko| | 2022-12-6 15:51 | 只看该作者
寄存器的含义在使用时很重要

使用特权

评论回复
15
Bowclad| | 2022-12-6 18:15 | 只看该作者
了解各个寄存器的定义很重要

使用特权

评论回复
16
Undshing| | 2022-12-6 19:31 | 只看该作者
知道该寄存器的哪一位对应着是什么状态或者功能

使用特权

评论回复
17
AloneKaven| | 2022-12-6 20:04 | 只看该作者
写的多了以后就了解寄存器了

使用特权

评论回复
18
MessageRing| | 2022-12-7 18:51 | 只看该作者
现在封装的很好,各个寄存器都已经有相应的函数调用了

使用特权

评论回复
19
V853|  楼主 | 2022-12-8 10:04 | 只看该作者
MessageRing 发表于 2022-12-6 14:11
不懂底层最后出问题不好找

是的,不光要懂底层,还要懂调试~

使用特权

评论回复
20
V853|  楼主 | 2022-12-8 10:08 | 只看该作者
AloneKaven 发表于 2022-12-6 20:04
写的多了以后就了解寄存器了

是的,遇到的问题越多,学到的也就越多!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

1842

帖子

1

粉丝