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_createpps_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事件与预期时间的偏移量。