feat(rtc-service): служба RTC с резервированным backup-томом

Перенесена из k1921vk028/1921vk028/lib/rtc_service.

Ядро rtc_service_core.c зависит только от stddef, string и собственных
заголовков; доступ к часам и backup-памяти вынесен в rtc_service_port.h.
Порт для K1921VK028 идёт в комплекте и служит образцом для следующего МК.
This commit is contained in:
2026-08-23 01:15:36 +03:00
parent 3dc636e012
commit 34cbd92808
9 changed files with 774 additions and 0 deletions

68
c/rtc-service/HELP.md Normal file
View File

@@ -0,0 +1,68 @@
# HELP — RTC Service
## Что делает модуль
Поддерживает:
- инициализацию RTC;
- валидацию даты/времени;
- сохранение последнего корректного времени в резервной области NVR;
- восстановление времени после перезагрузки по backup-данным.
## API
```c
RtcService_Result RtcService_Init(const RtcService_InitConfig* cfg, RtcService_Status* status);
RtcService_Result RtcService_GetDateTime(RtcService_DateTime* dt);
RtcService_Result RtcService_SetDateTime(const RtcService_DateTime* dt);
const char* RtcService_ResultToText(RtcService_Result res);
```
## Пример использования
```c
RtcService_InitConfig cfg = {
.preferred_clock_source = RTC_SERVICE_CLOCK_SOURCE_AUTO,
.fallback_datetime = {
.year = 25, .month = 1, .day = 1,
.weekday = 2, .hour = 12, .minute = 0, .second = 0
}
};
RtcService_Status status = {0};
RtcService_Result res = RtcService_Init(&cfg, &status);
```
```c
RtcService_DateTime now;
if (RtcService_GetDateTime(&now) == RTC_SERVICE_RESULT_OK) {
// now готов для чтения полей
}
```
## Конфигурация
1. Выберите `RtcService_InitConfig`:
- `preferred_clock_source` — пока рекомендуется `RTC_SERVICE_CLOCK_SOURCE_AUTO`;
- `fallback_datetime` — базовая дата на случай первого старта/коррупции.
2. Передайте структуру в `RtcService_Init`.
3. Используйте `RtcService_GetDateTime` для получения времени.
4. Для установки времени — `RtcService_SetDateTime`.
## Коды ошибок
`RtcService_ResultToText` возвращает строку:
- `OK`
- `INVALID_ARGUMENT`
- `INVALID_DATETIME`
- `HW_NOT_SUPPORTED`
- `BACKUP_CORRUPT`
- `BACKUP_UNAVAILABLE`
- `FAILED`
## Ограничения
- Проверка флага power-on reset в текущей платформенной части пока заглушка;
если нужно строгое поведение, в `rtc_service_port_k1921vk028.c` добавить чтение
реестра причины сброса/статуса.
- Выбор LSE/LSI на текущий момент реализован как `UNSUPPORTED` до появления
подходящего HAL-подхода.

45
c/rtc-service/PORTING.md Normal file
View File

@@ -0,0 +1,45 @@
# PORTING — RTC Service
## Что переносить
Библиотека строится как:
- переносимое ядро (`rtc_service_core.c`) — без зависимостей от HAL/реестров;
- порт (`rtc_service_port_k1921vk028.c`) — конкретные вызовы `plib028`.
## Шаги переноса на другой MCU
1. Создать новый `rtc_service_port_*.c` (и при необходимости `_port.h`).
2. Реализовать все функции из `rtc_service_port.h`:
- `RtcService_Port_InitClock`
- `RtcService_Port_EnableRtcClock`
- `RtcService_Port_IsPowerOnReset`
- `RtcService_Port_ReadRtc`
- `RtcService_Port_WriteRtc`
- `RtcService_Port_ReadBackup`
- `RtcService_Port_WriteBackup`
- `RtcService_Port_EraseBackup`
3. Собрать таблицу соответствий:
- форматы времени;
- единицы дня/месяца/года;
- адреса backup-области и ограничения размера.
4. Пройти чеклист:
- `RtcService_Init` с cold-reset и warm-reset;
- сценарий power-on без сохранённого backup;
- проверка CRC/валидности;
- задание корректной даты при запуске;
- корректная работа `RtcService_SetDateTime`.
## Что считать обязательным
- Выделить RAM/flash под backup-blob как минимум на 16 слов.
- Обеспечить атомарность записи backup (erase+write/защита от разрыва).
- Поддержать код возврата:
- ошибки портов;
- недоступность storage;
- отсутствие флага инициализации.
## Рекомендуемые расширения
- Реальное определение режима источника (`LSE`/`LSI`/`AUTO`).
- Реальная детекция `power-on reset`.
- Таймауты доступа к Flash.

