第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%新手会卡住的地方。