第2章:计算机基础回顾:CPU、内存、外设工作原理,操作系统与驱动的关系

说实话,很多刚入行的朋友找我聊,上来就问驱动怎么写。我通常会反问一句:你知道CPU拿到你写的代码后,是怎么一步步让硬件动起来的吗?

这个问题要是答不上来,写驱动就跟闭着眼开车差不多。今天咱们就把这层窗户纸捅破。

2.1 CPU:它到底在忙什么?

CPU说白了就是个执行机器。它从内存里取指令,然后解码、执行,再写回结果。这个过程叫指令周期。

我当年刚接触ARM Cortex-M3时,一直搞不懂“取指”和“执行”为什么能重叠。后来看了流水线原理图才明白——CPU其实是个流水线工厂。

核心要点:CPU不关心你写的是C语言还是汇编,它只认二进制指令。驱动代码最终会被编译成机器码,一条条喂给CPU。

举个例子,你写了一句:

*(volatile uint32_t *)0x40021000 = 0x01;

这行代码在CPU眼里就是:把数值0x01写入地址0x40021000。至于这个地址对应的是GPIO寄存器还是UART,CPU不管。它只负责执行。

我的经验:调试驱动时,如果硬件没反应,先别怀疑外设。用逻辑分析仪抓一下地址线的波形,看看CPU是不是真的发出了你期望的地址和数据。我踩过这个坑,整整查了两天。

2.2 内存:不只是存数据那么简单

内存分两种:RAM和ROM。驱动开发中,我们打交道最多的是RAM。

但这里有个关键概念——内存映射。什么意思呢?就是CPU把外设的寄存器也映射到了内存地址空间里。

你想想看,CPU访问内存是0x20000000到0x200FFFFF,访问外设寄存器是0x40000000到0x400FFFFF。对CPU来说,这两件事本质上是一样的——都是读写某个地址。

地址范围 用途 访问速度
0x00000000 - 0x1FFFFFFF 代码区(Flash/ROM) 较快
0x20000000 - 0x3FFFFFFF SRAM(数据区) 最快
0x40000000 - 0x5FFFFFFF 外设寄存器 取决于总线

注意:外设寄存器的访问通常要求使用volatile关键字。我曾经因为忘了加volatile,编译器把寄存器读操作优化掉了,结果外设死活不工作。嗯,这种错误犯一次就够了。

2.3 外设:CPU的“手脚”和“感官”

外设就是CPU与外界交互的桥梁。GPIO是手,UART是嘴,ADC是耳朵,定时器是心跳。

每个外设内部都有一组控制寄存器。驱动的工作,说白了就是往这些寄存器里写正确的值。

举个例子,你想让GPIO输出高电平:

// 假设GPIOA的BSRR寄存器地址是0x40020018
// 设置第0位为1,输出高电平
*(volatile uint32_t *)0x40020018 = (1 << 0);

就这么简单?对,硬件层面就这么简单。但实际项目中,你还要考虑时钟使能、引脚复用、上下拉配置等等。这些细节才是驱动开发的难点。

避坑指南:我曾经在STM32上配置UART时,忘了使能外设时钟。结果寄存器怎么写都没反应。后来查手册才发现,外设时钟默认是关闭的。这个坑,新手十有八九会踩。

2.4 操作系统与驱动:谁指挥谁?

很多人以为驱动是直接操作硬件的。其实在带操作系统的环境下,驱动是夹在操作系统和硬件之间的。

操作系统提供了一套抽象接口,比如Linux里的file_operations结构体。驱动开发者只需要实现这些接口:

struct file_operations my_driver_fops = {
    .open = my_driver_open,
    .read = my_driver_read,
    .write = my_driver_write,
    .release = my_driver_release,
};

用户程序调用open()、read()、write()时,操作系统会通过VFS(虚拟文件系统)找到对应的驱动函数,然后执行。

说白了,操作系统就是个中间人。它不关心你的硬件怎么工作,它只负责把用户的请求转交给驱动。

核心关系:

  • 用户程序 → 系统调用 → 操作系统内核 → 驱动 → 硬件
  • 驱动是操作系统和硬件之间的“翻译官”
  • 操作系统提供框架,驱动填充细节

2.5 一张图看懂整体架构

下面这张图是我自己画的,把CPU、内存、外设、操作系统和驱动的关系串起来了:

计算机系统分层架构 用户应用程序(App) 系统调用接口(System Call) 操作系统内核(VFS/进程管理/内存管理) 设备驱动(Driver) 硬件(CPU / 内存 / 外设) 用户态 内核态 内核态 内核态 硬件

从这张图可以看得很清楚:驱动是操作系统和硬件之间的桥梁。没有驱动,操作系统就是个空壳子,指挥不动任何硬件。

2.6 小结:驱动开发到底在做什么?

总结一下今天的内容:

  • CPU:执行指令,不关心指令含义
  • 内存:存储数据和代码,外设寄存器也被映射到内存空间
  • 外设:通过寄存器与CPU交互,驱动就是配置这些寄存器
  • 操作系统:提供统一接口,驱动负责实现这些接口

我个人觉得,理解这些基础概念比背一百个API都重要。因为驱动开发的本质,就是用软件去控制硬件。你不懂硬件怎么工作,写出来的驱动就是空中楼阁。

一句话总结:驱动开发 = 理解硬件手册 + 掌握操作系统框架 + 会写C语言。三者缺一不可。

好了,这一章就到这里。下一章我们开始搭建实际的开发环境,到时候我会手把手带大家配置工具链。嗯,那才是真正动手的时候。


可可精品资料库