View File

@@ -0,0 +1,39 @@
# PROJECT_RELATIONS.md — RTC Service
## Роль в проекте
RTC Service является отдельной переносимой библиотекой времени и восстановления
состояния через NVR (backup) памяти.
- Переносимое ядро: `rtc_service_core.c`
- Порт к текущему MCU: `rtc_service_port_k1921vk028.c`
- Публичный API: `rtc_service.h`, `rtc_service_types.h`
## Поток данных
`main.c` -> `RtcService_Init` -> `rtc_service_core.c` -> `rtc_service_port_*`
`RtcService_SetDateTime` -> `RtcService_Port_WriteRtc` -> `plib028_rtc`
`RtcService_SetDateTime` -> `RtcService_Port_WriteBackup` -> `plib028_bflash`
`RtcService_Init` -> `RtcService_Port_ReadBackup` -> `plib028_bflash`
`RtcService_Init` -> `RtcService_Port_ReadRtc` -> `plib028_rtc`
## Входные/выходные границы
- `rtc_service_core.c` (переносимый): только через порт-функции.
- `rtc_service_port_k1921vk028.c` (адаптер): напрямую зависит от `plib028_*`.
- `main.c` (приложение): использует только публичный API `rtc_service.h`.
## Данные и владение
- `RtcService_Status`, `RtcService_DateTime` и статусы ошибок — во владении
библиотечного API и хранятся во внешнем приложении.
- Backup-blob в NVR (`16` слов) — владелец `rtc_service_core.c`.
- Доступ к RTC/flash только через порт-функции.
## Важные ограничения
- `RtcService_Port_IsPowerOnReset` в текущей версии возвращает безопасное
значение по умолчанию (`0`), пока не подтверждена схема чтения флага сброса.
- Автовыбор источника RTC (`AUTO`) выбран из-за ограничений текущего PLIB API.

53
c/rtc-service/README.md Normal file
View File

@@ -0,0 +1,53 @@
# RTC Service
Независимый модуль для управления RTC с резервированным backup-томом на `K1921VK028`.
- Ядро: `rtc_service_core.c`
- Порт HAL: `rtc_service_port_k1921vk028.c`
- Публичный API: `rtc_service.h`, `rtc_service_types.h`, `rtc_service_port.h`
## Быстрый старт
```c
#include "rtc_service.h"
RtcService_InitConfig cfg = {
.preferred_clock_source = RTC_SERVICE_CLOCK_SOURCE_AUTO,
.fallback_datetime = {
.year = 25, .month = 1, .day = 1,
.weekday = 3, .hour = 0, .minute = 0, .second = 0
}
};
RtcService_Status status;
RtcService_Init(&cfg, &status);
```
Структура файлов:
```
lib/rtc_service/
├─ rtc_service_types.h
├─ rtc_service.h
├─ rtc_service_port.h
├─ rtc_service_port_k1921vk028.c
├─ rtc_service_core.c
├─ README.md
├─ PROJECT_RELATIONS.md
├─ HELP.md
└─ PORTING.md
```
## Интеграция
Для текущего проекта:
1. В `main/app/main.c` вызвать `RtcService_Init(...)` из `periph_init`.
2. Считать время `RtcService_GetDateTime(...)` где нужно.
3. Для изменения времени использовать `RtcService_SetDateTime(...)`.
## Полезные ссылки
- Обязательные правила: [TASK_RULES.md](../../TASK_RULES.md)
- Детальные связи: [PROJECT_RELATIONS.md](./PROJECT_RELATIONS.md)
- Пользовательская документация: [HELP.md](./HELP.md)
- Переход на другую платформу: [PORTING.md](./PORTING.md)

View File

