第一章:POS机外设驱动概述
大家好,我是你们这门课的老朋友。今天咱们聊聊POS机外设驱动到底是个啥玩意儿。说实话,我刚开始接触这个领域的时候,也觉得挺懵的——不就是个刷卡器吗?后来踩了不少坑才明白,这里面的门道深着呢。
1.1 什么是POS机外设驱动
说白了,POS机外设驱动就是让操作系统和硬件设备「对话」的翻译官。你想想看,Linux内核不认识你的密码键盘,也不懂你的热敏打印机。驱动就是那个中间人,把内核的通用指令翻译成硬件能听懂的信号。
我个人习惯把驱动分成三层来看:
- 硬件抽象层:负责和芯片寄存器打交道
- 核心逻辑层:处理数据缓冲、协议解析
- 用户接口层:给应用程序提供read/write/ioctl接口
我在项目中遇到过最典型的例子——某款密码键盘,厂商给的驱动只支持Windows。客户非要跑Linux,怎么办?只能自己撸一个。嗯,那时候我才真正体会到,驱动开发不是简单的「照着数据手册写代码」,你得懂硬件时序、懂内核机制、还得会调试。
核心要点:驱动开发 = 50%硬件知识 + 30%内核机制 + 20%调试经验
1.2 驱动开发环境搭建
搭建环境这事儿,说简单也简单,说复杂也复杂。我见过不少新手,一上来就在Windows上装虚拟机搞交叉编译,结果折腾两天连Hello World都没跑通。其实没那么玄乎。
交叉编译工具链
为什么需要交叉编译?因为你的开发电脑是x86架构,而POS机通常是ARM架构。你不能在x86上编译出ARM能跑的程序——除非你用交叉编译工具链。
我个人推荐用Linaro提供的工具链,稳定且社区活跃。安装步骤很简单:
# 下载ARM交叉编译工具链
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
# 解压到指定目录
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
小技巧:我习惯把环境变量写到~/.bashrc里,省得每次开终端都要export一遍。另外,记得检查一下工具链版本是否匹配你的内核版本,不匹配的话编译出来的模块可能加载不上。
内核源码树
驱动开发离不开内核源码树。你不需要编译整个内核,但需要头文件和模块编译框架。我建议直接下载和你目标板相同版本的内核源码:
# 下载内核源码
git clone --depth=1 -b your-branch https://github.com/your-kernel-repo.git
# 配置内核(使用目标板的配置文件)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
# 准备模块编译环境
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare
这里有个坑——我曾经因为内核版本号差了一个小版本,编译出来的驱动模块加载时直接报「Invalid module format」。折腾了一下午才发现是版本校验的问题。所以,一定要确保源码树版本和运行内核完全一致。
1.3 POS机常用外设接口
POS机的外设接口五花八门,但常用的就那么几种。我按使用频率排个序:
| 接口类型 | 典型外设 | 速率 | 开发难度 |
|---|---|---|---|
| 串口(UART) | 密码键盘、串口打印机 | 9600bps~115200bps | 低 |
| USB | 扫码枪、USB打印机、IC卡读写器 | 12Mbps~480Mbps | 中 |
| GPIO | 蜂鸣器、LED指示灯、按键 | 取决于主频 | 低 |
| I2C | 触摸屏、传感器、EEPROM | 100Kbps~400Kbps | 中 |
| SPI | 显示屏、SD卡、加密芯片 | 10Mbps~50Mbps | 中高 |
串口(UART)
串口是POS机外设的「老大哥」。为什么?因为它简单、可靠、成本低。我做过一个项目,客户要求兼容市面上20多种密码键盘,最后发现它们全用串口通信,只是协议不同。串口驱动在Linux里已经很成熟了,你主要工作是写一个用户空间的协议解析层。
串口通信的几个关键参数:波特率、数据位、停止位、校验位。大多数POS外设用9600波特率、8数据位、1停止位、无校验。但也不绝对——我记得有一款老式的串口打印机,非要19200波特率+偶校验,不然打印出来全是乱码。
USB
USB接口的外设现在越来越多了。Linux的USB子系统很强大,但写驱动时要注意:你的设备是HID类、CDC类还是vendor-specific类?不同类别对应的驱动框架不一样。
我曾经接手过一个USB扫码枪的项目,厂商只提供了Windows的DLL。我通过USB抓包分析,发现它用的是HID POS协议。嗯,花了三天时间逆向出通信协议,然后写了个Linux内核驱动。说实话,USB驱动开发最头疼的是描述符解析,一个字节对不上设备就不认你。
避坑指南:USB设备的热插拔处理一定要做好。我曾经因为没处理好设备断开事件,导致内核panic。后来在probe和dispose函数里加了完善的引用计数和锁机制,才算搞定。
GPIO
GPIO是最简单的接口,没有之一。但简单不代表不重要。POS机上的蜂鸣器、状态指示灯、甚至某些按键检测,都用GPIO。Linux提供了gpiolib子系统,操作起来很方便:
# 导出GPIO
echo 17 > /sys/class/gpio/export
# 设置方向为输出
echo out > /sys/class/gpio/gpio17/direction
# 设置高电平
echo 1 > /sys/class/gpio/gpio17/value
不过在实际项目中,我很少用sysfs接口——太慢了。一般直接在驱动里用gpiod_* API操作,响应速度能快一个数量级。
I2C
I2C总线在POS机里主要用于连接触摸屏和传感器。它只有两根线(SDA和SCL),但通信协议比串口复杂一些。Linux的i2c-dev接口让用户空间也能直接操作I2C设备,调试起来很方便。
我记得有一次调试I2C触摸屏,死活读不到设备ID。用逻辑分析仪抓波形才发现,是上拉电阻没焊好,导致时钟信号畸变。所以,I2C出问题先查硬件,别一上来就怀疑驱动代码。
SPI
SPI是速度最快的接口,常用于显示屏和加密芯片。它需要4根线(MISO、MOSI、SCLK、CS),全双工通信。Linux的SPI驱动框架设计得很优雅,但写起来要注意时序——尤其是片选信号的拉高拉低时机。
我做过一个SPI加密芯片的驱动,厂商数据手册说片选要在传输结束后保持至少100ns的低电平。结果我忽略了这一点,导致加密操作偶尔失败。后来在传输函数里加了个udelay(1),问题就解决了。嗯,有时候驱动开发就是跟这些微秒级的时序较劲。
总结一下:接口选择没有绝对的好坏,关键看你的外设需求。串口适合低速、长距离;USB适合高速、热插拔;GPIO适合简单控制;I2C适合板内短距离通信;SPI适合高速数据传输。选对了接口,驱动开发就成功了一半。
好了,第一章的内容就到这里。下一章咱们会深入串口驱动的具体实现,包括如何用中断方式接收数据、如何处理粘包问题。到时候我会拿一个真实的密码键盘驱动来讲解,保证干货满满。