Cortex-M3(stm32F10xx的内核cpu)支持4GB的存储空间
存储容量的基本单位是:bit、B(Byte)、KB、MB、GB
1B=8bit
1KB=1024B
1MB=1024KB
1GB=1024*1024*1024B=2^30B
至于为什么最大是4G,是因为它的地址总线是32位的,2^32能表示的空间地址范围是:0X0000 0000 ~ 0XFFFF FFFF,而在stm32中一个地址能存8bit也就是一字节=1B,也就是地址为0X0000 0000的这个房间能存1字节信息,0X0000 0001又能存1字节,所以一共能存2^32个字节也就是2^32B=4G。^_^
stm32内存地址映射关系如下图:
简单分析一下SRAM部分的起始位置:
右侧图地址0X2000 0000 ~ 0X2010 0000表示1MB 这是为什么?
STM32每个存储单元都是8位的(上面说了),也就是一个单元一个字节。
0X2000 0000 ~ 0X2010 0000一共0x0010 0000个房间
0X10 0000B=2^20B=1MB
为什么要分析这个位置,从图中可以看出 SRAM的起始位置1MB和Peripherals的起始位置的1MB,叫Bit Band region(位带区),而位带区上方还有个大小为32MB的区(Bit Band Alias),这个区叫位带别名区
Bit Band region大小为1MB,Bit Band Alias大小为32MB
这个图很清楚了说的,可以看到Bit Band region中地址为0x2000 0000 的房间可以放8个二进制数据,而Bit Band region房间中的1bit对应Bit Band Alias中的4个字节(32bit),所以位带区1MB对应位带别名区32MB
为什么Bit Band region中的1bit地址要拓展成32bit的地址?是因为stm32的数据总线是32位鸭,对于它的cpu来说一次处理32bit才是最快最有效的
假设我要设置位带区中的0X20000000的第1个bit为1,使用位带操作需要怎么操作呢?
bit_word_addr = 位带别名区的基地址 + (要设置的地址 - 要设置地址所在位带区的基地址)* 32 + (要设置地址的位数 * 4),好啦,翻译成代码就是:
bit_word_addr = 0X22000000 + (0X20000000 - 0X20000000) 32 + (1*4),由此可以定位到要位带操作的地址为bit_word_addr = 0X22000000 + 4 = 0X22000004,也就是图2中编号的0X22000004地址
这里乘以32是为了定位到要设置位的寄存器地址在位带别名区的地址,定位到寄存器在别名区的地址后,再按照位带区一个位占别名区四个位的规律定位到要设置的位在别名区的哪个地址,这句话理解了这个公式也就看懂啦
还记得51单片机的典型操作吗?(AT8951是8位的,stm32就比较 了32位的)
sbit led1 = P3^3 //含义:是将发光二极管 led1 接 P3口 3位端,用以控制 led1 的亮灭
然而我们在32却不能直接指定GPIO的某个引脚!
于是引入了“位带操作的概念”,什么事位带操作???标准的定义是:通过访问位带别名区来实现,即通过将每个比特(bit)位膨胀成一个32位字,当访问这些字的时候就达到了访问“位”的目的,这就是位带操作
看一下应用:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
//----------------------------------------------------
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n)
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n)
首先 <<5相当于乘32,<<2=乘4
addr就是基地址
*((volatile unsigned long *)(addr))
里面的addr 被强制换换成(volatile unsigned long *) 指针类型
volatile修饰符 就是说防止unsigned long * 被意想不到地改变
*(volatile unsigned long *) 获得 addr里的内容
P3^3就是P3.3引脚地址的具体值,P3是reg51.h等等与单片机相关的头文件内定义好的端口3的字节地址,P3^3这个运算可以得到端口3第3位的位地址,即P3.3引脚地址,我们之所以可以用P3_3直接操作P3.3这一个单独的引脚就是因为这些。
再例如:(51中)
使引脚p1.5输出高电平,可以这样写:
p1=0x20;
或者:
sbit p_5 = p1^5;
p_5 = 1;
第一种写法直接操作p1寄存器: 0x20写成二进制数就是0010 0000,从左到右代表了p1.7~p1.0,我们发现,在p1.5的位置为1(51是8位的,理所当然寄存器也都是8位)
第二种写法叫“位操作”:把p1.5命名为p_5,再给p_5写1,同样可以使得p1.5=1,让p1.5输出高电平。
sbit是一个“伪关键字”,并不是c语言标准的关键字。
|