RTC на STM32 и реализация календаря (с рабочей библиотекой)

http://spec-project.ucoz.ru/index/zapusk_rtc_na_stm32_i_realizacija_kalendarja_s_rabochej_bibliotekoj/0-13

Всем Доброго Времени Суток! В данной статье хотелось бы рассказать о запуске часов реального времени (RTC), и реализации календаря на STM32, а точнее на отладочной плате STM32-P103.

При написании данной статьи пользовался сл. источниками:
 
Как подключать и настраивать дисплей можно посмотреть здесь
 
Вот собственно код для запуска и настройки RTC:
 
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"
#include "unix_time.h"
 
 
char LCDBUF[50];
uint32_t timer=1384850400+14400;
uint32_t tim;
 
void  RTC_INIT  (void)                                                                            //Инициализация RTC
{
  if ((RCC->BDCR & RCC_BDCR_RTCEN) != RCC_BDCR_RTCEN)                 //Проверка работы часов, если не включены, то инициализировать
  {
 RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;  //Включить тактирование PWR и Backup
 PWR->CR |= PWR_CR_DBP;                                                            //Разрешить доступ к Backup области
 RCC->BDCR |= RCC_BDCR_BDRST;                                               //Сбросить Backup область
 RCC->BDCR &= ~RCC_BDCR_BDRST;
 RCC->BDCR |= RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_LSE;        //Выбрать LSE источник (кварц 32768) и подать тактирование
 RCC->BDCR |= RCC_BDCR_LSEON;                                                //Включить LSE
 while ((RCC->BDCR & RCC_BDCR_LSEON) != RCC_BDCR_LSEON){} //Дождаться включения
 BKP->RTCCR |= 3;                                                                         //калибровка RTC
 while (!(RTC->CRL & RTC_CRL_RTOFF));                                         //проверить закончены ли изменения регистров RTC
 RTC->CRL  |=  RTC_CRL_CNF;                                                        //Разрешить Запись в регистры RTC
 RTC->PRLL  = 0x7FFF;                                                                    //Настроит делитель на 32768 (32767+1)
 RTC->CRL  &=  ~RTC_CRL_CNF;                                                     //Запретить запись в регистры RTC
 while (!(RTC->CRL & RTC_CRL_RTOFF));                                         //Дождаться окончания записи
 RTC->CRL &= (uint16_t)~RTC_CRL_RSF;                                         //Синхронизировать RTC
 while((RTC->CRL & RTC_CRL_RSF) != RTC_CRL_RSF){}                  //Дождаться синхронизации
 PWR->CR &= ~PWR_CR_DBP;                                                         //запретить доступ к Backup области
  }
}
 
uint32_t RTC_GetCounter_(void)                                                             //Получить значение счетчика
{
          return  (uint32_t)((RTC->CNTH << 16) | RTC->CNTL);
}
 
void RTC_SetCounter_(uint32_t count)                                                    //Записать новое значение счетчика
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;  //включить тактирование PWR и Backup
PWR->CR |= PWR_CR_DBP;                                                            //разрешить доступ к Backup области
while (!(RTC->CRL & RTC_CRL_RTOFF));                                         //проверить закончены ли изменения регистров RTC
RTC->CRL |= RTC_CRL_CNF;                                                          //Разрешить Запись в регистры RTC
RTC->CNTH = count>>16;                                                              //записать новое значение счетного регистра
RTC->CNTL = count;
RTC->CRL &= ~RTC_CRL_CNF;                                                       //Запретить запись в регистры RTC
while (!(RTC->CRL & RTC_CRL_RTOFF));                                         //Дождаться окончания записи
PWR->CR &= ~PWR_CR_DBP;                                                         //запретить доступ к Backup области
}
 
 
 
 
int main(void)
{
 
unix_cal unix_time;
LCD_INIT();                                                                                    //Инициализируем LCD
RTC_INIT();                                                                                    //Инициализируем RTC
RTC_SetCounter_(timer);       //Записать новое значение счетчика
 
    while(1)
    {
    timer=RTC_GetCounter_();                                                              //Получить значение счетчика
    timer_to_cal (timer, &unix_time);                                                     //Получит из счетчика календарь и время
    tim=cal_to_timer(&unix_time);                                                        //Получить из значения календаря и времени счетчик
    LCD_CLEAR();                                                                               //очистить LCD
    sprintf(LCDBUF,"%i",unix_time.mday );                                          //Вывести
    LCD_PUT(LCDBUF);                                                                       //
    LCD_PUT(".");                                                                               //на
    sprintf(LCDBUF,"%i",unix_time.mon );                                           //
    LCD_PUT(LCDBUF);                                                                       //LCD
    LCD_PUT(".");                                                                               //
    sprintf(LCDBUF,"%i",unix_time.year );                                          //Дату
    LCD_PUT(LCDBUF);                                                                       //
    LCD_PUT(" ");                                                                               //и
    LCD_GOTO(0,1);                                                                           //
    sprintf(LCDBUF,"%i",unix_time.hour );                                           //Время
    LCD_PUT(LCDBUF);                                                                       //
    LCD_PUT(":");                                                                               //
    sprintf(LCDBUF,"%i",unix_time.min );                                            //
    LCD_PUT(LCDBUF);                                                                       //
    LCD_PUT(":");                                                                               //
    sprintf(LCDBUF,"%i",unix_time.sec );                                             //
    LCD_PUT(LCDBUF);                                                                       //
    delay_ms(100);
    }
}
 
 
Вот этого хватит что бы у вас заработали часы с календарем на вашем STM.
Результат:
 
Подключаем батарейку, комментируем вот эту строчку:
RTC_SetCounter_(timer);       //Записать новое значение счетчика
и прошиваем еще раз, что бы при подачи основного питания не перезаписывался счетчик.
Теперь у вас ходят часы не зависимо от внешнего питания. Если вы решите делать на этой же отладочной плате, то не забудьте убрать перемычку:
Это необходимо для того что бы батарейка запитывала только RTC и BKP, иначе она запитывает полностью весь контроллер.
Библиотека для перевода счетчика в календарь и время, и обратно, находится здесь.
 
Если вы заметили, в программе имеется сл. строка:
BKP->RTCCR |= 3;                                                                         //калибровка RTC
Это калибровка RTC. Как ее правильно рассчитать объясню в сл. статье.

Обратная связь

Интересуют вопросы реализации алгоритмов, программирования, выбора электроники и прочая информация, постараюсь осветить в отдельных статьях

пишите мне на netdm@mail.ru