本笔记是学习 B站 嵌入式C语言高级课程过程中产生的疑问和回答汇总,有比较基础的知识也有嵌入式硬件结合的知识

在这里插入图片描述

1. 地址相差4个字节?

这些地址之间相差4个字节(即0x4)是一个非常普遍且重要的硬件设计特征。这并非偶然,而是由两个关键因素共同决定的:

  1. ​地址总线按字节编址​

  2. ​寄存器宽度为32位(4字节)​

详细解释:

1.1. 地址总线按字节编址

现代处理器的内存地址空间通常是以​​字节​​为最小单位进行编址的。这意味着:

  • 地址 0xE270_0000指向第1个字节

  • 地址 0xE270_0001指向第2个字节

  • 地址 0xE270_0002指向第3个字节

  • 地址 0xE270_0003指向第4个字节

1.2. 寄存器宽度为32位(4字节)

从图片中Reset Value(复位值)一列可以看到,这些寄存器的默认值如 0x00008021,这是一个32位的数值(8个十六进制数)。因此,每个看门狗定时器寄存器都需要占用 ​​4个字节​​ 的地址空间。

1.3. 结论:地址计算

将以上两点结合起来,原因就非常清晰了:

  • ​WTCON​​ 是第一个寄存器,从基地址 0xE270_0000开始,占用了 0x00000x0003这4个字节。

  • 下一个寄存器 ​​WTDAT​​ 必须紧挨着它存放,所以从下一个可用的字节地址开始,也就是 0xE270_0000 + 4 = 0xE270_0004

  • 同理,​​WTCNT​​ 的地址就是 0xE270_0004 + 4 = 0xE270_0008

  • 以此类推。

可以这样形象地理解:每个寄存器都是一个“房间”,这个“房间”的大小是4个“格子”(字节)。系统给每个“房间”分配门牌号(地址)时,用的是第一个“格子”的编号。那么下一个“房间”的门牌号,自然要往后数4个格子。

| 寄存器 | 地址(起始字节) | 占用的地址范围 |

| :— | :--------------- | :----------------------- |

| WTCON | 0xE270_0000| 0xE270_0000- ...0003|

| WTDAT | 0xE270_0004| 0xE270_0004- ...0007|

| WTCNT | 0xE270_0008| 0xE270_0008- ...000B|

| WTCLRINT | 0xE270_000C| 0xE270_000C- ...000F|

这种设计保证了每个寄存器都在其​​自然边界​​上对齐(32位数据在地址能被4整除的地方对齐),CPU访问对齐的内存时速度最快、效率最高,这是硬件设计的最佳实践。


2. 结构体定义

2.1. 什么是结构体?

你可以把它想象成一个“自定义的包裹”或“超级变量”。

  • ​普通变量​​:只能存储一个单一类型的数据,比如一个 int变量存整数,一个 char变量存字符。

  • ​结构体变量​​:可以​​将多个不同类型的数据打包在一起​​,形成一个逻辑上的整体。这个“包裹”里的每一个数据称为结构的​​成员​​。

2.2. 为什么需要结构体?

在没有结构体之前,如果想描述一个学生(包含学号、姓名、成绩),您可能需要声明三个独立的变量:

int id;
char name[20];
float score;

这三个变量在语法上是完全独立的,管理起来很麻烦,尤其是当有多个学生时。

使用结构体,就可以创建一个名为 Student的新数据类型,把这三个信息捆绑在一起:

struct Student {
    int id;
    char name[20];
    float score;
};

这样,每个 Student类型的变量就自然包含了这三项信息,逻辑清晰,易于管理。


2.3. 两个关键步骤

使用结构体通常分为两步:

2.3.1. 第一步:定义结构体类型(蓝图)
struct myabc {
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int d;
};
  • ​作用​​:​​创建一种新的数据类型,而不是创建一个变量。​

  • ​详解​​:

    • struct是C语言的关键字,告诉编译器“我要开始定义一个结构体了”。

    • myabc是这个​​新类型的名字​​,它被称为“结构标签”。它和 int, float一样,现在是一种有效的类型名。

    • 大括号 {}内是它的​​成员列表​​。这里定义了四个成员,都是 unsigned int类型。

    • 最后的分号 ;​至关重要​​,它标志着这个类型定义语句的结束。忘记这个分号是初学者常见的错误。

  • ​重要提示​​:​​这一步不会分配任何内存​​。它只是告诉编译器:“嗨,以后如果你看到 struct myabc,就知道它里面包着四个无符号整数,名字分别叫a, b, c, d。” 这就像绘制了一张建筑的​​蓝图​​,定义了房子的结构,但蓝图本身并不等于一栋真实的房子。

2.3.2. 第二步:声明结构体变量(根据蓝图盖房子)
struct myabc mybuf; 
  • ​作用​​:​​使用已定义的结构体类型来创建具体的变量。​

  • ​详解​​:

    • struct myabc指明了这个变量的类型。就像我们用 int来声明一个整型变量一样,这里我们用自己定义的 struct myabc来声明变量。

    • mybuf是您为这个​​结构体变量​​起的名字。

  • ​重要提示​​:​​到这一步,编译器才会为 mybuf分配实际的内存空间​​。这块内存的大小至少足以存放四个 unsigned int(具体大小可能因内存对齐而略有增加)。

    • 现在,mybuf就是一个实实在在的“包裹”了,你可以在程序中使用它。

    • 你可以创建多个该类型的变量,例如 struct myabc yourbuf, hisbuf;,每个变量都有自己的独立内存空间。


2.4. 如何访问和使用结构体变量?

创建了结构体变量后,我们需要访问其内部的成员。这时需要使用​​成员访问运算符​.

// 给 mybuf 的成员赋值
mybuf.a = 10;
mybuf.b = 20;
mybuf.c = 30;
mybuf.d = 40;

// 从 mybuf 的成员中读取值
int sum = mybuf.a + mybuf.b; // sum 的值将是 30

// 打印成员的值
printf("The value of a is: %u\n", mybuf.a);

2.5. 总结与类比

步骤 代码示例 类比 作用 是否分配内存
​1. 定义类型​ struct MyStruct { int x; char c; }; ​绘制蓝图​ 创建一种名为 MyStruct的新复合数据类型 ​否​
​2. 声明变量​ struct MyStruct variable1; ​根据蓝图盖房子​ 创建一个实际可用的变量,类型为 MyStruct ​是​

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