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:
68
c/rtc-service/HELP.md
Normal file
68
c/rtc-service/HELP.md
Normal 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
45
c/rtc-service/PORTING.md
Normal 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.
|
||||
39
c/rtc-service/PROJECT_RELATIONS.md
Normal file
39
c/rtc-service/PROJECT_RELATIONS.md
Normal 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
53
c/rtc-service/README.md
Normal 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)
|
||||
21
c/rtc-service/rtc_service.h
Normal file
21
c/rtc-service/rtc_service.h
Normal 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 */
|
||||
330
c/rtc-service/rtc_service_core.c
Normal file
330
c/rtc-service/rtc_service_core.c
Normal 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(¤t);
|
||||
if (pres == RTC_SERVICE_PORT_OK) {
|
||||
status->initialized = rtc_service_is_datetime_valid(¤t);
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
40
c/rtc-service/rtc_service_port.h
Normal file
40
c/rtc-service/rtc_service_port.h
Normal 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 */
|
||||
127
c/rtc-service/rtc_service_port_k1921vk028.c
Normal file
127
c/rtc-service/rtc_service_port_k1921vk028.c
Normal 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;
|
||||
}
|
||||
51
c/rtc-service/rtc_service_types.h
Normal file
51
c/rtc-service/rtc_service_types.h
Normal 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 */
|
||||
Reference in New Issue
Block a user