piątek, 30 czerwca 2023

Colibri iMX6 - RTC

W tym poście chciałbym opisać sposób obsługi zegara RTC na przykładzie Colibri iMX6. 


Układ IMX6 jest wyposażony w wewnętrzny zegar RTC. Standardowo RTC do podtrzymywania czasu potrzebuje dodatkowego zasilania w postaci baterii, tak aby czas został utrzymywany nawet po zaniku zasilania. Standardowo RTC jest taktowane z zegara 32.765kHz, który jest umieszczony w module. Układ iMX6DL jest także wyposażony w wewnętrzny zegar RTC, natomiast zgodnie z dokumentacją producenta [link], dla utrzymania stabilnego działania należy wykorzystać zewnętrzny oscylator (jak w przypadku płytki Colibri). Powodem tego jest wrażliwość wewnętrznego oscylatora na zmiany napięć czy temperatury. 

Piny zasilające można znaleźć w dokumentacji [link]:


Zasilanie RTC jest podłączane do pinu VCC_BATT. Tutaj może być zasilanie bateryjne, lub jeśli nie ma dodatkowego zasilania to jest wykorzystywane zasilanie 3.3V.

Ważnym elementem jest pobór prądu. RTC a tutaj dokładniej SRTC jest częścią domeny SNVS (ang. Secure Non Volatile Storage), Nie jest on przystosowany do niskiego poboru prądu. Z tego powodu zastosowanie baterii np. CR2032 w większości przypadków może nie być odpowiednie. Należałoby użyć baterii, którą można byłoby ładować podczas normalnej pracy urządzenia. Ten problem można obejść przez zastosowanie zewnętrznego zegara RTC podłączonego do I2C. 

Maksymalne oraz dopuszczalne wartości napięć na liniach wynoszą:

Komendy:

Sprawdzenie aktualnej godziny:

  1. Last login: Tue Jun  6 14:16:42 2023 from 150.153.100.17
  2. root@colibri-imx6:~# timedatectl
  3.       Local time: Thu 2023-06-29 06:21:05 UTC
  4.   Universal time: Thu 2023-06-29 06:21:05 UTC
  5.         RTC time: n/a
  6.        Time zone: Universal (UTC, +0000)
  7.  Network time on: yes
  8. NTP synchronized: yes
  9.  RTC in local TZ: no
  10. root@colibri-imx6:~#

Jak widać powyżej zegar systemowy działa. Zegar RTC już nie. 

Na początku należy sprawdzić jakie interfejsy RTC są dostępne:

  1. root@colibri-imx6:~# dmesg | grep -i rtc
  2. [    1.665067] rtc-ds1307: probe of 2-0068 failed with error -5
  3. [    1.673325] snvs_rtc 20cc000.snvs:snvs-rtc-lp: rtc core: registered 20cc000.snvs:snvs-r as rtc1
  4. [    2.459098] hctosys: unable to open rtc device (rtc0)

Jak widać powyżej dostępne są interfejsy rtc0 oraz rtc1. Gdzie rtc0 jest przypisany do zegara ds1307, który miałby być podłączony przez interfejs I2C i jest zdefiniowany w device tree. Zegar wewnętrzny modułu Colibri jest przypisany do interfejsu rtc1. 

Sprawdzę jaki zegar rtc jest wykorzystywany domyślnie:

  1. ls -l /dev/rtc*
  2. lrwxrwxrwx    1 root     root             4 Apr  1  2019 /dev/rtc -> rtc0
  3. crw-------    1 root     root      253,   1 Apr  1  2019 /dev/rtc1

Z tego co widać domyślnym interfejsem RTC jest rtc0. Wobec tego należy zmienić wartość z rtc0 na rtc1. 

