第1章:驱动开发环境搭建

做嵌入式驱动开发,第一件事就是搭环境。这事儿看着简单,但我在带新人时发现,环境问题往往能卡住好几天。今天我就把Linux和Windows两套环境的搭建方法,还有交叉编译工具链的配置,一次性给你讲清楚。

1.1 Linux下的开发环境:GCC + Makefile

我个人习惯在Ubuntu上做开发,版本选18.04或20.04 LTS都行。为什么?因为社区活跃,遇到问题好搜解决方案。

安装GCC工具链

打开终端,敲下面这行命令:

sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install gcc-arm-linux-gnueabihf  # 交叉编译用

build-essential这个包会把gcc、g++、make、libc-dev这些基础工具一次性装好。我在项目中遇到过有人只装了gcc没装make,结果Makefile跑不起来,折腾了半天才发现问题。

小提示:装完后用 gcc --version 验证一下。如果显示版本号,说明装好了。

编写第一个Makefile

说白了,Makefile就是告诉编译器怎么干活。下面这个例子,是我做磁条卡读卡器驱动时常用的模板:

# 磁条卡读卡器驱动 Makefile
CC = arm-linux-gnueabihf-gcc
CFLAGS = -Wall -O2 -I./include
LDFLAGS = -lm

TARGET = magcard_drv
OBJS = main.o i2c_ops.o card_decode.o

$(TARGET): $(OBJS)
	$(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

clean:
	rm -f $(TARGET) $(OBJS)

install:
	cp $(TARGET) /usr/local/bin/

你想想看,这里面的-Wall是打开所有警告,-O2是二级优化。我刚开始做驱动时,总嫌警告烦人,后来有一次因为没开-Wall,一个类型转换错误在产线上才暴露出来...嗯,从那以后我再也不敢关警告了。

注意:交叉编译时,一定要用对应的编译器前缀。比如ARM平台就用arm-linux-gnueabihf-gcc,别用本地的gcc,否则编译出来的程序在目标板上跑不起来。

1.2 Windows下的开发环境:Visual Studio + WDK

Windows驱动开发和Linux完全是两码事。我记得第一次在Windows上写驱动时,光装环境就花了两天。这里我把步骤简化一下:

安装顺序很重要

  1. 先装Visual Studio 2019(社区版免费)
  2. 再装Windows 10 SDK
  3. 最后装WDK(Windows Driver Kit)

为什么是这个顺序?因为WDK安装时会检测VS和SDK的版本,顺序错了它会报错。我曾经试过先装WDK再装VS,结果WDK找不到编译器,只能卸载重来。

创建驱动项目

装好之后,打开VS,新建项目 -> 选择"Empty WDM Driver"模板。你会看到这样的代码框架:

#include <ntddk.h>

DRIVER_INITIALIZE DriverEntry;
DRIVER_UNLOAD DriverUnload;

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    DbgPrint("磁条卡读卡器驱动加载成功\n");
    DriverObject->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

VOID DriverUnload(
    _In_ PDRIVER_OBJECT DriverObject
)
{
    DbgPrint("驱动卸载完成\n");
}

核心要点:Windows驱动用DbgPrint输出调试信息,用DebugView工具查看。这和Linux的printk是一个道理。

1.3 交叉编译工具链配置

做嵌入式开发,交叉编译是绕不开的坎。说白了,就是在PC上编译出能在ARM、MIPS等嵌入式芯片上跑的程序。

获取工具链

有三种方式:

  • 官方提供:芯片厂商(如NXP、TI)会提供配套工具链
  • Linaro:开源社区维护的ARM工具链,质量不错
  • Buildroot/Yocto:自己编译工具链,适合定制化需求

我个人推荐用Linaro的,省事。下载后解压到/opt目录:

wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
sudo tar -xvf gcc-linaro-*.tar.xz -C /opt/

配置环境变量

编辑~/.bashrc,在末尾加上:

export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

然后执行source ~/.bashrc让它生效。验证方法:

arm-linux-gnueabihf-gcc --version
避坑指南:我曾经在配置环境变量时,把PATH写成了PTHA,结果终端里所有命令都找不到了。记住,环境变量名不能写错,否则连lscd这些基础命令都会失效。

测试交叉编译

写一个简单的测试程序:

// test.c
#include <stdio.h>

int main() {
    printf("磁条卡读卡器驱动开发环境搭建成功!\n");
    return 0;
}

编译:

arm-linux-gnueabihf-gcc test.c -o test_arm

file命令查看生成的文件:

file test_arm
# 输出:test_arm: ELF 32-bit LSB executable, ARM, version 1 (SYSV)

看到"ARM"字样,说明交叉编译成功了。这个程序在x86的PC上跑不了,得放到ARM开发板上才能运行。

经验之谈:我建议你在项目开始前,先写一个"Hello World"级别的测试程序,完整走一遍编译、传输、运行的全流程。这样能提前发现环境问题,避免写到一半才发现工具链不能用。

1.4 环境验证清单

最后,我整理了一份检查清单,你可以对着打勾:

检查项 Linux Windows
编译器版本 gcc --version cl.exe /?
Make工具 make --version nmake /?
交叉编译器 arm-linux-gnueabihf-gcc --version 不适用
调试工具 gdb --version WinDbg
头文件路径 /usr/include WDK安装目录\inc

环境搭好了,后面写驱动才能顺风顺水。下一章,我们开始真正接触磁条卡读卡器的硬件接口和通信协议。