четвер, 10 травня 2018 р.

STM32: Годинник реального часу на stm32f1xx. Проблеми, рішення (частина друга).


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

Немає коментарів:

Дописати коментар