Wykorzystam jeszcze demon udev w celu sprawdzenia parametrów części rtc1:

  1. root@colibri-imx6:~# udevadm info --attribute-walk --name=rtc1
  2.  
  3. Udevadm info starts with the device specified by the devpath and then
  4. walks up the chain of parent devices. It prints for every device
  5. found, all possible attributes in the udev rules key format.
  6. A rule to match, can be composed by the attributes of the device
  7. and the attributes from one single parent device.
  8.  
  9.   looking at device '/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-rtc-lp/rtc/rtc1':
  10.     KERNEL=="rtc1"
  11.     SUBSYSTEM=="rtc"
  12.     DRIVER==""
  13.     ATTR{date}=="1970-01-01"
  14.     ATTR{hctosys}=="0"
  15.     ATTR{max_user_freq}=="64"
  16.     ATTR{name}=="20cc000.snvs:snvs-r"
  17.     ATTR{since_epoch}=="11286"
  18.     ATTR{time}=="03:08:06"
  19.     ATTR{wakealarm}==""
  20.  
  21.   looking at parent device '/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-rtc-lp':
  22.     KERNELS=="20cc000.snvs:snvs-rtc-lp"
  23.     SUBSYSTEMS=="platform"
  24.     DRIVERS=="snvs_rtc"
  25.     ATTRS{driver_override}=="(null)"
  26.  
  27.   looking at parent device '/devices/soc0/soc/2000000.aips-bus/20cc000.snvs':
  28.     KERNELS=="20cc000.snvs"
  29.     SUBSYSTEMS=="platform"
  30.     DRIVERS==""
  31.     ATTRS{driver_override}=="(null)"
  32.  
  33.   looking at parent device '/devices/soc0/soc/2000000.aips-bus':
  34.     KERNELS=="2000000.aips-bus"
  35.     SUBSYSTEMS=="platform"
  36.     DRIVERS==""
  37.     ATTRS{driver_override}=="(null)"
  38.  
  39.   looking at parent device '/devices/soc0/soc':
  40.     KERNELS=="soc"
  41.     SUBSYSTEMS=="platform"
  42.     DRIVERS==""
  43.     ATTRS{driver_override}=="(null)"
  44.  
  45.   looking at parent device '/devices/soc0':
  46.     KERNELS=="soc0"
  47.     SUBSYSTEMS=="soc"
  48.     DRIVERS==""
  49.     ATTRS{family}=="Freescale i.MX"
  50.     ATTRS{machine}=="Toradex Colibri iMX6DL/S on Colibri Evaluation Board V3"
  51.     ATTRS{revision}=="1.4"
  52.     ATTRS{soc_id}=="i.MX6DL"
  53.     ATTRS{unique_id}=="191689d4d5ad04bd"

W celu uruchomienia zegara musimy dokonać zmian w plikach reguł:

  1. ls /etc/udev/rules.d/
  2. 10-imx.rules       autonet.rules      localextra.rules
  3. automount.rules    hotplug.rules      touchscreen.rules

Otwieramy plik localextra.rules:

  1. nano /etc/udev/rules.d/localextra.rules

W tym pliku należy zmienić parametr KERNEL=="rtc0" na "rtc1". 

  1. # There are a number of modifiers that are allowed to be used in some
  2. # of the different fields. They provide the following subsitutions:
  3. #
  4. # %n the "kernel number" of the device.
  5. #    For example, 'sda3' has a "kernel number" of '3'
  6. # %e the smallest number for that name which does not matches an existing node
  7. # %k the kernel name for the device
  8. # %M the kernel major number for the device
  9. # %m the kernel minor number for the device
  10. # %b the bus id for the device
  11. # %c the string returned by the PROGRAM
  12. # %s{filename} the content of a sysfs attribute
  13. # %% the '%' char itself
  14. #
  15.  
  16. # The first rtc device is symlinked to /dev/rtc
  17. KERNEL=="rtc1", SYMLINK+="rtc"
  18.  
  19. #The first framebuffer is symlinked to /dev/fb
  20. KERNEL=="fb0",  SYMLINK+="fb"
  21.  
  22. # Make all input devices read-write to the input group
  23. SUBSYSTEM=="input", GROUP="input", MODE="660"

Po czym zapisujemy plik i ponownie uruchamiamy płytkę. Po takiej operacji komendy pobrania danych z zegara wewnętrznego RTC są już dostępne.