@@ -0,0 +1,21 @@
/**
****************************************************************************
* @file rtc_service.h
* @brief Портируемый модуль RTC Service.
****************************************************************************
*/
#ifndef __RTC_SERVICE_H
#define __RTC_SERVICE_H
#include "rtc_service_types.h"
RtcService_Result RtcService_Init(const RtcService_InitConfig* cfg, RtcService_Status* status);
RtcService_Result RtcService_GetDateTime(RtcService_DateTime* dt);
RtcService_Result RtcService_SetDateTime(const RtcService_DateTime* dt);
const char* RtcService_ResultToText(RtcService_Result res);
#endif /* __RTC_SERVICE_H */

View File

@@ -0,0 +1,330 @@
/**
****************************************************************************
* @file rtc_service_core.c
* @brief Ядро RTC Service. Портируемая логика и валидация.
****************************************************************************
*/
#include "rtc_service.h"
#include "rtc_service_port.h"
#include <stddef.h>
#include <string.h>
#define RTC_SERVICE_MAGIC 0x52544353UL /* "RTCS" */
#define RTC_SERVICE_RECORD_VERSION 0x01UL
#define RTC_SERVICE_BACKUP_WORDS 16UL
#define RTC_SERVICE_BACKUP_DATA_WORDS 11UL
typedef struct {
uint32_t data[RTC_SERVICE_BACKUP_DATA_WORDS];
} RtcService_BackupPayload;
typedef union {
uint32_t raw[RTC_SERVICE_BACKUP_WORDS];
struct {
uint32_t magic;
uint32_t version;
uint32_t crc32;
uint32_t payload[RTC_SERVICE_BACKUP_DATA_WORDS];
} f;
} RtcService_BackupRecord;
static uint32_t s_last_backup_seq = 0U;
static uint8_t is_leap_year(uint16_t year)
{
/* year in range [0..99] interpreted as 2000..2099 */
uint16_t full_year = (uint16_t)(2000U + year);
if ((full_year % 4U) != 0U)
return 0U;
if ((full_year % 100U) == 0U)
return (full_year % 400U) == 0U;
return 1U;
}
static uint8_t days_in_month(uint16_t year, uint16_t month)
{
static const uint8_t dim[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month < 1U || month > 12U)
return 0U;
if (month != 2U)
return dim[month - 1U];
return is_leap_year(year) ? 29U : 28U;
}
static uint8_t rtc_service_is_datetime_valid(const RtcService_DateTime* dt)
{
if (dt == NULL)
return 0U;
if (dt->year > 99U || dt->month < 1U || dt->month > 12U)
return 0U;
{
uint8_t dim = days_in_month(dt->year, dt->month);
if (dt->day == 0U || dt->day > dim)
return 0U;
}
if (dt->hour > 23U || dt->minute > 59U || dt->second > 59U)
return 0U;
if (dt->weekday < 1U || dt->weekday > 7U)
return 0U;
return 1U;
}
static uint32_t crc32_fletcher(const uint32_t* data, size_t words)
{
uint32_t a = 0xFFFFU;
uint32_t b = 0xFFFFU;
for (size_t i = 0U; i < words; i++) {
uint32_t w = data[i];
a = (a + ((w >> 0U) & 0xFFU)) & 0xFFFFU;
b = (b + a) & 0xFFFFU;
a = (a + ((w >> 8U) & 0xFFU)) & 0xFFFFU;
b = (b + a) & 0xFFFFU;
a = (a + ((w >> 16U) & 0xFFU)) & 0xFFFFU;
b = (b + a) & 0xFFFFU;
a = (a + ((w >> 24U) & 0xFFU)) & 0xFFFFU;
b = (b + a) & 0xFFFFU;
}
return (b << 16U) | a;
}
static void rtc_service_pack_payload(const RtcService_DateTime* dt, uint32_t seq, RtcService_BackupPayload* payload)
{
memset(payload, 0, sizeof(*payload));
payload->data[0U] = RTC_SERVICE_MAGIC;
payload->data[1U] = seq;
payload->data[2U] = RTC_SERVICE_RECORD_VERSION;
payload->data[3U] = (uint32_t)dt->year;
payload->data[4U] = ((uint32_t)dt->month << 16U) | dt->day;
payload->data[5U] = ((uint32_t)dt->hour << 16U) | dt->minute;
payload->data[6U] = ((uint32_t)dt->weekday << 16U) | dt->second;
}
static void rtc_service_unpack_payload(const RtcService_BackupPayload* payload, RtcService_DateTime* dt, uint32_t* seq)
{
if (dt != NULL) {
dt->year = (uint16_t)(payload->data[3U] & 0xFFFFU);
dt->month = (uint16_t)((payload->data[4U] >> 16U) & 0xFFFFU);
dt->day = (uint16_t)(payload->data[4U] & 0xFFFFU);
dt->hour = (uint16_t)((payload->data[5U] >> 16U) & 0xFFFFU);
dt->minute = (uint16_t)(payload->data[5U] & 0xFFFFU);
dt->weekday = (uint16_t)((payload->data[6U] >> 16U) & 0xFFFFU);
dt->second = (uint16_t)(payload->data[6U] & 0xFFFFU);
}
if (seq != NULL)
*seq = payload->data[1U];
}
static RtcService_Result rtc_service_load_backup(RtcService_BackupPayload* payload, uint32_t* seq)
{
RtcService_BackupRecord rec;
if (payload == NULL)
return RTC_SERVICE_RESULT_INVALID_ARGUMENT;
if (RtcService_Port_ReadBackup(rec.raw, RTC_SERVICE_BACKUP_WORDS) != RTC_SERVICE_PORT_OK)
return RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE;
if (rec.f.magic != RTC_SERVICE_MAGIC)
return RTC_SERVICE_RESULT_BACKUP_CORRUPT;
if (rec.f.version != RTC_SERVICE_RECORD_VERSION)
return RTC_SERVICE_RESULT_BACKUP_CORRUPT;
{
uint32_t calc_crc = crc32_fletcher(rec.f.payload, RTC_SERVICE_BACKUP_DATA_WORDS);
if (calc_crc != rec.f.crc32)
return RTC_SERVICE_RESULT_BACKUP_CORRUPT;
}
memcpy(payload->data, rec.f.payload, sizeof(rec.f.payload));
if (seq != NULL)
*seq = payload->data[1U];
return RTC_SERVICE_RESULT_OK;
}
static RtcService_Result rtc_service_store_backup(const RtcService_DateTime* dt, uint32_t seq)
{
RtcService_BackupRecord rec;
RtcService_BackupPayload payload;
memset(&rec, 0, sizeof(rec));
rtc_service_pack_payload(dt, seq, &payload);
rec.f.magic = RTC_SERVICE_MAGIC;
rec.f.version = RTC_SERVICE_RECORD_VERSION;
rec.f.crc32 = crc32_fletcher(payload.data, RTC_SERVICE_BACKUP_DATA_WORDS);
memcpy(rec.f.payload, payload.data, sizeof(payload.data));
if (RtcService_Port_EraseBackup() != RTC_SERVICE_PORT_OK)
return RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE;
if (RtcService_Port_WriteBackup(rec.raw, RTC_SERVICE_BACKUP_WORDS) != RTC_SERVICE_PORT_OK)
return RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE;
return RTC_SERVICE_RESULT_OK;
}
static RtcService_Result rtc_service_sanitize_weekday(RtcService_DateTime* dt)
{
if (dt == NULL)
return RTC_SERVICE_RESULT_INVALID_ARGUMENT;
if (!rtc_service_is_datetime_valid(dt))
return RTC_SERVICE_RESULT_INVALID_DATETIME;
/* Если weekday некорректен, корректно пересчитываем по Zeller (для 2000..2099). */
if (dt->weekday < 1U || dt->weekday > 7U) {
int16_t q = (int16_t)dt->day;
int16_t m = (int16_t)dt->month;
int16_t Y = 2000 + (int16_t)dt->year;
if (m < 3) {
m += 12;
Y -= 1;
}
{
int16_t K = Y % 100;
int16_t J = Y / 100;
int16_t h = (q + (13 * (m + 1)) / 5 + K + K / 4 + J / 4 + 5 * J) % 7;
dt->weekday = (uint16_t)((h + 5U) % 7U + 1U);
}
}
return RTC_SERVICE_RESULT_OK;
}
RtcService_Result RtcService_Init(const RtcService_InitConfig* cfg, RtcService_Status* status)
{
if (cfg == NULL || status == NULL)
return RTC_SERVICE_RESULT_INVALID_ARGUMENT;
memset(status, 0, sizeof(*status));
s_last_backup_seq = 0U;
{
RtcService_PortResult pres = RtcService_Port_InitClock(cfg->preferred_clock_source,
&status->active_clock_source);
if (pres != RTC_SERVICE_PORT_OK) {
if (pres == RTC_SERVICE_PORT_RESULT_UNSUPPORTED_CLOCK)
return RTC_SERVICE_RESULT_HW_NOT_SUPPORTED;
return RTC_SERVICE_RESULT_FAILED;
}
}
RtcService_Port_EnableRtcClock();
{
RtcService_DateTime current = {0};
RtcService_PortResult pres = RtcService_Port_ReadRtc(&current);
if (pres == RTC_SERVICE_PORT_OK) {
status->initialized = rtc_service_is_datetime_valid(&current);
}
}
status->valid_reset = RtcService_Port_IsPowerOnReset();
RtcService_BackupPayload payload;
memset(&payload, 0, sizeof(payload));
RtcService_DateTime backup_dt = {0};
uint32_t seq = 0U;
RtcService_Result bres = rtc_service_load_backup(&payload, &seq);
uint8_t has_valid_backup = 0U;
if ((bres == RTC_SERVICE_RESULT_OK)) {
RtcService_DateTime unpacked = {0};
rtc_service_unpack_payload(&payload, &unpacked, NULL);
if (rtc_service_is_datetime_valid(&unpacked)) {
backup_dt = unpacked;
has_valid_backup = 1U;
}
}
s_last_backup_seq = seq;
if (has_valid_backup != 0U) {
if ((status->valid_reset != 0U) || (status->initialized == 0U)) {
status->restored_from_backup = 1U;
if (RtcService_SetDateTime(&backup_dt) != RTC_SERVICE_RESULT_OK)
return RTC_SERVICE_RESULT_FAILED;
return RTC_SERVICE_RESULT_OK;
}
return RTC_SERVICE_RESULT_OK;
}
if (RtcService_SetDateTime(&cfg->fallback_datetime) != RTC_SERVICE_RESULT_OK)
return RTC_SERVICE_RESULT_INVALID_DATETIME;
if (rtc_service_store_backup(&cfg->fallback_datetime, s_last_backup_seq + 1U) != RTC_SERVICE_RESULT_OK) {
return RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE;
}
s_last_backup_seq += 1U;
status->initialized = 1U;
return RTC_SERVICE_RESULT_OK;
}
RtcService_Result RtcService_GetDateTime(RtcService_DateTime* dt)
{
if (dt == NULL)
return RTC_SERVICE_RESULT_INVALID_ARGUMENT;
if (RtcService_Port_ReadRtc(dt) != RTC_SERVICE_PORT_OK)
return RTC_SERVICE_RESULT_FAILED;
if (!rtc_service_is_datetime_valid(dt))
return RTC_SERVICE_RESULT_INVALID_DATETIME;
return RTC_SERVICE_RESULT_OK;
}
RtcService_Result RtcService_SetDateTime(const RtcService_DateTime* dt)
{
RtcService_Result res;
RtcService_DateTime normalized = {0};
if (dt == NULL)
return RTC_SERVICE_RESULT_INVALID_ARGUMENT;
memcpy(&normalized, dt, sizeof(normalized));
res = rtc_service_sanitize_weekday(&normalized);
if (res != RTC_SERVICE_RESULT_OK)
return res;
if (!rtc_service_is_datetime_valid(&normalized))
return RTC_SERVICE_RESULT_INVALID_DATETIME;
if (RtcService_Port_WriteRtc(&normalized) != RTC_SERVICE_PORT_OK)
return RTC_SERVICE_RESULT_FAILED;
if (rtc_service_store_backup(&normalized, s_last_backup_seq + 1U) == RTC_SERVICE_RESULT_OK) {
s_last_backup_seq += 1U;
}
return RTC_SERVICE_RESULT_OK;
}
const char* RtcService_ResultToText(RtcService_Result res)
{
switch (res) {
case RTC_SERVICE_RESULT_OK:
return "OK";
case RTC_SERVICE_RESULT_INVALID_ARGUMENT:
return "INVALID_ARGUMENT";
case RTC_SERVICE_RESULT_INVALID_DATETIME:
return "INVALID_DATETIME";
case RTC_SERVICE_RESULT_HW_NOT_SUPPORTED:
return "HW_NOT_SUPPORTED";
case RTC_SERVICE_RESULT_BACKUP_CORRUPT:
return "BACKUP_CORRUPT";
case RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE:
return "BACKUP_UNAVAILABLE";
default:
return "FAILED";
}
}

