27-Openwrt rtc htpdate system

有时候根据需求需要外加时钟芯片,实现掉电保存的功能,linux已经为我们实现了一系列的rtc时钟芯片,所以我们在选择的时候一般就直接选择内核里面已有的芯片。有了rtc后,需要将网络时间同步到rtc里面,目前更多使用htpdate,不适用ntp。

1.驱动添加


rtc芯片一般使用i2c方式连接,很多arm的内部i2c总是会有时序不稳定的情况,所以会使用gpio模拟i2c的形式,linux内部也已经支持该部分。

1.1、内核driver—》CONFIG

内核需要开启两个驱动

  • i2c相关
  • rtc相关

i2c选项

CONFIG_PACKAGE_kmod-i2c-core=y
CONFIG_PACKAGE_kmod-i2c-algo-bit=y
CONFIG_PACKAGE_kmod-i2c-gpio=y

rtc相关

CONFIG_RTC_DRV_PCF85063=y
1.2、内核platform—》dts/board_info

platform一般两种方式,dts和board_info,目前主流的就是只用dts的方式

1.2.1 dts方式

dtsi里面添加宏

i2c: i2c@0 {
 	compatible = "i2c-gpio";   ---》使用gpio模拟i2c的方式
 	gpios = <&pio 14 GPIO_ACTIVE_HIGH>,  --》指定好gpio
 			<&pio 15 GPIO_ACTIVE_HIGH>;
 	i2c-gpio,delay-us = <3>;
 	#address-cells = <1>;
 	#size-cells = <0>;
 	status = "disabled";
 };

dts里面开启宏,设置好pinctrl和添加rtc

&i2c {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&i2c0_pins>;

	pcf85063: rtc@51 {
		status = "okay";
		compatible = "nxp,pcf85063";
		reg = <0x51>;
	};
};

很多gpio是复用功能,所以需要在pinctrl里面将function设置成gpio,这个group的名称需要去看pinctrl里面对应芯片的定义。

&pio {
    i2c0_pins: i2c0-pins {
		mux {
			function = "gpio";
			group = "i2c0";
		};
	};
}
1.2.2 board_info方式

需要在/arch/mips/mtk/mt7621.c里面注册该i2c设备的i2c_board_info和i2c设备注册platform_device_register。

#define MT7621_GPIO_I2C_SDA 3
#define MT7621_GPIO_I2C_SCL 4

static struct i2c_gpio_platform_data mt7621_i2c_gpio_data = { 
    .sda_pin    = MT7621_GPIO_I2C_SDA,
    .scl_pin    = MT7621_GPIO_I2C_SCL,
};

static struct platform_device mt7621_i2c_gpio_device = { 
    .name       = "i2c-gpio",
    .id     = 0,
    .dev = { 
        .platform_data  = &mt7621_i2c_gpio_data,
    }   
};

static struct i2c_board_info mt7621_i2c_board_info[] __initdata = { 
    {   
        I2C_BOARD_INFO("pcf85063", 0x51),
    },  
};

void mt7621_common_init(void)
{
    u32 gpio_mode;

    gpio_mode = rt_sysc_r32(SYSC_REG_GPIO_MDOE);

    /*  
     * i2c gpio to gpio mode; use i2c-gpio driver
     */
    gpio_mode |= MT7621_GPIO_I2C_MODE;
    gpio_mode &= ~MT7621_GPIO_WDT_MODE_MASK;
    gpio_mode |= MT7621_GPIO_WDT_MODE;
    gpio_mode &= ~MT7621_GPIO_UART2_MODE_MASK;
    gpio_mode |= MT7621_GPIO_UART2_MODE;
    gpio_mode &= ~MT7621_GPIO_UART3_MODE_MASK;
    gpio_mode |= MT7621_GPIO_UART3_MODE;
    gpio_mode |= MT7621_GPIO_JTAG_GPIO_MODE;

    mt7621_gpio_init(gpio_mode);

    i2c_register_board_info(0, mt7621_i2c_board_info,
                ARRAY_SIZE(mt7621_i2c_board_info));
                
    platform_device_register(&mt7621_i2c_gpio_device);
}

