赞
踩
STM32系统框图


存储器本身不具有地址信息,芯片厂商或用户,给存储器分配地址的过程。给存储器再分配一个地址在这4GB 的地址空间中,ARM 已经粗线条的平均分成了8 个块,每块512MB。

Block0 主要用于设计片内的FLASH:STM32F103ZET6的FLASH是512KB,属于大容量。

Block1 用于设计片内的SRAM:STM32F103ZET6的SRAM 都是64KB,

Block2 用于设计片内的外设,根据外设的总线速度不同,Block 被分成了APB 和AHB 两部分,其中APB 又被分为APB1 和APB2

片上外设每个单元对应一个起始地址,可以通过C语言指针的操作方式来访问这些单元。但是每次通过地址方式访问,不好记忆且容易出错// GPIOB 端口全部输出高电平
*(unsigned int*)(0x4001 0C0C) = 0xFFFF;
//在编译器看来,这只是一个普通的变量,是一个立即数,得进行强制类型转换,转换成指针,让编译器认为是指针
根据每个单元功能不同,以功能为名给这个内存单元取一个别名,这个别名就是寄存器给已经分配好地址的有特定功能的内存单元取别名的过程// GPIOB 端口全部输出高电平
#define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
* GPIOB_ODR = 0xFF;
把指针操作“*”也定义到寄存器别名里面
// GPIOB 端口全部输出高电平
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
GPIOB_ODR = 0xFF;
片上外设区:分为三条总线
总线的基地址:总线的最低地址,挂载在该总线上的首个外设的地址

相对外设基地址偏移:该总线地址与“片上外设”基地址0x4000 0000 的差值
外设基地址:APB1总线地址最低,片上外设从这里开始

在XX 外设的地址范围内,分布着的就是该外设的寄存器


寄存器的名称“(GPIOx_BSRR)(x=A⋯E)”本寄存器相对于这个外设的基地址的偏移表中列出它的0-31 位的名称及权限介绍了寄存器每一位的功能以下内容着重理解,记不住查表看得懂就好
把总线基地址和外设基地址都以相应的宏定义起来
总线或者外设都以他们的名字作为宏名


有了具体地址,就可以用指针读写
使用(unsigned int ) 把GPIOB_BSRR 宏的数值强制转换成了地址,然后再用“”号做取指针操作,对该地址的赋值出发点:用上面的方法去定义地址,还是稍显繁琐,例如GPIOA-GPIOE 都各有一组功能相同的寄存器,如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR 等等,它们只是地址不一样,但却要为每个寄存器都定义它的地址。
改进办法:引入C 语言中的结构体语法对寄存器进行封装


假如这个结构体的首地址为0x4001 0C00(这也是第一个成员变量CRL 的地址),那么结构体中第二个成员变量CRH 的地址即为0x4001 0C00 +0x04,加上的这个0x04,正是代表CRL 所占用的4 个字节地址的偏移量,其它成员变量相对于结构
用GPIO_TypeDef 类型定义一个结构体指针GPIOx, 并让指针指向地址GPIOB_BASE(0x4001 0C00),使用地址确定下来,然后根据C 语言访问结构体的语法,用GPIOx->ODR 及GPIOx->IDR 等方式读写寄存器。
其他外设也同样可以用这种方法来封装。好消息是,这部分工作都由固件库帮我们完成了,这里我们只是分析了下这个封装的过程,让大家知其然,也只其所以然。使用C 语言对寄存器赋值时,要求只修改该寄存器的某几位的值,且其它的寄存器位不变,需要用到C 语言的位操作方法。
变量a代表寄存器,假设寄存器本来已有数值,需要把a的某一位清零,其他位不变
//定义一个变量a = 1001 1111 b (二进制数)
unsigned char a = 0x9f;
//对bit2 清零
a &= ~(1<<2);
//括号中的1 左移两位,(1<<2) 得二进制数:0000 0100 b
//按位取反,~(1<<2) 得1111 1011 b
//假如a 中原来的值为二进制数: a = 1001 1111 b
//所得的数与a 作”位与&”运算,a = (1001 1111 b)&(1111 1011 b),
//经过运算后,a 的值a=1001 1011 b
// a 的bit2 位被清零,而其它位不变。
寄存器位经过清零操作后,可以对某几位写入所需要的数值,且其它位不变
写入的数值一般就是需要设置寄存器的位参数。
//a = 1000 0011 b
//此时对清零后的第2组bit4、bit5设置成二进制数“01 b ”
a |= (1<<2*2);
//a = 1001 0011 b,成功设置了第2组的值,其它组不变
需要对寄存器的某个位进行取反操作,即1变0,0变1,直接如下操作,其它位不变
//a = 1001 0011 b
//把bit6 取反,其它位不变
a ^=(1<<6);
//a = 1101 0011 b
| 操作符 | 操作符作用 | 操作符示例 |
|---|---|---|
| >> | 8>>3 | 8右移3位 |
| << | 1<<6 | 1左移6位 |
| &= | a &= ~(1<<2) | 按位与后赋值(两个二进制的对应位都为1时,结果为1,否则结果等于0) |
| ^= | x = 0x02, x ^= 0x01, x = 0x03 | 按位异或后赋值(两个二进制的对应位相同,结果为0,否则结果为1) |
| |= | x = 0x02, x ^= 0x01, x = 0x03 | 按位或后赋值(两个二进制对应位都为0时,结果等于0,否则结果等于1) |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。