update 07.10.2018: знайшов помилку в бібліотеці у функції задавання часу. Виправив бібліотеку і трішки змінив саму бібліотеку. Всі функції залишились на своєму місці. Коли викликається функція взяття часу, то крім часу визначається і дата. Коли викликається дата, то просто повертається дата, яку вже вирахували під час взяття часу. Якщо, до взяття дати, функція взяття часу, ніколи не викликалась, то в функції взяття дати, виклик функції взяття часу, станеться автоматично. Так само автоматично оновиться дата при її зміні.
Передмова
Це продовження статті "
STM32: Годинник реального часу на stm32f1xx. Проблеми, рішення (частина перша)". В першій частині описано проблеми RTC на STM32F1xx серії та невдалі спроби подолати їх за допомоги HAL драйверу. Проблема полягає в тому, що при використанні HAL_RTC - лічильник RTC, який рахує секунди, функціями HAL драйверу, "обрізається" в межах доби. Максимально 86400 секунд. А дата зберігається окремо просто в RAM пам'яті мікроконтролера. Тому при вимкнені мікроконтролеру чи перехід в сон, втрачається дата RTC. Можна записати дату в backup регістри, але це не врятує, якщо мікроконтролер буде без живлення при переході на нову дату. Тому на серії STM32F1xx єдиний вихід правильної роботи RTC це враховувати в лічильнику RTC секунди, як часу так і дати - разом, а за допомоги розрахунків видобувати вже поточну час і дату. Ну і якщо вже використовуємо HAL драйвера та не створювати дублікат бібліотеки HAL_RTC, я просто створив невеличку бібліотеку зі зміненими деякими функціями:
HAL_StatusTypeDef mRTC_GetTime(RTC_HandleTypeDef* hrtc, RTC_TimeTypeDef* sTime, uint32_t Format);
HAL_StatusTypeDef mRTC_GetDate(RTC_HandleTypeDef* hrtc, RTC_DateTypeDef* sDate, uint32_t Format);
HAL_StatusTypeDef mRTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_StatusTypeDef mRTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
HAL_StatusTypeDef mRTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);
HAL_StatusTypeDef mRTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);
Це функції встановлення/взяття часу, та встановелння/взяття дати, а також встановлення будильнику.
Щоб коректно задавати час, та брати час слід використовувати саме ці функції, а не аналогічні функції, які надаються HAL драйверами.
Налаштування і запуск RTC в CubeMX
Всі налаштування RTC в CubeMX залишаються без змін і не мають ніяких особливостей. Як налаштувати роботу RTC можна переглянути в попередній
статті, все абсолютно так само в однойменному розділі
"Налаштування і запуск RTC в CubeMX".
Та не забудьте увімкнути переривання від RTC раз на секунду:
 |
