• Главная
  • О сайте
  • Контакты
  • Архив
  • RSS

Модуль часов DS3231 и Fedora 18

Заметки, Сubieboard, Raspberry Pi, *nix22 августа 2014 г.
Как известно, Rasperry Pi не имеет встроенных часов, которые сохраняют системное время при отключении питания. Т.е. при загрузке системное время установлено в какое-то фантастическое значение на несколько лет назад. Для решения этой проблемы был куплен модуль DS3231 и тут началось. В интернете полно описаний как быстро и легко настраивается данный модуль. Однако эти описания как правило для ОС Raspbian. Аналогично легко настраивается данный модуль на плате Cubieboard с ОС Cubian. Т.е. во всех ОС семейства Debian/Ubuntu есть модуль ядра rtc-ds1307 с драйвером под эту микросхему, ну или есть варианты нарыть этот модуль и добавить в свое ядро. Но вот Федора начиная с версии 18 решила что этот модуль не нужен и удалили его из ядра, вроде как из-за того что устройство несколько самопальное и нисколько не стандартизированное. У меня же на Raspberry установлена Fedora 18 и на Cubieboard2 работает Fedora 19. Чем интересно использование модуля ядра rtc-ds1307. В этом варианте в системе появляется новое устройство /dev/rtc0, и для работы с ним имеется масса стандартных программ типа hwclock. Но без наличия устройства /dev/rtc0 с часами работать оказалось тоже весьма легко. В общем, два дня у меня ушло на разборки с Федорой и модулем ядра. После этого я бросил идею решить задачу на уровне ядра и за один день настроил скрипты для чтения и синхронизации времени без драйверов устройства. А значит задачка не очень сложная, особенно учитывая, что это мой самый первый опыт работы с Python. Доступ к DS3231 очень просто выполняется через шину I2C, а уж тут средств для реализации своего творческого потенциала более чем достаточно. Работать с DS3231 через I2C можно используя C/C++, Python или даже bash. Т.е. имеем основные задачи:
  • прочитать время из DS3231
  • установить время в DS3231
  • сверить и изменить системное время
  • автоматизировать процесс
Мой выбор пал на Python. Использование его для доступа к I2C и GPIO показалось мне наиболее простым и приятным. Ну и с чего-то же нужно было начинать осваивать этот язык.

Чтение и запись данных в DS3231

Для работы с DS3231 через шину I2C устанавливаем i2c-tools. Тут есть варианты и выбор, но выбрал i2c-tools как легковесный и достаточный инструментарий. Основная альтернатива это Arduino, но для моих задач это как из пушки по воробьям. i2c-tools можно ставить из репозитория, но сборка выполняется очень просто и быстро. После установки будут доступны такие программы как i2cdetect, i2cget, i2cset, i2cdump. Т.е. уже можно из командной строки или shell-скрипта делать с DS3231 уже почти все что угодно.

Примеры (раскрыть...)

1. Список доступных шин I2C:

$ sudo i2cdetect -l
i2c-0	i2c       	bcm2708_i2c.0                   	I2C adapter
i2c-1	i2c       	bcm2708_i2c.1                   	I2C adapter
2. Список устройств на шине и их статус:
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --
3. Прочитать содержимое регистра из устройства:
$ sudo i2cget -y 1 0x68 0
0x31
4. Список регистров DS3231:
00  seconds
01  minutes
02  hours
03  day
04  date
05  month
06  year
07  alarm1 sec
08  alarm1 min
09  alarm1 hour
0A  alarm1 date
0B  alarm2 min
0C  alarm2 hour
0D  alarm2 date
0E  control
0F  status
10  age offset
11  tempMSB
12  tempLSB
5. Более детально по управляющему и статусному регистрам:
status register 0Fh
bit7 OSF      Oscillator Stop Flag (if 1 then oscillator has stopped)
bit3 EN32kHz  Enable 32kHz output (1 if needed)
bit2 BSY      Busy with TCXO functions
bit1 A2F      Alarm 2 Flag - (1 if alarm2 was triggered)
bit0 A1F      Alarm 1 Flag - (1 if alarm1 was triggered)
control register 0Eh
bit7 EOSC   Enable Oscillator (1 if oscillator must be stopped when on battery)
bit6 BBSQW  Battery Backed Square Wave
bit5 CONV   Convert temperature (1 forces a conversion NOW)
bit4 RS2    Rate select - frequency of square wave output
bit3 RS1    Rate select
bit2 INTCN  Interrupt control (1 for use of the alarms and to disable square wave)
bit1 A2IE   Alarm2 interrupt enable (1 to enable)
bit0 A1IE   Alarm1 interrupt enable (1 to enable)