i2c_board_info里面将设备的型号和地址传进去,这个在/drivers/i2c/i2c-boardinfo.c里面使用到。

platform_device_register接口里面将I2C的GPIO脚,和要probe的i2c-gpio名字传进去,这个在/drivers/i2c/busses/i2c-gpio.c里面会用到。

内核调试打印流程

[    3.560000] bus: 'platform': add driver rtc_cmos
[    3.560000] bus: 'platform': remove driver rtc_cmos
[    3.560000] driver: 'rtc_cmos': driver_release
[    3.560000] bus: 'i2c': add driver rtc-ds3232
[    3.560000] bus: 'i2c': driver_probe_device: matched device 0-0068 with driver rtc-ds3232
[    3.560000] bus: 'i2c': really_probe: probing driver rtc-ds3232 with device 0-0068
[    3.570000] device: 'rtc0': device_add
[    3.570000] rtc-ds3232 0-0068: rtc core: registered ds3232 as rtc0
[    3.580000] driver: '0-0068': driver_bound: bound to device 'rtc-ds3232'
[    3.580000] bus: 'i2c': really_probe: bound device 0-0068 to driver rtc-ds3232
[    3.580000] i2c /dev entries driver
[    3.590000] device class 'i2c-dev': registering
[    3.590000] device: 'i2c-0': device_add

2.i2c应用调试


只要上面的驱动配置都正常,i2c设备的调试先要有i2c适配器设备,即在/dev/下有i2c设备

root@openwrt:/dev# ls /dev/i2c-0 
/dev/i2c-0

有了i2c驱动之后,就可以使用busybox提供的各种i2c工具直接测试i2c是否正常。

2.1、检测到adapter

使用i2c-detect工具可以检测到adapter

root@openwrt:/dev# i2cdetect -l
i2c-0   i2c             1e000000.palmbus:i2c@0                  I2C adapter
2.2、寻找设备
root@openwrt:/dev# i2cdetect -r -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- 51 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

手把手教你使用 i2c-tools:https://blog.csdn.net/qq_38769551/article/details/124261403

2.3、读取全部寄存器值
root@openwrt:/# i2cdump -f -y 0 0x51
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
10: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
20: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
30: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
40: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
50: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
60: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
70: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
80: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
90: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
a0: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
b0: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
c0: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
d0: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??..............
e0: 00 05 00 00 21 14 08 26 03 10 22 80 80 80 80 80    .?..!??&??"?????
f0: b7 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ??.............
2.4、读取单个寄存器值
root@openwrt:/# i2cget -fy 0 0x51 0x07
0x26
root@openwrt:/# i2cget -fy 0 0x51 0x09
0x10

http://www.atmcu.com/2341.html

2.5、设置寄存器值
root@openwrt:/# i2cset -fy 0 0x51 0x12 0x04 b

3. “系统时间”与“硬件时间”


系统时间: 一般说来就是我们执行 date 命令看到的时间,linux系统下所有的时间调用(除了直接访问硬件时间的命令)都是使用的这个时间。

root@openwrt:/# date 
Tue May 21 15:05:26 CST 2019

时间设置

date -s "2020-03-23 11:22:50"

settimeofday

硬件时间: 外部RTC时钟,由电池供电来维持运行,所以适配器掉电了时间也可以正常运行

root@openwrt:/# hwclock 
Tue May 21 15:06:53 2019  0.000000 seconds
  • -r, --show 读取并打印硬件时钟(read hardware clock and print result)
  • -s, --hctosys 将硬件时钟同步到系统时钟(set the system time from the hardware clock)
  • -w, --systohc 将系统时钟同步到硬件时钟(set the hardware clock to the current system time)

drivers/rtc/下面有很多的时钟芯片驱动,一般用的都是Maxim/Dallas的I2C芯片,所以我们只需要添加i2c驱动即可。

原理如下

hwclock -w

    -> xioctl(RTC_SET_TIME);

      -> rtc_dev_ioctl()

        -> rtc_set_time()  

