Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Точная синхронизация RTC в STM32F4
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
athlon64
Необходимо реализовать синхронизацию даты/времени в устройстве с внешним хостом (по SNTP и другим протоколам) с точностью до сотен микросекунд. Задача, получившая эталонное время вызывает следующую функцию и передаёт ей структуру с временем:
CODE
void RTC_TimeSync(vTime DateTime)
{
uint32_t TimeReg, DateReg;
uint32_t SSR_old, SSR_new;

// Преобразование времени в BCD
TimeReg = ((DateTime.Sec % 10) & 0x0F);
TimeReg |= (((DateTime.Sec / 10) & 0x07) << 4);
TimeReg |= (((DateTime.Min % 10) & 0x0F) << 8);
TimeReg |= (((DateTime.Min / 10) & 0x07) << 12);
TimeReg |= (((DateTime.Hour % 10) & 0x0F) << 16);
TimeReg |= (((DateTime.Hour / 10) & 0x03) << 20);
// Преобразование даты в BCD
DateReg = ((DateTime.Day % 10) & 0x0F);
DateReg |= (((DateTime.Day / 10) & 0x03) << 4);
DateReg |= (((DateTime.Month % 10) & 0x0F) << 8);
DateReg |= (((DateTime.Month / 10) & 0x01) << 12);
DateReg |= (((DateTime.Year % 10) & 0x0F) << 16);
DateReg |= (((DateTime.Year / 10) & 0x0F) << 20);

// Пишем ключи для доступа к регистрам RTC
RTC->WPR = RTC_KEY1;
RTC->WPR = RTC_KEY2;

if (!(RTC->ISR & RTC_ISR_INITF)) // RTC не в режиме инициализации?
{
RTC->ISR = (uint32_t)RTC_INIT_MASK; // Переходим в режим инициализации RTC
while (!(RTC->ISR & RTC_ISR_INITF)); // Ждём установки бита INIT в RTC->ISR
}

RTC->TR = TimeReg; // Пишем время в RTC
RTC->DR = DateReg; // Пишем дату в RTC

RTC->ISR &= ~RTC_ISR_INIT; // Выходим из режима инициализации RTC
RTC->WPR = 0xFF; // Включаем защиту регистров RTC


// Пишем ключи для доступа к регистрам RTC
RTC->WPR = RTC_KEY1;
RTC->WPR = RTC_KEY2;

SSR_old = RTC->SSR; // Текущее значение синхронного счётчика
SSR_new = RTC_PREDIV_S - ((DateTime.t100mks * 16384) / 10000); // Желаемое значение синхронного счётчика

while (RTC->ISR & RTC_ISR_SHPF);

if (SSR_new >= SSR_old) // К счётчику нужно прибавить (откат времени назад)
RTC->SHIFTR = SSR_new - SSR_old;
else // От счётчика нужно отнять (приращение времени)
RTC->SHIFTR = ((16383 - SSR_old) + SSR_new) | RTC_SHIFTR_ADD1S;

RTC->WPR = 0xFF;
}

RTC_PREDIV_S = 0x3FFF
RTC_PREDIV_A = 0x01

Дата и время (до секунд) синхронизируется нормально, а вот коррекция доли секунд не отрабатывает. Вообще. Т.е. как будто я пишу только дату и время до секунд.
Если заменить RTC простым счётом по таймеру, то синхронизация проходит верно и часы идут синхронно с удалёнными часами (т.е. пересчёт между форматами времени сделан верно).
khach
Смотрите в сторону PTP. Для начала гляньте AN3411 IEEE 1588 precision time protocol demonstration for STM32F107 connectivity line microcontroller. Конечно для достижения точности в наносекунды понадобится отдельный аппаратный TimeServer.
athlon64
Цитата(khach @ Nov 24 2012, 16:20) *
Смотрите в сторону PTP. Для начала гляньте AN3411 IEEE 1588 precision time protocol demonstration for STM32F107 connectivity line microcontroller. Конечно для достижения точности в наносекунды понадобится отдельный аппаратный TimeServer.

С получением точного времени у меня проблем нет. Проблема в коррекции субсекундного счётчика RTC, который пытается изменить процедура, приведённая выше.
Судя по RTC->SSR, коррекция не проходит, не понятно почему.
Делаю в соответствии с документом AN3371: Using the hardware real-time clock (RTC) in STM32 F0, F2, F3, F4 and L1 series of MCUs.

Оказалось всё просто.
После установки времени и даты в регистрах DR и TR, субсекундный счётчик сбрасывается и остаётся только вычислить и установить его новое значение.
Рабочий вариант процедуры:
CODE
void RTC_TimeSync(vTime DateTime)
{
uint32_t TimeReg, DateReg;

// Преобразование времени в BCD
TimeReg = ((DateTime.Sec % 10) & 0x0F);
TimeReg |= (((DateTime.Sec / 10) & 0x07) << 4);
TimeReg |= (((DateTime.Min % 10) & 0x0F) << 8);
TimeReg |= (((DateTime.Min / 10) & 0x07) << 12);
TimeReg |= (((DateTime.Hour % 10) & 0x0F) << 16);
TimeReg |= (((DateTime.Hour / 10) & 0x03) << 20);
// Преобразование даты в BCD
DateReg = ((DateTime.Day % 10) & 0x0F);
DateReg |= (((DateTime.Day / 10) & 0x03) << 4);
DateReg |= (((DateTime.Month % 10) & 0x0F) << 8);
DateReg |= (((DateTime.Month / 10) & 0x01) << 12);
DateReg |= (((DateTime.Year % 10) & 0x0F) << 16);
DateReg |= (((DateTime.Year / 10) & 0x0F) << 20);

// Пишем ключи для доступа к регистрам RTC
RTC->WPR = RTC_KEY1;
RTC->WPR = RTC_KEY2;

if (!(RTC->ISR & RTC_ISR_INITF)) // RTC не в режиме инициализации?
{
RTC->ISR = (uint32_t)RTC_INIT_MASK; // Переходим в режим инициализации RTC
while (!(RTC->ISR & RTC_ISR_INITF)); // Ждём установки бита INIT в RTC->ISR
}

RTC->TR = TimeReg; // Пишем время в RTC
RTC->DR = DateReg; // Пишем дату в RTC
RTC->SHIFTR = (RTC_PREDIV_S - ((t100mks * 16384) / 10000) + 1) | RTC_SHIFTR_ADD1S;

RTC->ISR &= ~RTC_ISR_INIT; // Выходим из режима инициализации RTC
RTC->WPR = 0xFF; // Включаем защиту регистров RTC
}

Всем спасибо
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.