View File

@@ -0,0 +1,40 @@
/**
****************************************************************************
* @file rtc_service_port.h
* @brief Адаптерная прослойка для RTC Service.
****************************************************************************
*/
#ifndef __RTC_SERVICE_PORT_H
#define __RTC_SERVICE_PORT_H
#include <stddef.h>
#include <stdint.h>
#include "rtc_service_types.h"
typedef enum {
RTC_SERVICE_PORT_OK = 0,
RTC_SERVICE_PORT_RESULT_UNSUPPORTED_CLOCK,
RTC_SERVICE_PORT_RESULT_HW_ERROR,
RTC_SERVICE_PORT_RESULT_NO_STORAGE,
} RtcService_PortResult;
RtcService_PortResult RtcService_Port_InitClock(RtcService_ClockSource preferred_source,
RtcService_ClockSource* actual_source);
void RtcService_Port_EnableRtcClock(void);
uint8_t RtcService_Port_IsPowerOnReset(void);
RtcService_PortResult RtcService_Port_ReadRtc(RtcService_DateTime* dt);
RtcService_PortResult RtcService_Port_WriteRtc(const RtcService_DateTime* dt);
RtcService_PortResult RtcService_Port_ReadBackup(uint32_t* buffer, size_t words);
RtcService_PortResult RtcService_Port_WriteBackup(const uint32_t* buffer, size_t words);
RtcService_PortResult RtcService_Port_EraseBackup(void);
#endif /* __RTC_SERVICE_PORT_H */