hwclock是busybox下面的一个程序,内部会调用xioctl函数,改函数会对驱动设备进行写数据。

busybox-1.22.1$ vim ./libbb/rtc.c

int FAST_FUNC rtc_xopen(const char **default_rtc, int flags)
{
    int rtc;

    if (!*default_rtc) {
        *default_rtc = "/dev/rtc";
        rtc = open(*default_rtc, flags);
        if (rtc >= 0)
            return rtc;
        *default_rtc = "/dev/rtc0";
        rtc = open(*default_rtc, flags);
        if (rtc >= 0)
            return rtc;
        *default_rtc = "/dev/misc/rtc";
    }   

    return xopen(*default_rtc, flags);
}

rtc驱动里面会初始化字符设备信息

cdev_init(&rtc->char_dev, &rtc_dev_fops);

static const struct file_operations rtc_dev_fops = { 
    .owner      = THIS_MODULE,
    .llseek     = no_llseek,
    .read       = rtc_dev_read,
    .poll       = rtc_dev_poll,
    .unlocked_ioctl = rtc_dev_ioctl,
    .open       = rtc_dev_open,
    .release    = rtc_dev_release,
    .fasync     = rtc_dev_fasync,
};

4.互联网时间htpdate


4.1 htpdate逻辑

htpdate启动参数

参数含义
-l
-s立即设置时间
-t
-d开启日志
-D后台运行
-m请求失败间隔时间
-M

将htpdata的日志打开-d

htpdate启动脚本位于/etc/init.d/htpdate

root@openwrt:/# cat /etc/init.d/htpdate
#!/bin/sh /etc/rc.common
# Copyright (C) 2006 OpenWrt.org

START=10
STOP=91
BIN=htpdate
DEFAULT=/etc/default/$BIN
RUN_D=/var/run
PID_F=$RUN_D/$BIN.pid

EXTRA_COMMANDS='save'

start() {
        local disabled

        config_load htpdate
        config_get_bool disabled htpdate disabled 0

        htpdate_stop

        [ "$disabled" -gt 0 ] || {
                [ -f $DEFAULT ] && . $DEFAULT
                mkdir -p $RUN_D
                $BIN -l -s -t -m 300 -M 600 -D $OPTIONS
        }
}

htpdate_stop() {
        [ -f $PID_F ] && {
                kill -9 $(cat $PID_F)
                rm -rf $PID_F
        }
}

stop() {
        htpdate_stop
}

域名配置文件

cat /etc/default/htpdate
OPTIONS="www.baidu.com www.taobao.com www.jd.com www.youku.com"

启动后进程内容如下:

htpdate -l -s -t -d -m 300 -M 600 -D www.baidu.com www.taobao.com www.jd.com www.youku.com

打开debug日志后,logread内容大概如下

Tue Oct 25 16:00:56 2022 user.info : htpdate version 1.1.1 started
Tue Oct 25 16:00:56 2022 user.info : burst: 1 try: 1 when: 200000
Tue Oct 25 16:00:57 2022 user.info : www.baidu.com             25 Oct 2022 08:00:57 GMT (0.055) => 0
Tue Oct 25 16:02:12 2022 user.info : burst: 1 try: 1 when: 400000
Tue Oct 25 16:02:12 2022 user.info : www.taobao.com            25 Oct 2022 08:02:13 GMT (0.048) => 1
Tue Oct 25 16:02:12 2022 user.info : burst: 1 try: 2 when: 400000
Tue Oct 25 16:02:13 2022 user.info : www.taobao.com            25 Oct 2022 08:02:14 GMT (0.047) => 1
Tue Oct 25 16:02:13 2022 user.info : burst: 1 try: 1 when: 600000
Tue Oct 25 16:02:13 2022 user.info : www.jd.com                25 Oct 2022 08:02:14 GMT (0.053) => 1
Tue Oct 25 16:02:13 2022 user.info : burst: 1 try: 2 when: 600000
Tue Oct 25 16:02:14 2022 user.info : www.jd.com                25 Oct 2022 08:02:15 GMT (0.054) => 1
Tue Oct 25 16:02:14 2022 user.info : burst: 1 try: 1 when: 800000
Tue Oct 25 16:02:14 2022 user.info : www.youku.com             25 Oct 2022 08:02:15 GMT (0.057) => 1
Tue Oct 25 16:02:14 2022 user.info : burst: 1 try: 2 when: 800000
Tue Oct 25 16:02:15 2022 user.info : www.youku.com             25 Oct 2022 08:02:16 GMT (0.059) => 1
Tue Oct 25 16:02:15 2022 user.info : #: 4 mean: 1 average: 0.750
Tue Oct 25 16:02:15 2022 user.info : Timezone: GMT+8 (CST,)
Tue Oct 25 16:02:15 2022 user.info : Setting 0.750 seconds
Tue Oct 25 16:02:16 2022 user.info : Set: Tue Oct 25 16:02:16 2022

