技术交流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,这个也是外设的基地址+上一个偏移量对吧.