View File

@@ -0,0 +1,127 @@
/**
****************************************************************************
* @file rtc_service_port_k1921vk028.c
* @brief Порт RTC Service для семейства K1921VK028 (plib028).
****************************************************************************
*/
#include "rtc_service_port.h"
#include "rtc_service_types.h"
#include "plib028_bflash.h"
#include "plib028_rtc.h"
#include "plib028_rcu.h"
#include <stddef.h>
#include <stdint.h>
/* Вывод: в данном HAL API явного выбора источника LSE/LSI на уровне C API нет,
поэтому конфигурирование делается как AUTO с флагом активного источника AUTO. */
RtcService_PortResult RtcService_Port_InitClock(RtcService_ClockSource preferred_source,
RtcService_ClockSource* actual_source)
{
if (actual_source != NULL) {
*actual_source = RTC_SERVICE_CLOCK_SOURCE_AUTO;
}
if (preferred_source != RTC_SERVICE_CLOCK_SOURCE_AUTO) {
/* На текущей ревизии драйвер NIIЭТ не предоставляет стабильных API для ручного
* выбора LSE/LSI через публичные функции. Рекомендуется использовать AUTO. */
return RTC_SERVICE_PORT_RESULT_UNSUPPORTED_CLOCK;
}
/* Включение тактирования RTC в RCC */
RCU_APBClk0Cmd(RCU_APBClk0_RTC, ENABLE);
return RTC_SERVICE_PORT_OK;
}
void RtcService_Port_EnableRtcClock(void)
{
/* На K1921VK028 дополнительной явной подготовки источника после включения тактирования
* RTC/APB-блока, как правило, не требуется. Реализация оставлена явной для port API. */
(void)0;
}
uint8_t RtcService_Port_IsPowerOnReset(void)
{
/* TODO: при наличии отдельного API/регистра статуса сброса заменить на реальную проверку.
* Безопасная дефолтная стратегия — считать, что это не cold reset. */
return 0U;
}
RtcService_PortResult RtcService_Port_ReadRtc(RtcService_DateTime* dt)
{
if (dt == NULL)
return RTC_SERVICE_PORT_RESULT_HW_ERROR;
RTC_Time_TypeDef t = {0};
RTC_Date_TypeDef d = {0};
RTC_GetTime(RTC_Format_BIN, &t);
RTC_GetDate(RTC_Format_BIN, &d);
dt->year = (uint16_t)d.Year;
dt->month = (uint16_t)d.Month;
dt->day = (uint16_t)d.Day;
dt->weekday = (uint16_t)d.Weekday;
dt->hour = (uint16_t)t.Hour;
dt->minute = (uint16_t)t.Minute;
dt->second = (uint16_t)t.Second;
return RTC_SERVICE_PORT_OK;
}
RtcService_PortResult RtcService_Port_WriteRtc(const RtcService_DateTime* dt)
{
RTC_Time_TypeDef t = {0};
RTC_Date_TypeDef d = {0};
if (dt == NULL)
return RTC_SERVICE_PORT_RESULT_HW_ERROR;
t.PSecond = 0U;
t.Hour = dt->hour;
t.Minute = dt->minute;
t.Second = dt->second;
d.Year = dt->year;
d.Month = dt->month;
d.Day = dt->day;
d.Weekday = (RTC_Weekday_TypeDef)dt->weekday;
RTC_SetTime(RTC_Format_BIN, &t);
RTC_SetDate(RTC_Format_BIN, &d);
RTC_ShadowCmd(DISABLE);
RTC_ShadowCmd(ENABLE);
return RTC_SERVICE_PORT_OK;
}
RtcService_PortResult RtcService_Port_ReadBackup(uint32_t* buffer, size_t words)
{
if ((buffer == NULL) || (words == 0U))
return RTC_SERVICE_PORT_RESULT_HW_ERROR;
for (size_t i = 0U; i < words; ++i) {
BFLASH_ReadData((uint32_t)i, &buffer[i], BFLASH_Region_NVR);
}
return RTC_SERVICE_PORT_OK;
}
RtcService_PortResult RtcService_Port_WriteBackup(const uint32_t* buffer, size_t words)
{
if ((buffer == NULL) || (words == 0U))
return RTC_SERVICE_PORT_RESULT_HW_ERROR;
for (size_t i = 0U; i < words; ++i) {
BFLASH_WriteData((uint32_t)i, (uint32_t*)&buffer[i], BFLASH_Region_NVR);
}
return RTC_SERVICE_PORT_OK;
}
RtcService_PortResult RtcService_Port_EraseBackup(void)
{
BFLASH_ErasePage(0UL, BFLASH_Region_NVR);
return RTC_SERVICE_PORT_OK;
}

