第1章:POS机外设驱动开发概述

大家好,我是你们这门课的主讲。在嵌入式Linux驱动这个行当摸爬滚打了十几年,经手过的POS机型号少说也有几十款。今天咱们开始第一讲,先把POS机外设驱动开发的整体框架搭起来。

说实话,很多人一上来就急着写代码,结果连外设怎么接的、系统怎么跑的都没搞清楚。我早期也犯过这个错,后来发现——先把架构吃透,后面能省一半的调试时间。

1.1 POS机系统架构

一台POS机,说白了就是个专用的小电脑。它的系统架构分三层:

  • 硬件层:主控芯片(ARM Cortex-A系列为主)、内存、存储、各种外设接口
  • 内核层:Linux内核 + 设备驱动,负责管理硬件资源
  • 应用层:支付应用、管理软件,通过系统调用与驱动交互

我习惯把POS机比作一个「带收银功能的平板」。主控芯片通常是全志、瑞芯微或联发科的方案,跑着Linux 4.x或5.x内核。外设通过串口、USB、GPIO、I2C等总线连接。

核心要点:驱动开发者的工作,就是让应用层能通过标准接口(如/dev/ttyS1、/dev/input/event0)操控外设。你写的驱动,是硬件和应用之间的翻译官。

1.2 外设分类

POS机的外设种类不少,但归纳起来就五大类。我按开发难度排个序:

外设类型 接口方式 驱动复杂度 典型芯片/协议
热敏打印机 串口/USB ★★★☆☆ ESC/POS指令集
扫描枪 USB HID/串口 ★★☆☆☆ HID POS、串口透传
密码键盘 USB HID/串口 ★★★☆☆ DES/3DES加密、PIN输入
客显 串口/I2C ★★☆☆☆ LED/LCD字符显示
钱箱 GPIO/串口 ★☆☆☆☆ 12V电平控制

嗯,这里要注意——钱箱看起来最简单,但坑最多。我曾经遇到一个项目,钱箱死活弹不开,查了两天才发现是GPIO驱动能力不够,得加三极管放大。这种经验,后面章节我会详细讲。

1.3 驱动开发环境搭建

环境搭建是第一步,也是很多人卡住的地方。我建议你按这个顺序来:

1.3.1 交叉编译工具链

为什么需要交叉编译?因为你的开发电脑是x86架构,而POS机是ARM架构。在x86上编译出ARM能跑的程序,就叫交叉编译。

我个人习惯用Linaro提供的工具链,稳定且社区活跃。以ARM Cortex-A7为例:

# 下载工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压到/opt
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

# 添加环境变量
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin

# 验证
arm-linux-gnueabihf-gcc --version

小技巧:我习惯把工具链路径写到~/.bashrc里,这样每次开终端就不用重新export了。另外,记得检查工具链的glibc版本是否和POS机上的匹配,不匹配的话编译出来的程序跑不起来。

1.3.2 内核源码树

驱动开发离不开内核源码。你需要拿到POS机厂商提供的内核源码,或者从芯片厂商的官方仓库下载。

为什么一定要源码树?因为编译驱动模块时,需要依赖内核的头文件和Makefile结构。光有内核镜像是不够的。

# 假设内核源码在~/kernel目录
cd ~/kernel

# 配置内核(通常用厂商提供的defconfig)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xxx_defconfig

# 编译内核(第一次需要,后面只编模块可以跳过)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules

# 安装模块到临时目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=./modules

避坑指南:我曾经因为内核版本和工具链不匹配,编译出来的驱动模块一加载就报「Invalid module format」。后来发现是内核的 vermagic 和模块的不一致。解决办法是:确保编译内核和编译模块用的是同一套源码、同一个配置文件。

1.3.3 第一个驱动模块

环境搭好了,咱们写个最简单的「Hello World」驱动验证一下:

// hello_pos.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init hello_pos_init(void)
{
    printk(KERN_INFO "Hello, POS world!\n");
    printk(KERN_INFO "This is my first POS driver.\n");
    return 0;
}

static void __exit hello_pos_exit(void)
{
    printk(KERN_INFO "Goodbye, POS world!\n");
}

module_init(hello_pos_init);
module_exit(hello_pos_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple POS driver example");

对应的Makefile:

obj-m := hello_pos.o

KDIR := /path/to/your/kernel/source

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

编译后得到hello_pos.ko,传到POS机上用insmod加载。如果dmesg里看到打印信息,恭喜你,环境通了!

小结

这一章咱们把POS机外设驱动开发的整体面貌过了一遍。你想想看,后面每个外设的驱动,其实都是在这个框架下填充具体逻辑。打印机要处理ESC/POS指令流,密码键盘要处理加密协议,钱箱就是控制一个GPIO电平——但每个都有它的门道。

下一章,咱们会深入热敏打印机的驱动开发。我会带着你从硬件时序开始,一步步写出能打印小票的驱动。到时候你会看到,前面搭的环境是怎么派上用场的。

课后作业:在你的开发板上搭建好交叉编译环境,编译并加载hello_pos.ko模块。如果遇到问题,先检查内核源码路径和工具链路径是否正确——这是80%新手会卡住的地方。