STM32工作笔记0036---C语言复习--寄存器地址名称映射

技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152

先看与运算.

位与运算,是两个都是1的时候是1,

有一个为0 就是0

位或运算,两个位有一个为1,就是1,连个都是0那结果就是0.

取反,就1变成0,0变成1

左移,比如1010,这里左移1位就变成0100,最左边移出去了,

然后右边补0.

右移也是一样的.

这个位异或运算,是,比如1010 和1111,上图右边,异或的时候

他是不同的时候,为1, 0和1异或的时候为1,然后相同的为0,

所以异或结果上面的就是,0101

可以打开个工程,看看:

STM32的官方库,也很多地方用到了位操作.

定时器里面就用到了取反.

再看宏定义

可以看到下面:官方库里也有用到宏定义.

这个条件宏定义,这里就是判断,有没有这个标识符,有的话,做什么...没有的话,做什么..

这里可以看,咱们在mdk中添加的宏定义,其实就是这样的意思,如果有这个宏定义,怎么 怎么样...

这个条件宏定义,在STM32库函数中用到的也很多.

然后再看extern变量声明

这类可以看到比如在main.c中有个变量id,那么在tesc.c中如果我想用这个id,这里

只需要在id前面加extern这个关键字表示,我在test.c中用,但是我在其他文件中已经定义了

也就是在test.c中不用定义了,这个extern u8 id;并不是定义,而是告诉编译器,我其他文件中已经定义了

你去其他文件中去找就可以了.

这个是给类型起一个别名.

可以看到

在STM32的官方库中也有很多类型别名.

可以看到这里把最后一行uint32_t定义成了u32这样一个别名,给这个类型起了个别名u32这样用起来方便

注意可以看到这个里的:uint32_t这个也是,定义的一个别名,c语言中没有uint32_t这样的一个类型.

上面可以看到有定义的地方,uint32_t

这个定义,这个定义实际上就是把unsigned int 给这个类型起了个别名叫unit32_t;来用了.

看一下结构体,他就是把一堆变量放在一起,相关联的变量放一起以后,看起来会比较有序.

如果不用结构体,都用变量的话,后面程序维护很麻烦,变量太多.

有了结构体,就只需要修改结构体就可以了,不用再修改所有的变量了.

结构体再文档中也有介绍

然后再看静态.

static修饰的方法一般都是在内部使用,对于static声明的局部变量,存在静态存储区.

static修饰的局部变量,是可以进行保留的,这个比如上图说的,右边的函数flag会返回1,2,3,而左边的会一直返回1,

这个前面有说过.

注意所有的mcu的操作,都是去操作寄存器.

51单片机中的地址映射方法:

51中sfr这个声明是51单片机中的,表示这个是个地址,然后P0这个=0x80,实际上P0这个引脚,映射的地址是寄存器的0x80这个地址,

而P0=0x00,实际上是往寄存器的0x80这个地址中去写入0x00这个内容.

再看STM32中的地址映射,因为stm32比51单片机的寄存器多得多,所以,这里

要做地址映射.

比如GPIOA->ODR=PX00000000;这样的话,就是给GPIOA的ODR寄存器赋值.

但是,具体0x00000000这个值是怎么写入到了GPIOA的ODR寄存器的对应的地址了呢?

先去看代码:

实际上可以看到.h文件中定义了很多的结构体等等.这个映射关系也是定义在这里的,

这里咱们以GPIO为例:

可以看到这一组GPIO有7个寄存器.

他是如何映射的呢

注意看图中的小字,实际上他是,按照比如外设有个基地址,这个是PERIPH BASE 

然后APB2他的基地址是外设基地址加上一个偏移量.

然后GPIOA这个IO口,他的基地址是APB2的基地址+上一个偏移量.

然后GPIOB这个IO口的地址就是GPIOA的基地址+一个偏移量.

这样来做的.

注意上面的图看不清,点击可以放大看的很清楚了就.

在参考手册中也有说明:

可以看到这个也有地址说明,每个寄存器,以及偏移量等都有说明.

然后再代码中是如何映射的呢

可以看到以GPIOA_BASE这个基地址,可以看到GPIOA_BASE的基地址,=APB2PERIPH_BASE+上一个偏移量.

再搜一下这个GPIOA_BASE

可以看到这个

GPIOA_BASE这里定义了一个GPIOA

注意这里把GPIOA_BASE这个基地址,直接强制转换为(GPIO_TYPEDef)这个结构体了.

这个是什么意思呢?这里很重要:

可以看到这个结构体,这里的强制转换是这样的:

可以放大看,这里实际上是:

当把GPIOA_BASE强制转换为GPIO_TypeDef的时候,这个时候会从GPIOA_BASE这个基地址开始,

他会这样,来解析接下来的数据,会从这个基地址开始,查一行8位,4行32位,4行,这个是CRL寄存器,

也就是从GPIOA_BASE基地址开始,查4行32位,给CRL寄存器来用,接下来再4行给CRH寄存器来用

以此类推.

注意这里每行都对应一个地址,也就是一个寄存器占用4个地址区.

这个就是这个强制转换的意思,他会从指定的基地址开始,把对应的结构体填充上.

根据结构体中定义的数据类型.的长度.来填充.

这里再看,这个

APB2..这个外设基地址+偏移量 位GPIOA_BASE的基地址.

然后再找定义,看到外设基地址,就是一个常量了.

就是通过这样的方法把所有的寄存器.名称跟地址映射起来的.

比如再看看,RCC时钟:

看到这里RCC_BASE也是被转换成了RCC时钟,这里也有个RCC_TypeDef.

走到RCC_BASE定义,再看看RCC_base,这个也是外设的基地址+上一个偏移量对吧.

猜你喜欢

转载自blog.csdn.net/lidew521/article/details/108095670
今日推荐