Linux PPS子系统:PPS子系统框架、PPS-GPIO驱动、pps-tools等介绍
PPS(Pulse Per Second)指的是“每秒脉冲”,它是一种高精度的时间信号,每秒钟提供一个脉冲,通常用于时间同步和时钟校准。PPS源可以是各种设备,比如GPS接收器、网络时间协议(NTP)服务器或其他能够提供精确时间信号的设备。
PPS信号的发送时机通常是在每秒的开始,即在UTC时间的整秒时刻。这种信号可以用于校准计算机或其他设备的时钟,以确保时间的准确性。
1 PPS子系统框架
PPS子系统分为:
- PPS核心层:负责PPS注册和去注册,以及事件上报接口。
- PPS驱动层:负责特定PPS源使能,并将其注册到系统中。
- PPS用户层应用:通过/dev/ppsX去读PPS时间等信息。
2 PPS核心层
2.1 PPS子系统初始化
pps子系统初始化:
pps_int
class_create--创建pps设备类。
alloc_chrdev_region--分配pps设备主设备号。
pps_exit
class_destroy
unregister_chrdev_region
pps类的属性包括:
static struct attribute *pps_attrs[] = { &dev_attr_assert.attr,--PPS信号的“assert”事件的时间戳和序列号。 &dev_attr_clear.attr,--PPS信号的“clear”事件的时间戳和序列号。 &dev_attr_mode.attr, &dev_attr_echo.attr,--PPS源是否具有回声(echo)功能。如果PPS源支持回声功能,它可以将PPS信号反馈到输出,这在某些应用中可能很有用。 &dev_attr_name.attr,--报告PPS源的名称。 &dev_attr_path.attr, NULL, };
pps支持的mode有:
/* Device/implementation parameters */ #define PPS_CAPTUREASSERT 0x01 /* capture assert events */ #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert event */ #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear event */ #define PPS_CANWAIT 0x100 /* can we wait for an event? */ #define PPS_CANPOLL 0x200 /* bit reserved for future use */ /* Kernel actions */ #define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ #define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ /* Timestamp formats */ #define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ #define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */
2.2 PPS核心层API
pps子系统核心层提供一下功能:
- pps_register_source:将PPS源注册到系统中。PPS源是指能够提供每秒一个脉冲信号的设备或接口。
- pps_unregister_source:用于从系统中注销一个已经注册的PPS源。
pps_register_source
pps_register_cdev
cdev_init--初始化字符设备结构体,操作函数集为ops_cdef_fops。
cdev_add
device_create
pps_unregister_source
pps_kc_remove
pps_unregister_cdev
- pps_event:用于报告一个PPS事件。当PPS源检测到一个脉冲时,驱动需要调用此函数来通知系统发生了PPS事件。
pps_event
--更新assert_tu/clear_tu。
pps_kc_event
hardpps
wake_up_interruptible_all--唤醒所有poll上可中断等待的进程。
kill_fasync--唤醒所有fasync上等待的进程。
pps字符设备文件操作函数集pps_cdef_fops:
static const struct file_operations pps_cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .poll = pps_cdev_poll, .fasync = pps_cdev_fasync, .compat_ioctl = pps_cdev_compat_ioctl, .unlocked_ioctl = pps_cdev_ioctl, .open = pps_cdev_open, .release = pps_cdev_release, };
其中pps_cdev_ioctl处理ioctl配置:
- PPS_GETPARAMS:用于从PPS设备获取当前的参数设置。用户空间程序可以通过这个ioctl调用获取PPS源的当前参数,如时间戳的格式、边缘检测模式等。
- PPS_SETPARAMS:用于设置PPS设备的参数。用户空间程序可以通过这个ioctl调用修改PPS源的参数,如时间戳的格式、边缘检测模式等。
- PPS_GETCAP:用于获取PPS设备的能力。用户空间程序可以通过这个ioctl调用获取PPS源支持的功能,如是否支持硬件时间戳等。
- PPS_FETCH:用于从PPS设备获取事件数据。用户空间程序可以通过这个ioctl调用获取PPS事件的时间戳和其他相关信息。
- PPS_KC_BIND:用于将PPS源绑定到特定的内核消费者。用户空间程序可以通过这个ioctl调用指定PPS源应该向哪个内核消费者发送事件。
3 PPS Souce驱动:pps-gpio
PPS-GPIO是Linux内核中用于处理通过GPIO引脚输入的PPS(Pulse Per Second,每秒一个脉冲)信号的驱动框架。
-
GPIO作为PPS信号源:PPS-GPIO驱动允许使用GPIO引脚作为PPS信号源。
- 中断处理:PPS-GPIO驱动通过注册GPIO中断来处理PPS信号。当检测到GPIO电平变化时,驱动会记录当前系统运行时刻,并把事件发送到用户空间。
- 时间同步:PPS信号可以用于高精度时间同步。通过结合PPS信号和NTP(Network Time Protocol)或其他时间同步协议,可以实现系统级的精确时间同步。
- PPS信号的生成和输出:除了作为PPS信号的消费者,PPS-GPIO也可以用于生成PPS信号。有些项目,如pps-gen-gpio,提供了通过GPIO引脚生成PPS信号的功能,这在测试和模拟PPS信号时非常有用。
module_platform_driver
pps_gpio_driver
pps_gpio_probe
pps_gpio_setup--根据dts配置获取GPIO pin、是否assert-falling-edge、echo、echo-active-ms等。
gpiod_to_irq--配置GPIO为中断。
pps_register_source
devm_request_irq
pps_gpio_irq_handler
pps_get_ts--获取当前时间戳。
gpiod_get_value--读取GPIO当前值,判断是上升沿(PPS_CAPTUREASSERT)还是下降沿(PPS_CAPTURECLEAR)。
pps_event--处理PPS事件。
pps_gpio_remove
4 pps-tools
4.1 pps-tools编译
Utilities
pps-tools...................................................... PPS-tools
4.2 ppsctl
ppsctl用于管理和配置PPS(Pulse Per Second,每秒脉冲)信号。
- -b: 选项来绑定内核PPS消费者。
- -B: 选项来解除绑定内核PPS消费者。
- -f: 选项来设置内核NTP PPS标志。
- -F: 选项来取消设置内核NTP PPS标志。
- -a: 选项来使用assert edge。
- -c: 选项来使用clear edge(默认)。
4.3 ppstest
ppstest用于测试 LinuxPPS API 接口。这个工具可以帮助用户检查和验证系统中的PPS(Pulse Per Second,每秒脉冲)信号源。
assert 表示PPS信号的上升沿,clear 表示下降沿。时间戳显示了PPS信号发生的时刻,序列编号用于跟踪PPS信号的顺序。
4.4 ppswatch
ppswatch连续打印PPS(Pulse Per Second,每秒脉冲)时间戳。这个工具对于需要高精度时间同步的应用非常有用,因为它可以显示PPS信号的时间戳,这些时间戳可以用来校准系统时间或进行其他时间相关的测量。
ppswatch 会输出PPS事件的时间戳,格式通常包括时间戳、序列号和偏移量。例如:
timestamp 是PPS事件发生的Unix时间戳,sequence 是PPS事件的序列号,offset 是PPS事件与预期时间的偏移量。