Вызов команд пакета i2c-tools нужно выполнять от рута, иначе программа не сможет получить доступ к устройству как на запись, так и на чтение:
$ i2cget -y 1 0x68 0
Error: Could not open file `/dev/i2c-1': Permission denied
Также i2c-tools содержит библиотеку Python для работы с I2C. Чтобы она была собрана при компиляции нужно указать следующее:
$ make EXTRA="py-smbus"
После компиляции необходимо выполнить её установку:
$ cd py-smbus $ sudo python setup.py install
Теперь в Python библиотека доступна для использования:
import smbus
Далее становится вопрос какие данные и функции используются для работы с DS3231. Все доступные регистры описаны выше - ничего сложного в общем-то, но с регистрами работать постоянно не совсем удобно. Напрашивается создание библиотеки для того чтобы реализовать простой и красивый интерфейс работы с устройством. В результате было выбрано готовое решение RTC_SDL_DS3231. Эту библиотеку из 200 строк можно взять за основу, изучать как работать в DS3231 и точить самому если что-то не устраивает. Я исправил только название файла и класса - удалил SDL_ из SDL_DS3231, слишком длинным показался. Там есть демонстрационная программа testSDL_DS3231.py, которая записывает текущее системное время в DS3231 и всячески демонстрирует возможности работы с микросхемой - показывает время и считывает данные датчика температуры. Для считывания времени из DS3231 используется скрипт ds3231time.py:
#!/usr/bin/env python
# prepares data string
# from DS3231 for system clock
import sys
import DS3231
ds3231 = DS3231.DS3231(1, 0x68)
sys.stdout.write("%s" % ds3231.read_datetime())
Пример вызова:
# ./ds3231time.py 
2014-08-22 18:18:14
Используется вызов sys.stdout.write вместо print чтобы в конце не вставлялся перевод строки. Для записи текущего системного времени в DS3231 вызывается функция:
ds3231.write_now()
Для записи текущего системного времени в DS3231 скрипт еще не готов, так как тут есть несколько нерешенных моментов. Дело в том, что DS3231 хранит время с точностью до 1 секунды. Т.е. чтобы избежать расхождений запись в DS3231 нужно выполнить в момент перехода между секундами. Особой надобности выверять точность нет, но хотелось бы попасть на границу хоть приблизительно. Системное время корректируется при наличии соединения с интернетом. После выполнения корректировки хочется выполнить автоматически корректировку времени в DS3231. Так что этот скрипт пока еще в процессе создания.

Корректировка системного времени

Для этого я воспользовался вызовом date из GNU coreutils. Текстовую строку со значением формирует скрипт ds3231time.py.
date --set="`ds3231time.py`" +"%Y-%m-%d %H:%M:%S"
Обращаю внимание, что ds3231time.py должен быть исполняемым файлом, или же нужно писать python ds3231time.py. Также я написал скрипт check.py для проверки разницы времени между системными часами и DS3231. Допустимой разницей времени установил 10 минут, и если разница меньше, то скрипт вернет ноль, иначе единицу. Код check.py:
#!/usr/bin/env python
#
# Check time difference between
# system clock and DS3231
#
# Exit values
# 0 - time is ok
# 1 - correction needed

import sys
import datetime
import DS3231

# allowed difference 10 min
delta_allowed = datetime.timedelta(minutes=10)

ds3231 = DS3231.DS3231(1, 0x68)
currenttime = datetime.datetime.now()
rtctime = ds3231.read_datetime()

if rtctime > currenttime :
    deltatime = rtctime - currenttime
else:
    deltatime = currenttime - rtctime

#print "system",  str(currenttime)
#print "ds3231", str(rtctime)
#print "delta ", str(deltatime)

if deltatime > delta_allowed :
    sys.exit(1)
else:
    sys.exit(0)
    print "System time is ok"
Ничего необычного, только один немного странный оператор if. Дело в том, что мне нужно вычислить разницу (расстояние) между двумя моментами времени. Оператор if обеспечивает вычитание меньшего значения из большего. Если вычитать из меньшей даты большую получим "интересный" результат. Вот пример обоих вариантов вычитания:
system 2014-08-22 20:52:57.883845
ds3231 2014-08-22 20:52:58
delta  -1 day, 23:59:59.883845
delta  0:00:00.538300
Получается, если значение "-1 day, 23:59:59.883845" сравнить с интервалом "10 минут", то потребуется коррекция времени, хотя на самом деле расстояние меньше секунды. Какое значение времени будет больше, system или ds3231, зависит от момента считывания времени и величины рассинхронизации времени, т.е. носит случайный характер.

Автоматизация работы

В Fedora18 используется systemd. Будем использовать для считывания времени из DS3231 запуск сервиса chronyd, который обеспечивает функционал NTP протокола и синхронизацию времени через интернет. Для корректировки сделан shell-скрипт hw_clock, который вызывается перед запуском chronyd. Он выполняет проверку разницы времени скриптом check.py, и если нужно выполняется корректировка системного времени. Запуск hw_clock прописывается в файле /usr/lib/systemd/system/chronyd.service в параметре ExecStartPre. Код hw_clock:
#!/bin/bash
#******************************************
# chronyd before start script
# for setting RPi time from DS3231
# 
# put this script at ExecStartPre
# (/usr/lib/systemd/system/chronyd.service)
# of systemctl start chronyd.service
#******************************************
RETVAL=0
#
# check system and DS3231 time difference
echo "System time checking"
/etc/ds3231/check.py  2>&1
RETVAL=$?
[ $RETVAL = 0 ] && exit 0
#
# system time is wrong and needs corretion
# read time from DS3231 and set system time
echo "System time correcting"
date --set="`/etc/ds3231/ds3231time.py`" +"%Y-%m-%d %H:%M:%S" 2>&1
#
# checking correction results
/etc/ds3231/check.py > /dev/null 2>&1
RETVAL=$?
[ $RETVAL != 0 ] && echo "Correction failed"
exit 0
Результат и лог корректировки времени можно увидеть командой:
# systemctl status chronyd
chronyd.service - NTP client/server
   Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled)
   Active: active (running) since Fri 2014-08-22 17:41:01 EEST; 2h 17min ago
  Process: 359 ExecStartPost=/usr/libexec/chrony-helper add-dhclient-servers (code=exited, status=0/SUCCESS)
  Process: 355 ExecStart=/usr/sbin/chronyd -u chrony $OPTIONS (code=exited, status=0/SUCCESS)
  Process: 346 ExecStartPre=/etc/chronyd_pid_fix (code=exited, status=0/SUCCESS)
  Process: 153 ExecStartPre=/etc/ds3231/hw_clock (code=exited, status=0/SUCCESS)
 Main PID: 357 (chronyd)
   CGroup: name=systemd:/system/chronyd.service
           └─357 /usr/sbin/chronyd -u chrony

Mar 07 22:04:21 rpi_fedora.home hw_clock[153]: System time correcting
Aug 22 17:40:59 rpi_fedora.home hw_clock[153]: 2014-08-22 17:40:59
Aug 22 17:41:01 rpi_fedora.home chronyd[357]: chronyd version 1.29 starting
Aug 22 17:41:01 rpi_fedora.home chronyd[357]: Linux kernel major=3 minor=6 patch=11
Aug 22 17:41:01 rpi_fedora.home chronyd[357]: hz=100 shift_hz=7 freq_scale=1.00000000 nominal_tick=10000 slew_delta_tick=833 max_tick_bias=1000 shift_pll=2
Aug 22 17:41:01 rpi_fedora.home chronyd[357]: Frequency 2.453 +/- 19.823 ppm read from /var/lib/chrony/drift
Aug 22 17:41:01 rpi_fedora.home systemd[1]: Started NTP client/server.
Aug 22 17:42:31 rpi_fedora.home chronyd[357]: Selected source 91.236.251.29
Aug 22 17:44:44 rpi_fedora.home chronyd[357]: Selected source 79.142.192.4
Aug 22 17:46:55 rpi_fedora.home chronyd[357]: Selected source 91.218.89.74

Итог

Скрипты разместил в папке /etc/ds3231:
-rwxrw-r-- 1 root root  779 Aug 22 20:13 check.py
-rw-rw-r-- 1 root root 6633 Aug 21 20:49 DS3231.py
-rwxrw-r-- 1 root root  201 Aug 22 18:18 ds3231time.py
-rwxrw-r-- 1 root root  742 Aug 22 20:15 hw_clock
Скачать скрипты
Установка ImageMagick и GD на веб-сервер
Компиляция git
Оставьте комментарий:

grin LOL cheese smile wink smirk rolleyes confused surprised big surprise tongue laugh tongue rolleye tongue wink raspberry blank stare long face ohh grrr gulp oh oh downer red face sick shut eye hmmm mad angry zipper kiss shock cool smile cool smirk cool grin cool hmm cool mad cool cheese vampire snake excaim question


Комментарий будет опубликован после проверки

     

  

(обязательно)

Рубрики
  • Новости1
  • Заметки15
    • *nix12
      • CentOS/RHEL3
      • Debian/Ubuntu1
    • C++1
    • Raspberry Pi4
    • Сubieboard4
  • Торрент трекер2
    • Раздачи1
«« Май 2022 »»
ПнВтСрЧтПтСбВс
      1
2345678
9101112131415
161718
19
202122
23242526272829
3031     
Hosting Ukraine
© Записки Вовика, 2022
Работает на MaxSite CMS | Время: 0.0804 | SQL: 16 | Память: 4.18MB | Вход