Keil创建新的STM32工程以及CortexM3的位带操作
(*(__IO uint32_t *) (RAM_BB_BASE ((VarAddr - RAM_BASE) < 5) ((BitNumber) < 2)) = 0)
这一行第一眼看去不明觉厉。。需要仔细想想。把Var_ResetBit_BB 定义成这样一个函数,它的两个参数是(VarAddr, BitNumber)
//用这种形式表示
void Reset(VarAddr, BitNumber){
首先是 (RAM_BB_BASE ((VarAddr - RAM_BASE) < 5) ((BitNumber) < 2)) 假设得到的结果是A
然后是 (__IO uint32_t *) A 这是强制类型转换,即把A 的类型转换成__IO uint32_t的指针,假设结果是B,即B =(__IO uint32_t *) A
接下来 * B 即取 B的内容,假设C = *B
最后是 C = 0
}
接着再来具体分析一下函数里面的过程,也就是转换公式的实现部分( RAM_BB_BASE ( (VarAddr - RAM_BASE) < 5) ( (BitNumber) < 2) )
VarAddr对应于公式中的 Addr ,BitNumber 对应于 n
(1) 首先,二进制数左移n位,就相当于乘以2的n次方(D),因此
(VarAddr - RAM_BASE) < 5 就相当于(VarAddr - RAM_BASE) × 32 ,(BitNumber) < 2相当于(BitNumber) × 4
对照转换公式可以发现两者很像了,如果 按位或 和 公式中的 + 在这里能得到相同的结果,那么这个函数就可以用了。下面来看看是不是这样。
(2)
RAM_BB_BASE=0x2200 0,转换成二进制就是
0010 0010 0 0 0 0 0 0
由于低25位全都是0(加上一个数肯定不会产生进位),因此 与一个数相加 和 与一个数按位或 的结果是一样的(但是 或 更快),当然这个数不能超过25位。
而位带区地址最多是0x2 0开始的1MB=2^20范围,也就是VarAddr-RAM_BASE涉及到的范围是在0x00 ~ 0xFFFFF,也即
0 0 0 0 0 ~ 1 1 1 1 1 (20位)
左移5位后也就是最多25位,因此符合上面的条件,按位或和加法等效,不存在进位问题,精妙的设计!
每一个序号(地址)对应的内存中有8位,也就是说BitNumber范围是0~7 (0 ~ 0),左移2位后是 00 ~ 00,不超过VarAddr-RAM_BASE左移5位 后多出来的0,因此也符合条件,按位或和加法等效。
因此( RAM_BB_BASE ( (VarAddr - RAM_BASE) < 5) ( (BitNumber) < 2) ) = 转换公式的右边
再回到(*(__IO uint32_t *) (RAM_BB_BASE ((VarAddr - RAM_BASE) < 5) ((BitNumber) < 2)) = 0) ,在刚才假设的那个void Reset(VarAddr, BitNumber) 函数中,A(或者说是B)代表变量在位带别名区的对应地址(序号),C=*B,也就是取内容,即取这个序号的内容(其实是由此开始的4个序号,即 32位 )。
若要Reset,则C = 0,若要Set 则 C = 1(不用 =31,因为该字只有最低位有效)。由此实现了:若要对位带别名区的地址(序号)中的内容进行复位/置位(其实是写一个字进去),就可以改变位带区中对应的地址中的某一位的值(比如),而这个位带别名区的地址只需通过VarAddr = (uint32_t)&Var得到在位带区中的地址,并把要改动该地址中的哪一位(BitNumber)一起作为参数给 Reset /Set 函数即可 的功能。而GetBit则是到*B为止,即return位带别名区地址的内容(32位)。
但是现在有个问题:VarAddr = (uint32_t)&Var只是从Var的开始地址算起,Offset = VarAddr - BB_BASE已经固定了,无法转到下一个字(n=0~7),这要是Var这个变量过大,那不就够不着对应的位带别名区了。。?
2.2.2
结合代码中调用这几个“函数”的部分来看,会有进一步发现。
//Var = 0x05AA5;
//VarAddr = (uint32_t)&Var;
(1)
1 /* Modify Var variable bit 0 --*/2 Var_ResetBit_BB(VarAddr, 0); /* Var = 0x05AA4 = 0 0 0 0 0101 1010 1010 0100 */3 printf("VAR=0x%x\n",Var);4 5 Var_SetBit_BB(VarAddr, 0); /* Var = 0x05AA5 = 0 0 0 0 0101 1010 1010 0101 */6 printf("VAR=0x%x\n",Var);
位带区地址(序号) | …… | 0x2 3.(7 ~ 0) | 0x2 2.(7 ~ 0) | 0x2 1.(7 ~ 0) | 0x2 0.(7 ~ 0) |
位带区地址对应的内容.(7 ~ 0) | …… | 0 0 | 0 0 | 0101 1010 | 1010 0101 |
Reset(VarAddr, 0)之后 | 0 0 | 0 0 | 0101 1010 | 1010 0100 | |
再Set(VarAddr, 0)之后 | 0 0 | 0 0 | 0101 1010 | 1010 0101 |
BitNumber=0,Reset得到的结果是把第一个地址的.0位(第1位)变成了0,Set后又回到了1。
(2)
1 /* Modify Var variable bit 11 --*/2 Var_ResetBit_BB(VarAddr, 11); /* Var = 0x052A5 = 0 0 0 0 0101 0010 1010 0101*/3 printf("VAR=0x%x",Var);4 5 /* Get Var variable bit 11 value */6 VarBitValue = Var_GetBit_BB(VarAddr, 11); /* VarBitValue = 0x00 */7 printf(" Bit11=%x\n",VarBitValue);8 9 //******************************************10 Var_SetBit_BB(VarAddr, 11); /* Var = 0x05AA5 = 0 0 0 0 0101 1010 1010 0101*/11 printf("VAR=0x%x",Var);12 13 /* Get Var variable bit 11 value */14 VarBitValue = Var_GetBit_BB(VarAddr, 11); /* VarBitValue = 0x01 */15 printf(" Bit11=%x\n",VarBitValue);
KeilSTM32工程CortexM3位带操 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)