View File

@@ -0,0 +1,51 @@
/**
****************************************************************************
* @file rtc_service_types.h
* @brief Общие типы для модуля RTC Service.
****************************************************************************
*/
#ifndef __RTC_SERVICE_TYPES_H
#define __RTC_SERVICE_TYPES_H
#include <stdint.h>
typedef enum {
RTC_SERVICE_CLOCK_SOURCE_AUTO = 0,
RTC_SERVICE_CLOCK_SOURCE_LSE,
RTC_SERVICE_CLOCK_SOURCE_LSI,
} RtcService_ClockSource;
typedef enum {
RTC_SERVICE_RESULT_OK = 0,
RTC_SERVICE_RESULT_INVALID_ARGUMENT,
RTC_SERVICE_RESULT_INVALID_DATETIME,
RTC_SERVICE_RESULT_HW_NOT_SUPPORTED,
RTC_SERVICE_RESULT_BACKUP_CORRUPT,
RTC_SERVICE_RESULT_BACKUP_UNAVAILABLE,
RTC_SERVICE_RESULT_FAILED,
} RtcService_Result;
typedef struct {
uint16_t year;
uint16_t month;
uint16_t day;
uint16_t weekday; // 1..7, 1=понедельник
uint16_t hour;
uint16_t minute;
uint16_t second;
} RtcService_DateTime;
typedef struct {
RtcService_ClockSource preferred_clock_source;
RtcService_DateTime fallback_datetime;
} RtcService_InitConfig;
typedef struct {
uint8_t initialized;
uint8_t restored_from_backup;
uint8_t valid_reset;
RtcService_ClockSource active_clock_source;
} RtcService_Status;
#endif /* __RTC_SERVICE_TYPES_H */