htpdate逻辑1:默认如果时间同步成功后,会等待30min后再次请求

/* Sleep for 30 minutes after a time adjust or set */
sleep( DEFAULT_MIN_SLEEP );

30分钟后再次请求:

Tue Oct 25 16:32:16 2022 user.info : burst: 1 try: 1 when: 200000
Tue Oct 25 16:32:16 2022 user.info : www.baidu.com             25 Oct 2022 08:32:16 GMT (0.053) => 0
Tue Oct 25 16:33:31 2022 user.info : burst: 1 try: 1 when: 400000
Tue Oct 25 16:33:31 2022 user.info : www.taobao.com            25 Oct 2022 08:33:31 GMT (0.046) => 0
Tue Oct 25 16:34:46 2022 user.info : burst: 1 try: 1 when: 600000
Tue Oct 25 16:34:46 2022 user.info : www.jd.com                25 Oct 2022 08:34:47 GMT (0.054) => 1
Tue Oct 25 16:34:46 2022 user.info : burst: 1 try: 2 when: 600000
Tue Oct 25 16:34:47 2022 user.info : www.jd.com                25 Oct 2022 08:34:48 GMT (0.053) => 1
Tue Oct 25 16:34:47 2022 user.info : burst: 1 try: 1 when: 800000
Tue Oct 25 16:34:47 2022 user.info : www.youku.com             25 Oct 2022 08:34:48 GMT (0.056) => 1
Tue Oct 25 16:34:47 2022 user.info : burst: 1 try: 2 when: 800000
Tue Oct 25 16:34:48 2022 user.info : www.youku.com             25 Oct 2022 08:34:49 GMT (0.056) => 1
Tue Oct 25 16:34:48 2022 user.info : #: 4 mean: 1 average: 0.500
Tue Oct 25 16:34:48 2022 user.info : Timezone: GMT+8 (CST,)
Tue Oct 25 16:34:48 2022 user.info : Adjusting 0.500 seconds
Tue Oct 25 16:34:48 2022 user.info : Drift 256.15 PPM, 22.13 s/day

htpdate逻辑2:就算入参有-s立即设置时间,但是这个也只生效一次,第二次就变成adjust time了

	/* After first poll cycle do not step through time, only adjust */
	if ( setmode != 3 ) {
		setmode = 1;
	}

设置时间用asctime()函数,调整时间用adjtime()函数,还有一个调整内核时间adjtimex()函数

4.2 htp时间同步到rtc

修改htpdate的源码,在htpdate更新时间的位置,添加调用时间同步到rtc脚本,如下:

static void htpdate_save() {
    system("/etc/init.d/htpdate save 0");
}

脚本内如也位于/etc/init.d/htpdate,内容如下:

save() 
{
    local local_time=$(date '+%s')

    date -k
    # set to rtc
    hwclock -w

    uci set htpdate.htpdate.sync_time=$local_time
    uci commit htpdate
    if [ "$1" != "1" ]; then
            # call hotplug
            time-hotplug sync
    fi
}

完成htp时间同步到rtc中的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值