Увімкнути переривання від RTC раз на секунду |
Це потрібно для демонстрації роботи RTC.
Додаткова бібліотека RTC з модифікованими функціями
Додаткова бібліотека mRTC з модіфікованими функціями містить два файли:
В цій бібліотеці є змінені функції аналогічні функціям HAL драйверу, так і додаткові для зручності використання.
Список всіх функцій додаткової бібліотеки mRTC:
#define TIME_COUNTER(hrtc) mRTC_ReadCounter(hrtc) // повертає значення лічильнику часу
#define ALARM_COUNTER(hrtc) mRTC_ReadAlarmCounter(hrtc) // повертає значення лічильнику будильника
#define SECOND(hrtc, format) mRTC_GetSecond(hrtc, format) // повертає секунди
#define MINUTE(hrtc, format) mRTC_GetMinute(hrtc, format) // повертає хвилини
#define HOUR(hrtc, format) mRTC_GetHour(hrtc, format) // повертає години
#define WEEKDAY(hrtc) mRTC_GetWeekDay(hrtc) // повертає день тижня
#define DAY(hrtc, format) mRTC_GetDay(hrtc, format) // повертає день
#define MONTH(hrtc, format) mRTC_GetMonth(hrtc, format) // повертає місяць
#define YEAR(hrtc, format) mRTC_GetYear(hrtc, format) // повертає рік
void mRTC_Begin(RTC_HandleTypeDef* hrtc); // оновлює дату в буфері, викликати раз після увімкнення MCU
uint8_t mRTC_GetSecond(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає секунди
uint8_t mRTC_GetMinute(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає хвилини
uint8_t mRTC_GetHour(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає години
uint8_t mRTC_GetWeekDay(RTC_HandleTypeDef* hrtc); // повертає день тижня
uint8_t mRTC_GetDay(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає день
uint8_t mRTC_GetMonth(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає місяць
uint16_t mRTC_GetYear(RTC_HandleTypeDef* hrtc, uint32_t Format); // повертає рік
HAL_StatusTypeDef mRTC_GetTime(RTC_HandleTypeDef* hrtc, RTC_TimeTypeDef* sTime, uint32_t Format); // повертає час в структуру часу
HAL_StatusTypeDef mRTC_GetDate(RTC_HandleTypeDef* hrtc, RTC_DateTypeDef* sDate, uint32_t Format); // повертає дату в структуру дати
HAL_StatusTypeDef mRTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format); // задає час з структури часу
HAL_StatusTypeDef mRTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format); // задає дату з структури дати
HAL_StatusTypeDef mRTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format); // задає час будильника в межах доби
HAL_StatusTypeDef mRTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format); // задає час будильника в межах доби з перериваннями
На початку програми, після увімкнення MCU потрібно один раз викликати функцію
mRTC_Begin для оновлення дати в буфері. Щоб не вираховувати кожного разу день тижня, день, місяць і рік у функціях:
mRTC_GetWeekDay, mRTC_GetDay, mRTC_GetMonth, mRTC_GetYear, та
mRTC_GetDate - значення беруться просто з буферу. Кожного разу коли викликається функція
mRTC_GetTime, автоматично оновлюється дата в буфері. Якщо користуватись тільки функцією
RTC_GetDate про зміну дня не потрібно турбуватись, функція
mRTC_GetTime, при зміні дня, викличеться автоматично. Якщо користуватись функціями взяття окремо годин (mRTC_GetHour), хвилин (mRTC_GetMinute), секунд (mRTC_GetSecond), дня (mRTC_GetDay), місяця (mRTC_GetMonth), року (mRTC_GetYear) і дня тижня (mRTC_GetWeekDay), то теж потрібно самому контролювати вчасне оновлення дати в буфері викликом функції
mRTC_Begin, або
mRTC_GetTime.
Демонстраційна програма
- За допомоги CubeMX створіть проект з налаштуваннями RTC, як зазначено вище у розділі "Налаштування і запуск RTC в CubeMX". Дайте йому ім'я, та згенеруйте код ініціалізації для свого засобу розробки.
- Відкрийте чи імпортуйте проект створений програмою CubeMX у своєму засобі розробки. В мене це Atolic TrueStudio, професійний засіб розробки абсолютно безкоштовний.
- Додайте файли бібліотеки до свого проекту. Файли з розширенням ".h" до теки "inc", а файли з розширенням ".c" до теки "src".
- Відрийте файл "main.c" і в зазначені для користувача ділянки коду додаємо демонстраційний код.
Приєднуємо до програми бібліотеку "mRTC":
/* USER CODE BEGIN Includes */
#include "m_rtc.h"
/* USER CODE END Includes */
Оголошуємо глобальні змінні та структури часу і дати:
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t aShowTime[12] = {0};
uint8_t aShowDate[12] = {0};
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
/* USER CODE END PV */
Оголошуємо прототипи функцій показу часу/дати та встановлення часу/дати:
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
void Show_RTC_Calendar(void);
void setDataTime(void);
/* USER CODE END PFP */
Викликаємо функції: увімкнути переривання RTC кожної секунди, оновити в буфері значення дати, показати дату і час.
/* USER CODE BEGIN 2 */
HAL_RTCEx_SetSecond_IT(&hrtc);
// setDataTime(); // якщо потрібно встановити початковий час і дату, то розкоментуйте рядок
mRTC_Begin(&hrtc);
Show_RTC_Calendar();
/* USER CODE END 2 */
Та не забуваємо написати самі функції в користувацькому блоці 4:
/* USER CODE BEGIN 4 */
void HAL_RTCEx_RTCEventCallback (RTC_HandleTypeDef* hrtc)
{
Show_RTC_Calendar();
}
void Show_RTC_Calendar(void)
{
/* Note: need to convert in decimal value in using __LL_RTC_CONVERT_BCD2BIN helper macro */
/* Display time Format : hh:mm:ss */
mRTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
sprintf((char*)aShowTime,"%.2d:%.2d:%.2d", sTime.Hours, sTime.Minutes, sTime.Seconds);
/* Display date Format : mm-dd-yy */
mRTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
sprintf((char*)aShowDate,"%.2d-%.2d-%.2d-%.2d", sDate.Date, sDate.Month, sDate.Year, sDate.WeekDay);
}
void setDataTime(void)
{
sTime.Hours = 21;
sTime.Minutes = 37;
sTime.Seconds = 00;
if(mRTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
sDate.Date = 9;
sDate.Month = 5;
sDate.Year = 18;
if(mRTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE END 4 */
В такому варіанті кожної секунди буде викликатись функція
Show_RTC_Calendar і в масивах
aShowTime та
aShowDate будуть знаходитись текстові рядки з актуальним часом і датою. А вже цей текст з датою і часом виводьте - чи то на дисплей, чи то в послідовний порт. На ваш розсуд.
RTC Alarm
Також можна встановити будильник за допомоги функцій mRTC_SetAlarm або mRTC_SetAlarm_IT значення лічильника RTC_Alarm встановлюється в межах доби, якщо поточний час більший за значення часу будильника і якщо значення будильника менше значення поточного часу, то будильник встановиться на наступну добу.
Значення лічильника RTC_Alarm встановлюється коректним і по досягненню лічильника RTC значення RTC_Alarm будильника скидається. Але чомусь самого переривання по Alarm не відбувається. Хоча все робив як в прикладі, що йде з HAL драйверами. Запустити будильник мені так і не вдалось: як з рідними HAL драйвером RTC, як за допомоги CMSIS, так не виходило і за допомоги STD_Periph драйверів, ну і звісно і зараз не вийшло.
Можливо я просто не розумію як працює цей alarm і не правильно користуюсь ним. Як хто має позитивний досвід використання RTC_Alarm на STM32F1xx серії - поділіться цим зі мною.
RTC LL
Зараз в додачу до HAL драйверів з'явились LL драйвер (сучасний нащадок Std_Periph). Пробував запустити RTC на STM32F1xx серії за допомоги LL драйверу. То також повне фіаско. В лічильнику RTC додається одна секунда аж раз на 30 реальних секунд. В кого є позитивний досвід використання RTC за допомоги LL драйверу, теж, будь ласка, відгукніться.
Бібліотека mRTC