c文件及h文件中所有代码、全局变量、局部变量、’const’限定符定义的常量数据、startup.asm文件中的代码(类似ARM中的bootloader或者X86中的BIOS,一些低端的单片机是没有这个的)通通都存储在ROM中。
RAM——程序运行中数据的随机存取(掉电后数据消失)
整个程序中,所用到的需要被改写的量,都存储在RAM中,“被改变的量”包括全局变量、局部变量、堆栈段。
FLASH——存储用户程序和需要永久保存的数据。
ROM的大小疑问:
对于flash类型的MCU,ROM空间的大小通常都是整字节的,即为ak8bits。这很好理解,一眼就知道,ROM的空间为aK。但是,对于某些OTP类型的单片机,比如holtek或者sonix公司的单片机,经常看到数据手册上写的是“OTP progarming ROM 2k15bit…”,可能会产生疑惑,这个“15bit”认为是1个字节有余,2个字节又不足,那这个ROM空间究竟是2k,多于2k,还是4k但是少了一点点呢br> 答:这里要明确两个概念:一个是指令的位宽,另一个是指令的长度。指令的位宽是指一条指令所占的数据位的宽度;有些是8位位宽,有些是15位位宽。指令长度是指每条指令所占的存储空间,有1个字节,有2个字节的,也有3个字节甚至4个字节的指令。实事上也确实如此,当在反汇编或者汇编时,可以看到,复合指令的确是有简单的指令组合起来的。
三、flash
关于flash,在单片机中需要外接,且需要cup具有SPI接口
例如:25PE80V6、25080BVSIG等
移位运算符
移动操作符可以把数字中每个二进制数位统一想左或者向右移动n个位置,移动操作会得到一个新数字,不会修改原来的数字。
1)
向左移动是右边空出来的位置上一定补充0
例如 二进制 (0000 0011) 3 0000 0011 3 x 2^2 =12
0000 1100
再如:
-5
对于unsigned类型,右移时使用0填充左端空出的位。对于有符 类型,结果依赖于机器。空出的位可能用0填充,或者使用符 (最左端的)位的副本填充。
例如 二进制 (0000 1100) >> 2 向右移动两位结果为 0000 0011
12 0000 1100 12 / 2^2 = 3
也可以这么理解,相对于unsigned类型而言,向右移动n位相当于除以2的n次方
注意:
应避免使用 a
所以说,左移和右移的位数不能大于数据的长度,不能小于 0。
位运算的用法
五、位运算符用法
1、掩码
flags &= MASK;
例如:flags二进制为 1001 0110 MASK二进制为 0000 0010 即:
flags &= 0x02;
flags = 0000 0010
这个语句将导致flags的除位 1之外,所有位都被设为 0。
2、打开位
flags |= MASK;
例如:flags二进制为 1001 0100 MASK二进制为 0000 0010 即:
flags |= 0x02;
flags = 1001 0110
这个语句将flags中的位1设为1,并保留其他所有位不变。
结合移位运算符
flags |= MASK
例如:flags二进制为 1001 0110 MASK二进制为 0000 0001 n为4即:
flags |= 0x01
flags = 1001 1110
这个语句将flags的位 3 设为1,并保留其他所有位不变。
3、关闭位
flags &= ~MASK;
例如:flags二进制为 1001 0110 MASK二进制为 0000 0010 即:
flags &= ~0x02;
flags = 1001 0100
这个语句将flags除位1设为0以外,保留其他所有位不变。
结合移位运算符
flags &= ~(MASK
例如:flags二进制为 1001 0110 MASK二进制为 0000 0001 n为3即:
flags &= ~(0x01
flags = 1001 0010
这个语句将flags的位 2 设为0,并保留其他所有位不变
4、转置位
flags ^= MASK;
例如:flags二进制为 1001 0110 MASK二进制为 0000 0010 即:
flags ^= 0x02;
flags = 1001 0100
转置一个位表示如果该位打开,则关闭该位,如果该位关闭,则打开该位。
5、查看一位的值
if ((flags & MASK) == MASK)
puts (“Wow);
例如:flags二进制为 1001 0110 MASK二进制为 0000 0010 即:
if ((flags & 0x02) == 0x02)
puts (“Wow);
所有基础的数据类型, 最大的也不过 10 个字节;
int main()
{
struct Bit
{
unsigned a: 1; /* 1 Bit, 取值范围: 0 – 1 /
unsigned b: 2; / 2 Bit, 取值范围: 0 – 3 /
unsigned c: 3; / 3 Bit, 取值范围: 0 – 7 /
unsigned d: 4; / 4 Bit, 取值范围: 0 – 15 */
}B;
B.a = 1;
B.b = 3;
B.c = 7;
B.d = 15;
printf(“%d, %d, %d, %d/n”, B.a, B.b, B.c, B.d);
getchar();
return 0;
}
上例中, 位域的类型被指定为是无符 的整型(unsigned int), 我试着只要是整型都可以, 但要一致。
struct Bit {
int b8: 1;
int b7: 1;
int b6: 1;
int b5: 1;
int b4: 1;
int b3: 1;
int b2: 1;
int b1: 1;
} B;
unsigned char *p = NULL;
B.b1 = 1; B.b2 = 1; B.b3 = 1; B.b4 = 1; B.b5 = 1; B.b6 = 1; B.b7 = 1; B.b8 = 1;
p = (unsigned char *)&B;
printf(“%dn”, p); / 255 */
B.b1 = 0; B.b2 = 0; B.b3 = 0; B.b4 = 0; B.b5 = 0; B.b6 = 0; B.b7 = 0; B.b8 = 1;
p = (unsigned char *)&B;
printf(“%dn”, p); / 1 */
B.b1 = 0; B.b2 = 0; B.b3 = 0; B.b4 = 0; B.b5 = 1; B.b6 = 1; B.b7 = 1; B.b8 = 1;
p = (unsigned char *)&B;
printf(“%dn”, p); / 15 */
B.b1 = 0; B.b2 = 1; B.b3 = 1; B.b4 = 1; B.b5 = 1; B.b6 = 1; B.b7 = 1; B.b8 = 1;
p = (unsigned char *)&B;
printf(“%dn”, p); / 127 */
getchar();
return 0;
包含位字段的结构和其他结构没有区别, 譬如同时包含其他类型的字段:
#include
int main(void)
{
struct Bit {
unsigned b1: 1;
unsigned b2: 1;
float f;
} B;
B.b1 = 0;
B.b2 = 1;
B.f = 3.14;
printf(“%d, %d, %gn”, B.b1, B.b2, B.f);
getchar();
return 0;
}
- 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
unsigned a:4
unsigned :0 /空域/
unsigned b:4 /从下一单元开始存放/
unsigned c:4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 - 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
- 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
int a:1
int :2 /该2位不能使用/
int b:3
int c:2
};
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!