Następnie konfigurujemy czas w RTC:

  1. root@colibri-imx6:~# hwclock
  2. Thu Jan  1 04:39:37 1970  0.000000 seconds
  3. root@colibri-imx6:~# hwclock -r
  4. Thu Jan  1 04:38:59 1970  0.000000 seconds
  5. root@colibri-imx6:~# hwclock -r -f /dev/rtc1
  6. Thu Jan  1 04:40:14 1970  0.000000 seconds
  7. root@colibri-imx6:~# date -s "2023-06-29 12:56:23"
  8. Thu Jun 29 12:56:23 UTC 2023
  9. root@colibri-imx6:~# hwclock -w
  10. root@colibri-imx6:~# hwclock -r
  11. Thu Jun 29 12:56:33 2023  0.000000 seconds

Teraz po odczytaniu danych zegara całość będzie działała poprawnie:

  1. root@colibri-imx6:~# timedatectl
  2.       Local time: Thu 2023-06-29 11:15:17 UTC
  3.   Universal time: Thu 2023-06-29 11:15:17 UTC
  4.         RTC time: Thu 2023-06-29 13:13:42
  5.        Time zone: UTC (UTC, +0000)
  6.  Network time on: yes
  7. NTP synchronized: yes
  8.  RTC in local TZ: no

W przypadku gdy układ działa zostanie przesłana informacja jak w poprzedniej komendzie. Jeśli będzie błąd to zostanie wysłana informacja o błędnych parametrach.

Sprawdzenie strefy czasowej:

  1. root@colibri-imx6:~# timedatectl | grep Time
  2.        Time zone: Universal (UTC, +0000)

Listę dostępnych stref czasowych można odczytać komendą:

  1. timedatectl list-timezones

W celu ograniczenia listy korzystamy z komendy grep:

  1. root@colibri-imx6:~# timedatectl list-timezones |  egrep  -o "Europe/W.*"
  2. Europe/Warsaw

Jeśli chcielibyśmy ją ustawić to niestety będzie to niemożliwe. Można je wylistować ale nie są obecne w systemie. Aby to zmienić należy doinstalować potrzebne pakiety:

  1. root@colibri-imx6:~#opkg update
  2. //........
  3. //........
  4. root@colibri-imx6:~# opkg install tzdata-europe
  5. Installing tzdata-europe (2017c) on root
  6. Downloading http://feeds.toradex.com/angstrom/feeds/v2017.12/ipk/glibc/all/tzdata-europe_2017c-r0.0_all.ipk.
  7. Configuring tzdata-europe.
  8. root@colibri-imx6:~# timedatectl set-timezone "Europe/Warsaw"
  9. root@colibri-imx6:~#

Dodatkowo można ustawić rodzaj wyświetlanego czasu jako UTC:

  1. timedatectl set-timezone UTC

Należy pamiętać, że w przypadku wykorzystywania NTP, nie można ustawić czasu w urządzeniu standardową komendą. 

  1. root@colibri-imx6:~# timedatectl set-time 16:10:30
  2. Failed to set time: Automatic time synchronization is enabled

Dopiero wyłączenie automatycznej konfiguracji czasu pozwoli na jego ręczne ustawienie:

  1. root@colibri-imx6:~# timedatectl set-time 16:10:30
  2. Failed to set time: Automatic time synchronization is enabled
  3. root@colibri-imx6:~# timedatectl set-ntp false
  4. root@colibri-imx6:~# timedatectl set-time 16:10:30
  5. root@colibri-imx6:~# timedatectl
  6.       Local time: Fri 2023-06-30 16:10:34 UTC
  7.   Universal time: Fri 2023-06-30 16:10:34 UTC
  8.         RTC time: Fri 2023-06-30 16:10:34
  9.        Time zone: UTC (UTC, +0000)
  10.  Network time on: no
  11. NTP synchronized: no
  12.  RTC in local TZ: no
  13. root@colibri-imx6:~#